diff options
143 files changed, 5575 insertions, 2509 deletions
diff --git a/api/current.txt b/api/current.txt index 65571c283532..7c9c851c4bad 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5821,6 +5821,8 @@ package android.content.pm { field public static final java.lang.String FEATURE_LOCATION_NETWORK = "android.hardware.location.network"; field public static final java.lang.String FEATURE_MICROPHONE = "android.hardware.microphone"; field public static final java.lang.String FEATURE_NFC = "android.hardware.nfc"; + field public static final java.lang.String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape"; + field public static final java.lang.String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait"; field public static final java.lang.String FEATURE_SENSOR_ACCELEROMETER = "android.hardware.sensor.accelerometer"; field public static final java.lang.String FEATURE_SENSOR_BAROMETER = "android.hardware.sensor.barometer"; field public static final java.lang.String FEATURE_SENSOR_COMPASS = "android.hardware.sensor.compass"; @@ -13590,6 +13592,7 @@ package android.os { public class Build { ctor public Build(); + method public static java.lang.String getRadioVersion(); field public static final java.lang.String BOARD; field public static final java.lang.String BOOTLOADER; field public static final java.lang.String BRAND; @@ -13604,7 +13607,7 @@ package android.os { field public static final java.lang.String MANUFACTURER; field public static final java.lang.String MODEL; field public static final java.lang.String PRODUCT; - field public static final java.lang.String RADIO; + field public static final deprecated java.lang.String RADIO; field public static final java.lang.String SERIAL; field public static final java.lang.String TAGS; field public static final long TIME; @@ -16497,6 +16500,7 @@ package android.renderscript { method public void copy1DRangeFrom(int, int, short[]); method public void copy1DRangeFrom(int, int, byte[]); method public void copy1DRangeFrom(int, int, float[]); + method public void copy1DRangeFrom(int, int, android.renderscript.Allocation, int); method public void copy1DRangeFromUnchecked(int, int, int[]); method public void copy1DRangeFromUnchecked(int, int, short[]); method public void copy1DRangeFromUnchecked(int, int, byte[]); @@ -16505,6 +16509,7 @@ package android.renderscript { method public void copy2DRangeFrom(int, int, int, int, short[]); method public void copy2DRangeFrom(int, int, int, int, int[]); method public void copy2DRangeFrom(int, int, int, int, float[]); + method public void copy2DRangeFrom(int, int, int, int, android.renderscript.Allocation, int, int); method public void copy2DRangeFrom(int, int, android.graphics.Bitmap); method public void copyFrom(android.renderscript.BaseObj[]); method public void copyFrom(int[]); @@ -16569,8 +16574,10 @@ package android.renderscript { method public void subData1D(int, int, short[]); method public void subData1D(int, int, byte[]); method public void subData1D(int, int, float[]); + method public void subData1D(int, int, android.renderscript.AllocationAdapter, int); method public void subData2D(int, int, int, int, int[]); method public void subData2D(int, int, int, int, float[]); + method public void subData2D(int, int, int, int, android.renderscript.AllocationAdapter, int, int); method public void subElementData(int, int, android.renderscript.FieldPacker); } @@ -21559,8 +21566,12 @@ package android.view { ctor public TextureView(android.content.Context, android.util.AttributeSet); ctor public TextureView(android.content.Context, android.util.AttributeSet, int); method public final void draw(android.graphics.Canvas); + method public android.graphics.Bitmap getBitmap(); + method public android.graphics.Bitmap getBitmap(int, int); + method public android.graphics.Bitmap getBitmap(android.graphics.Bitmap); method public android.graphics.SurfaceTexture getSurfaceTexture(); method public android.view.TextureView.SurfaceTextureListener getSurfaceTextureListener(); + method public boolean isAvailable(); method protected final void onDraw(android.graphics.Canvas); method public void setSurfaceTextureListener(android.view.TextureView.SurfaceTextureListener); } diff --git a/core/java/android/app/Fragment.java b/core/java/android/app/Fragment.java index f2be9ad9ba3f..cb97c4671bfe 100644 --- a/core/java/android/app/Fragment.java +++ b/core/java/android/app/Fragment.java @@ -244,12 +244,12 @@ final class FragmentState implements Parcelable { * on {@link ListFragment} for most of its work. Note the implementation of * clicking an item: depending on the current activity's layout, it can either * create and display a new fragment to show the details in-place (more about - * this later), or start a new activity show the details.</p> + * this later), or start a new activity to show the details.</p> * * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java * titles} * - * <p>The details fragment showing the contents of selected item here just + * <p>The details fragment showing the contents of a selected item just * displays a string of text based on an index of a string array built in to * the app:</p> * @@ -257,7 +257,7 @@ final class FragmentState implements Parcelable { * details} * * <p>In this case when the user clicks on a title, there is no details - * container in the current activity, so the title title fragment's click code will + * container in the current activity, so the titles fragment's click code will * launch a new activity to display the details fragment:</p> * * {@sample development/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java @@ -367,6 +367,9 @@ public class Fragment implements ComponentCallbacks, OnCreateContextMenuListener // Target fragment. Fragment mTarget; + // For use when retaining a fragment: this is the index of the last mTarget. + int mTargetIndex = -1; + // Target request code. int mTargetRequestCode; diff --git a/core/java/android/app/FragmentManager.java b/core/java/android/app/FragmentManager.java index f05e2b3b2c57..285f1c156086 100644 --- a/core/java/android/app/FragmentManager.java +++ b/core/java/android/app/FragmentManager.java @@ -695,6 +695,10 @@ final class FragmentManagerImpl extends FragmentManager { if (!f.mAdded && newState > Fragment.CREATED) { newState = Fragment.CREATED; } + if (f.mRemoving && newState > f.mState) { + // While removing a fragment, we can't change it to a higher state. + newState = f.mState; + } if (f.mState < newState) { // For fragments that are created from a layout, when restoring from @@ -915,6 +919,7 @@ final class FragmentManagerImpl extends FragmentManager { // the fragment now should move to once the animation // is done. f.mStateAfterAnimating = newState; + newState = Fragment.CREATED; } else { if (DEBUG) Log.v(TAG, "movefrom CREATED: " + f); if (!f.mRetaining) { @@ -932,9 +937,13 @@ final class FragmentManagerImpl extends FragmentManager { throw new SuperNotCalledException("Fragment " + f + " did not call through to super.onDetach()"); } - f.mImmediateActivity = null; - f.mActivity = null; - f.mFragmentManager = null; + if (!f.mRetaining) { + makeInactive(f); + } else { + f.mImmediateActivity = null; + f.mActivity = null; + f.mFragmentManager = null; + } } } } @@ -1040,9 +1049,6 @@ final class FragmentManagerImpl extends FragmentManager { fragment.mRemoving = true; moveToState(fragment, inactive ? Fragment.INITIALIZING : Fragment.CREATED, transition, transitionStyle); - if (inactive) { - makeInactive(fragment); - } } } @@ -1397,6 +1403,7 @@ final class FragmentManagerImpl extends FragmentManager { } fragments.add(f); f.mRetaining = true; + f.mTargetIndex = f.mTarget != null ? f.mTarget.mIndex : -1; } } } @@ -1561,6 +1568,7 @@ final class FragmentManagerImpl extends FragmentManager { f.mBackStackNesting = 0; f.mInLayout = false; f.mAdded = false; + f.mTarget = null; if (fs.mSavedFragmentState != null) { fs.mSavedFragmentState.setClassLoader(mActivity.getClassLoader()); f.mSavedViewState = fs.mSavedFragmentState.getSparseParcelableArray( @@ -1600,12 +1608,12 @@ final class FragmentManagerImpl extends FragmentManager { if (nonConfig != null) { for (int i=0; i<nonConfig.size(); i++) { Fragment f = nonConfig.get(i); - if (f.mTarget != null) { - if (f.mTarget.mIndex < mActive.size()) { - f.mTarget = mActive.get(f.mTarget.mIndex); + if (f.mTargetIndex >= 0) { + if (f.mTargetIndex < mActive.size()) { + f.mTarget = mActive.get(f.mTargetIndex); } else { Log.w(TAG, "Re-attaching retained fragment " + f - + " target no longer exists: " + f.mTarget); + + " target no longer exists: " + f.mTargetIndex); f.mTarget = null; } } diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java index 9246a1035087..61d3707f8c64 100644 --- a/core/java/android/bluetooth/BluetoothA2dp.java +++ b/core/java/android/bluetooth/BluetoothA2dp.java @@ -130,7 +130,25 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> Currently, the system supports only 1 connection to the + * A2DP profile. The API will automatically disconnect connected + * devices before connecting. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -149,7 +167,29 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -220,7 +260,18 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Set priority of the profile + * + * <p> The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} or + * {@link #PRIORITY_OFF}, + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { @@ -243,7 +294,16 @@ public final class BluetoothA2dp implements BluetoothProfile { } /** - * {@inheritDoc} + * Get the priority of the profile. + * + * <p> The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device * @hide */ public int getPriority(BluetoothDevice device) { diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java index 8a9bef05f447..23724f245cab 100644 --- a/core/java/android/bluetooth/BluetoothHeadset.java +++ b/core/java/android/bluetooth/BluetoothHeadset.java @@ -248,7 +248,25 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> Currently, the system supports only 1 connection to the + * headset/handsfree profile. The API will automatically disconnect connected + * devices before connecting. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -267,7 +285,29 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -338,7 +378,18 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Set priority of the profile + * + * <p> The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} or + * {@link #PRIORITY_OFF}, + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { @@ -361,7 +412,16 @@ public final class BluetoothHeadset implements BluetoothProfile { } /** - * {@inheritDoc} + * Get the priority of the profile. + * + * <p> The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device * @hide */ public int getPriority(BluetoothDevice device) { diff --git a/core/java/android/bluetooth/BluetoothInputDevice.java b/core/java/android/bluetooth/BluetoothInputDevice.java index df212a82d4e6..282b70a42c81 100644 --- a/core/java/android/bluetooth/BluetoothInputDevice.java +++ b/core/java/android/bluetooth/BluetoothInputDevice.java @@ -119,7 +119,23 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> The system supports connection to multiple input devices. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -138,7 +154,29 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -209,7 +247,18 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Set priority of the profile + * + * <p> The device should already be paired. + * Priority can be one of {@link #PRIORITY_ON} or + * {@link #PRIORITY_OFF}, + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Paired bluetooth device + * @param priority + * @return true if priority is set, false on error * @hide */ public boolean setPriority(BluetoothDevice device, int priority) { @@ -232,7 +281,16 @@ public final class BluetoothInputDevice implements BluetoothProfile { } /** - * {@inheritDoc} + * Get the priority of the profile. + * + * <p> The priority can be any of: + * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, + * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. + * + * @param device Bluetooth device + * @return priority of the device * @hide */ public int getPriority(BluetoothDevice device) { diff --git a/core/java/android/bluetooth/BluetoothPan.java b/core/java/android/bluetooth/BluetoothPan.java index 9ffed26f4acb..7490f9ee86b4 100644 --- a/core/java/android/bluetooth/BluetoothPan.java +++ b/core/java/android/bluetooth/BluetoothPan.java @@ -140,7 +140,21 @@ public final class BluetoothPan implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate connection to a profile of the remote bluetooth device. + * + * <p> This API returns false in scenarios like the profile on the + * device is already connected or Bluetooth is not turned on. + * When this API returns true, it is guaranteed that + * connection state intent for the profile will be broadcasted with + * the state. Users can get the connection state of the profile + * from this intent. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean connect(BluetoothDevice device) { @@ -159,7 +173,29 @@ public final class BluetoothPan implements BluetoothProfile { } /** - * {@inheritDoc} + * Initiate disconnection from a profile + * + * <p> This API will return false in scenarios like the profile on the + * Bluetooth device is not in connected state etc. When this API returns, + * true, it is guaranteed that the connection state change + * intent will be broadcasted with the state. Users can get the + * disconnection state of the profile from this intent. + * + * <p> If the disconnection is initiated by a remote device, the state + * will transition from {@link #STATE_CONNECTED} to + * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the + * host (local) device the state will transition from + * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to + * state {@link #STATE_DISCONNECTED}. The transition to + * {@link #STATE_DISCONNECTING} can be used to distinguish between the + * two scenarios. + * + * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} + * permission. + * + * @param device Remote Bluetooth Device + * @return false on immediate error, + * true otherwise * @hide */ public boolean disconnect(BluetoothDevice device) { @@ -229,27 +265,6 @@ public final class BluetoothPan implements BluetoothProfile { return BluetoothProfile.STATE_DISCONNECTED; } - /** - * {@inheritDoc} - * @hide - */ - public boolean setPriority(BluetoothDevice device, int priority) { - // Priorities are not supported for PAN devices - since we don't - // auto connect. - return false; - } - - /** - * {@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 { diff --git a/core/java/android/bluetooth/BluetoothProfile.java b/core/java/android/bluetooth/BluetoothProfile.java index 1ad66f71b938..22555f02d4c5 100644 --- a/core/java/android/bluetooth/BluetoothProfile.java +++ b/core/java/android/bluetooth/BluetoothProfile.java @@ -104,58 +104,6 @@ public interface BluetoothProfile { public static final int PRIORITY_UNDEFINED = -1; /** - * Initiate connection to a profile of the remote bluetooth device. - * - * <p> Currently, the system supports only 1 connection to the - * A2DP and Headset/Handsfree profile. The API will automatically - * disconnect connected devices before connecting. - * - * <p> This API returns false in scenarios like the profile on the - * device is already connected or Bluetooth is not turned on. - * When this API returns true, it is guaranteed that - * connection state intent for the profile will be broadcasted with - * the state. Users can get the connection state of the profile - * from this intent. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise - * @hide - */ - public boolean connect(BluetoothDevice device); - - /** - * Initiate disconnection from a profile - * - * <p> This API will return false in scenarios like the profile on the - * Bluetooth device is not in connected state etc. When this API returns, - * true, it is guaranteed that the connection state change - * intent will be broadcasted with the state. Users can get the - * disconnection state of the profile from this intent. - * - * <p> If the disconnection is initiated by a remote device, the state - * will transition from {@link #STATE_CONNECTED} to - * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the - * host (local) device the state will transition from - * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to - * state {@link #STATE_DISCONNECTED}. The transition to - * {@link #STATE_DISCONNECTING} can be used to distinguish between the - * two scenarios. - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Remote Bluetooth Device - * @return false on immediate error, - * true otherwise - * @hide - */ - public boolean disconnect(BluetoothDevice device); - - /** * Get connected devices for this specific profile. * * <p> Return the set of devices which are in state {@link #STATE_CONNECTED} @@ -195,38 +143,6 @@ public interface BluetoothProfile { public int getConnectionState(BluetoothDevice device); /** - * Set priority of the profile - * - * <p> The device should already be paired. - * Priority can be one of {@link #PRIORITY_ON} or - * {@link #PRIORITY_OFF}, - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} - * permission. - * - * @param device Paired bluetooth device - * @param priority - * @return true if priority is set, false on error - * @hide - */ - public boolean setPriority(BluetoothDevice device, int priority); - - /** - * Get the priority of the profile. - * - * <p> The priority can be any of: - * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, - * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} - * - * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission. - * - * @param device Bluetooth device - * @return priority of the device - * @hide - */ - public int getPriority(BluetoothDevice device); - - /** * An interface for notifying BluetoothProfile IPC clients when they have * been connected or disconnected to the service. */ diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 33c2937aa983..1cd8ec0f5b4a 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -925,6 +925,26 @@ public abstract class PackageManager { /** * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports portrait orientation + * screens. For backwards compatibility, you can assume that if neither + * this nor {@link #FEATURE_SCREEN_LANDSCAPE} is set then the device supports + * both portrait and landscape. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SCREEN_PORTRAIT = "android.hardware.screen.portrait"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and + * {@link #hasSystemFeature}: The device supports landscape orientation + * screens. For backwards compatibility, you can assume that if neither + * this nor {@link #FEATURE_SCREEN_PORTRAIT} is set then the device supports + * both portrait and landscape. + */ + @SdkConstant(SdkConstantType.FEATURE) + public static final String FEATURE_SCREEN_LANDSCAPE = "android.hardware.screen.landscape"; + + /** + * Feature for {@link #getSystemAvailableFeatures} and * {@link #hasSystemFeature}: The device supports live wallpapers. */ @SdkConstant(SdkConstantType.FEATURE) diff --git a/core/java/android/net/NetworkPolicyManager.java b/core/java/android/net/NetworkPolicyManager.java index 13ece4097c1b..e9d65e6b3842 100644 --- a/core/java/android/net/NetworkPolicyManager.java +++ b/core/java/android/net/NetworkPolicyManager.java @@ -168,6 +168,15 @@ public class NetworkPolicyManager { time.normalize(true); } + /** + * Check if given UID can have a {@link #setUidPolicy(int, int)} defined, + * usually to protect critical system services. + */ + public static boolean isUidValidForPolicy(Context context, int uid) { + return (uid >= android.os.Process.FIRST_APPLICATION_UID + && uid <= android.os.Process.LAST_APPLICATION_UID); + } + /** {@hide} */ public static void dumpPolicy(PrintWriter fout, int policy) { fout.write("["); diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index 8ff5beb035cc..1b28aa2bcc6b 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -16,6 +16,8 @@ package android.os; +import com.android.internal.telephony.TelephonyProperties; + /** * Information about the current build, extracted from system properties. */ @@ -56,8 +58,16 @@ public class Build { /** The system bootloader version number. */ public static final String BOOTLOADER = getString("ro.bootloader"); - /** The radio firmware version number. */ - public static final String RADIO = getString("gsm.version.baseband"); + /** + * The radio firmware version number. + * + * @deprecated The radio firmware version is frequently not + * available when this class is initialized, leading to a blank or + * "unknown" value for this string. Use + * {@link #getRadioVersion} instead. + */ + @Deprecated + public static final String RADIO = getString(TelephonyProperties.PROPERTY_BASEBAND_VERSION); /** The name of the hardware (from the kernel command line or /proc). */ public static final String HARDWARE = getString("ro.hardware"); @@ -243,6 +253,26 @@ public class Build { * later. Applications that don't support a screen size at least as * large as the current screen will provide the user with a UI to * switch them in to screen size compatibility mode.</p> + * + * <p>This version introduces new screen size resource qualifiers + * based on the screen size in dp: see + * {@link android.content.res.Configuration#screenWidthDp}, + * {@link android.content.res.Configuration#screenHeightDp}, and + * {@link android.content.res.Configuration#smallestScreenWidthDp}. + * Supplying these in <supports-screens> as per + * {@link android.content.pm.ApplicationInfo#requiresSmallestWidthDp}, + * {@link android.content.pm.ApplicationInfo#compatibleWidthLimitDp}, and + * {@link android.content.pm.ApplicationInfo#largestWidthLimitDp} is + * preferred over the older screen size buckets and for older devices + * the appropriate buckets will be inferred from them.</p> + * + * <p>New {@link android.content.pm.PackageManager#FEATURE_SCREEN_PORTRAIT} + * and {@link android.content.pm.PackageManager#FEATURE_SCREEN_LANDSCAPE} + * features are introduced in this release. Applications that target + * previous platform versions are assumed to require both portrait and + * landscape support in the device; when targeting Honeycomb MR1 or + * greater the application is responsible for specifying any specific + * orientation it requires.</p> */ public static final int HONEYCOMB_MR2 = 13; @@ -266,6 +296,14 @@ public class Build { public static final String USER = getString("ro.build.user"); public static final String HOST = getString("ro.build.host"); + /** + * Returns the version string for the radio firmware. May return + * null (if, for instance, the radio is not currently on). + */ + public static String getRadioVersion() { + return SystemProperties.get(TelephonyProperties.PROPERTY_BASEBAND_VERSION, null); + } + private static String getString(String property) { return SystemProperties.get(property, UNKNOWN); } diff --git a/core/java/android/pim/RecurrenceSet.java b/core/java/android/pim/RecurrenceSet.java index fdd078311316..b7fb3200dcd3 100644 --- a/core/java/android/pim/RecurrenceSet.java +++ b/core/java/android/pim/RecurrenceSet.java @@ -18,7 +18,7 @@ package android.pim; import android.content.ContentValues; import android.database.Cursor; -import android.provider.Calendar; +import android.provider.CalendarContract; import android.text.TextUtils; import android.text.format.Time; import android.util.Log; @@ -50,10 +50,10 @@ public class RecurrenceSet { */ public RecurrenceSet(ContentValues values) throws EventRecurrence.InvalidFormatException { - String rruleStr = values.getAsString(Calendar.Events.RRULE); - String rdateStr = values.getAsString(Calendar.Events.RDATE); - String exruleStr = values.getAsString(Calendar.Events.EXRULE); - String exdateStr = values.getAsString(Calendar.Events.EXDATE); + String rruleStr = values.getAsString(CalendarContract.Events.RRULE); + String rdateStr = values.getAsString(CalendarContract.Events.RDATE); + String exruleStr = values.getAsString(CalendarContract.Events.EXRULE); + String exdateStr = values.getAsString(CalendarContract.Events.EXDATE); init(rruleStr, rdateStr, exruleStr, exdateStr); } @@ -68,10 +68,10 @@ public class RecurrenceSet { */ public RecurrenceSet(Cursor cursor) throws EventRecurrence.InvalidFormatException { - int rruleColumn = cursor.getColumnIndex(Calendar.Events.RRULE); - int rdateColumn = cursor.getColumnIndex(Calendar.Events.RDATE); - int exruleColumn = cursor.getColumnIndex(Calendar.Events.EXRULE); - int exdateColumn = cursor.getColumnIndex(Calendar.Events.EXDATE); + int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE); + int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE); + int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE); + int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE); String rruleStr = cursor.getString(rruleColumn); String rdateStr = cursor.getString(rdateColumn); String exruleStr = cursor.getString(exruleColumn); @@ -208,7 +208,7 @@ public class RecurrenceSet { start.timezone = Time.TIMEZONE_UTC; } long millis = start.toMillis(false /* use isDst */); - values.put(Calendar.Events.DTSTART, millis); + values.put(CalendarContract.Events.DTSTART, millis); if (millis == -1) { if (false) { Log.d(TAG, "DTSTART is out of range: " + component.toString()); @@ -216,13 +216,13 @@ public class RecurrenceSet { return false; } - values.put(Calendar.Events.RRULE, rrule); - values.put(Calendar.Events.RDATE, rdate); - values.put(Calendar.Events.EXRULE, exrule); - values.put(Calendar.Events.EXDATE, exdate); - values.put(Calendar.Events.EVENT_TIMEZONE, tzid); - values.put(Calendar.Events.DURATION, duration); - values.put(Calendar.Events.ALL_DAY, allDay ? 1 : 0); + values.put(CalendarContract.Events.RRULE, rrule); + values.put(CalendarContract.Events.RDATE, rdate); + values.put(CalendarContract.Events.EXRULE, exrule); + values.put(CalendarContract.Events.EXDATE, exdate); + values.put(CalendarContract.Events.EVENT_TIMEZONE, tzid); + values.put(CalendarContract.Events.DURATION, duration); + values.put(CalendarContract.Events.ALL_DAY, allDay ? 1 : 0); return true; } @@ -230,14 +230,14 @@ public class RecurrenceSet { public static boolean populateComponent(Cursor cursor, ICalendar.Component component) { - int dtstartColumn = cursor.getColumnIndex(Calendar.Events.DTSTART); - int durationColumn = cursor.getColumnIndex(Calendar.Events.DURATION); - int tzidColumn = cursor.getColumnIndex(Calendar.Events.EVENT_TIMEZONE); - int rruleColumn = cursor.getColumnIndex(Calendar.Events.RRULE); - int rdateColumn = cursor.getColumnIndex(Calendar.Events.RDATE); - int exruleColumn = cursor.getColumnIndex(Calendar.Events.EXRULE); - int exdateColumn = cursor.getColumnIndex(Calendar.Events.EXDATE); - int allDayColumn = cursor.getColumnIndex(Calendar.Events.ALL_DAY); + int dtstartColumn = cursor.getColumnIndex(CalendarContract.Events.DTSTART); + int durationColumn = cursor.getColumnIndex(CalendarContract.Events.DURATION); + int tzidColumn = cursor.getColumnIndex(CalendarContract.Events.EVENT_TIMEZONE); + int rruleColumn = cursor.getColumnIndex(CalendarContract.Events.RRULE); + int rdateColumn = cursor.getColumnIndex(CalendarContract.Events.RDATE); + int exruleColumn = cursor.getColumnIndex(CalendarContract.Events.EXRULE); + int exdateColumn = cursor.getColumnIndex(CalendarContract.Events.EXDATE); + int allDayColumn = cursor.getColumnIndex(CalendarContract.Events.ALL_DAY); long dtstart = -1; @@ -299,16 +299,16 @@ public class RecurrenceSet { public static boolean populateComponent(ContentValues values, ICalendar.Component component) { long dtstart = -1; - if (values.containsKey(Calendar.Events.DTSTART)) { - dtstart = values.getAsLong(Calendar.Events.DTSTART); + if (values.containsKey(CalendarContract.Events.DTSTART)) { + dtstart = values.getAsLong(CalendarContract.Events.DTSTART); } - String duration = values.getAsString(Calendar.Events.DURATION); - String tzid = values.getAsString(Calendar.Events.EVENT_TIMEZONE); - String rruleStr = values.getAsString(Calendar.Events.RRULE); - String rdateStr = values.getAsString(Calendar.Events.RDATE); - String exruleStr = values.getAsString(Calendar.Events.EXRULE); - String exdateStr = values.getAsString(Calendar.Events.EXDATE); - Integer allDayInteger = values.getAsInteger(Calendar.Events.ALL_DAY); + String duration = values.getAsString(CalendarContract.Events.DURATION); + String tzid = values.getAsString(CalendarContract.Events.EVENT_TIMEZONE); + String rruleStr = values.getAsString(CalendarContract.Events.RRULE); + String rdateStr = values.getAsString(CalendarContract.Events.RDATE); + String exruleStr = values.getAsString(CalendarContract.Events.EXRULE); + String exdateStr = values.getAsString(CalendarContract.Events.EXDATE); + Integer allDayInteger = values.getAsInteger(CalendarContract.Events.ALL_DAY); boolean allDay = (null != allDayInteger) ? (allDayInteger == 1) : false; if ((dtstart == -1) || diff --git a/core/java/android/preference/PreferenceFragment.java b/core/java/android/preference/PreferenceFragment.java index 9d46b7a139dc..488919c7c6b3 100644 --- a/core/java/android/preference/PreferenceFragment.java +++ b/core/java/android/preference/PreferenceFragment.java @@ -146,7 +146,6 @@ public abstract class PreferenceFragment extends Fragment implements super.onCreate(savedInstanceState); mPreferenceManager = new PreferenceManager(getActivity(), FIRST_REQUEST_CODE); mPreferenceManager.setFragment(this); - mPreferenceManager.setOnPreferenceTreeClickListener(this); } @Override @@ -179,9 +178,16 @@ public abstract class PreferenceFragment extends Fragment implements } @Override + public void onStart() { + super.onStart(); + mPreferenceManager.setOnPreferenceTreeClickListener(this); + } + + @Override public void onStop() { super.onStop(); mPreferenceManager.dispatchActivityStop(); + mPreferenceManager.setOnPreferenceTreeClickListener(null); } @Override @@ -196,7 +202,6 @@ public abstract class PreferenceFragment extends Fragment implements public void onDestroy() { super.onDestroy(); mPreferenceManager.dispatchActivityDestroy(); - mPreferenceManager.setOnPreferenceTreeClickListener(null); } @Override diff --git a/core/java/android/provider/Calendar.java b/core/java/android/provider/CalendarContract.java index 835914460266..3db1827b1dc2 100644 --- a/core/java/android/provider/Calendar.java +++ b/core/java/android/provider/CalendarContract.java @@ -84,10 +84,10 @@ import android.util.Log; * {@link SyncState}, which contains free-form data maintained by the sync * adapters</li> * </ul> - * + * * @hide */ -public final class Calendar { +public final class CalendarContract { private static final String TAG = "Calendar"; /** @@ -528,7 +528,7 @@ public final class Calendar { */ public static int deleteCalendarsForAccount(ContentResolver cr, Account account) { // delete all calendars that match this account - return Calendar.Calendars.delete(cr, + return CalendarContract.Calendars.delete(cr, WHERE_DELETE_FOR_ACCOUNT, new String[] { account.name, account.type }); } @@ -1774,7 +1774,7 @@ public final class Calendar { * Fields and helpers for accessing reminders for an event. */ public static final class Reminders implements BaseColumns, RemindersColumns, EventsColumns { - private static final String REMINDERS_WHERE = Calendar.Reminders.EVENT_ID + "=?"; + private static final String REMINDERS_WHERE = CalendarContract.Reminders.EVENT_ID + "=?"; /** * The projection used by the reminders query. */ @@ -2066,7 +2066,7 @@ public final class Calendar { } Intent intent = new Intent(EVENT_REMINDER_ACTION); - intent.setData(ContentUris.withAppendedId(Calendar.CONTENT_URI, alarmTime)); + intent.setData(ContentUris.withAppendedId(CalendarContract.CONTENT_URI, alarmTime)); intent.putExtra(ALARM_TIME, alarmTime); PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0); manager.set(AlarmManager.RTC_WAKEUP, alarmTime, pi); @@ -2165,7 +2165,7 @@ public final class Calendar { * The content:// style URI for this table */ public static final Uri CONTENT_URI = - Uri.withAppendedPath(Calendar.CONTENT_URI, CONTENT_DIRECTORY); + Uri.withAppendedPath(CalendarContract.CONTENT_URI, CONTENT_DIRECTORY); } /** diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index 60bee9ab2e1a..62792f469b78 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -1737,7 +1737,6 @@ public class BluetoothService extends IBluetooth.Stub { 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: diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 6823b73ba057..7d596df05482 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -499,7 +499,7 @@ public class TextToSpeech { String defaultEngine = getDefaultEngine(); String engine = defaultEngine; if (!areDefaultsEnforced() && !TextUtils.isEmpty(mRequestedEngine) - && mEnginesHelper.isEngineEnabled(engine)) { + && mEnginesHelper.isEngineEnabled(mRequestedEngine)) { engine = mRequestedEngine; } diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index d5cad9634106..383bfb3b3137 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -166,6 +166,7 @@ class GLES20Canvas extends HardwareCanvas { static native void nUpdateTextureLayer(int layerId, int width, int height, int surface); static native void nDestroyLayer(int layerId); static native void nDestroyLayerDeferred(int layerId); + static native boolean nCopyLayer(int layerId, int bitmap); /////////////////////////////////////////////////////////////////////////// // Canvas management diff --git a/core/java/android/view/GLES20Layer.java b/core/java/android/view/GLES20Layer.java index bc191a6fae9b..69dfc2bc5367 100644 --- a/core/java/android/view/GLES20Layer.java +++ b/core/java/android/view/GLES20Layer.java @@ -17,6 +17,8 @@ package android.view; +import android.graphics.Bitmap; + /** * An OpenGL ES 2.0 implementation of {@link HardwareLayer}. */ @@ -40,7 +42,10 @@ abstract class GLES20Layer extends HardwareLayer { return mLayer; } - + boolean copyInto(Bitmap bitmap) { + return GLES20Canvas.nCopyLayer(mLayer, bitmap.mNativeBitmap); + } + @Override void destroy() { if (mFinalizer != null) mFinalizer.destroy(); diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 2611ec0752c2..5944bd493b53 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -17,6 +17,7 @@ package android.view; +import android.graphics.Bitmap; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; @@ -204,8 +205,18 @@ public abstract class HardwareRenderer { * @param surface The surface to update */ abstract void updateTextureLayer(HardwareLayer layer, int width, int height, - SurfaceTexture surface); - + SurfaceTexture surface); + + /** + * Copies the content of the specified layer into the specified bitmap. + * + * @param layer The hardware layer to copy + * @param bitmap The bitmap to copy the layer into + * + * @return True if the copy was successful, false otherwise + */ + abstract boolean copyLayer(HardwareLayer layer, Bitmap bitmap); + /** * Initializes the hardware renderer for the specified surface and setup the * renderer for drawing, if needed. This is invoked when the ViewAncestor has @@ -814,6 +825,11 @@ public abstract class HardwareRenderer { ((GLES20TextureLayer) layer).update(width, height, surface.mSurfaceTexture); } + @Override + boolean copyLayer(HardwareLayer layer, Bitmap bitmap) { + return ((GLES20Layer) layer).copyInto(bitmap); + } + static HardwareRenderer create(boolean translucent) { if (GLES20Canvas.isAvailable()) { return new Gl20Renderer(translucent); diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java index bc1ad3c8e92c..4daa892d3e35 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -17,6 +17,7 @@ package android.view; import android.content.Context; +import android.graphics.Bitmap; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.SurfaceTexture; @@ -89,6 +90,8 @@ import android.util.Log; * @see SurfaceTexture */ public class TextureView extends View { + private static final String LOG_TAG = "TextureView"; + private HardwareLayer mLayer; private SurfaceTexture mSurface; private SurfaceTextureListener mListener; @@ -148,7 +151,7 @@ public class TextureView extends View { super.onAttachedToWindow(); if (!isHardwareAccelerated()) { - Log.w("TextureView", "A TextureView or a subclass can only be " + Log.w(LOG_TAG, "A TextureView or a subclass can only be " + "used with hardware acceleration enabled."); } } @@ -293,8 +296,95 @@ public class TextureView extends View { } /** + * <p>Returns a {@link android.graphics.Bitmap} representation of the content + * of the associated surface texture. If the surface texture is not available, + * this method returns null.</p> + * + * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} + * pixel format and its dimensions are the same as this view's.</p> + * + * <p><strong>Do not</strong> invoke this method from a drawing method + * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> + * + * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface + * texture is not available or the width <= 0 or the height <= 0 + * + * @see #isAvailable() + * @see #getBitmap(android.graphics.Bitmap) + * @see #getBitmap(int, int) + */ + public Bitmap getBitmap() { + return getBitmap(getWidth(), getHeight()); + } + + /** + * <p>Returns a {@link android.graphics.Bitmap} representation of the content + * of the associated surface texture. If the surface texture is not available, + * this method returns null.</p> + * + * <p>The bitmap returned by this method uses the {@link Bitmap.Config#ARGB_8888} + * pixel format.</p> + * + * <p><strong>Do not</strong> invoke this method from a drawing method + * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> + * + * @param width The width of the bitmap to create + * @param height The height of the bitmap to create + * + * @return A valid {@link Bitmap.Config#ARGB_8888} bitmap, or null if the surface + * texture is not available or width is <= 0 or height is <= 0 + * + * @see #isAvailable() + * @see #getBitmap(android.graphics.Bitmap) + * @see #getBitmap() + */ + public Bitmap getBitmap(int width, int height) { + if (isAvailable() && width > 0 && height > 0) { + return getBitmap(Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)); + } + return null; + } + + /** + * <p>Copies the content of this view's surface texture into the specified + * bitmap. If the surface texture is not available, the copy is not executed. + * The content of the surface texture will be scaled to fit exactly inside + * the specified bitmap.</p> + * + * <p><strong>Do not</strong> invoke this method from a drawing method + * ({@link #onDraw(android.graphics.Canvas)} for instance).</p> + * + * @param bitmap The bitmap to copy the content of the surface texture into, + * cannot be null, all configurations are supported + * + * @return The bitmap specified as a parameter + * + * @see #isAvailable() + * @see #getBitmap(int, int) + * @see #getBitmap() + */ + public Bitmap getBitmap(Bitmap bitmap) { + if (bitmap != null && isAvailable()) { + mAttachInfo.mHardwareRenderer.copyLayer(mLayer, bitmap); + } + return bitmap; + } + + /** + * Returns true if the {@link SurfaceTexture} associated with this + * TextureView is available for rendering. When this method returns + * true, {@link #getSurfaceTexture()} returns a valid surface texture. + */ + public boolean isAvailable() { + return mSurface != null; + } + + /** * Returns the {@link SurfaceTexture} used by this view. This method - * may return null if the view is not attached to a window. + * may return null if the view is not attached to a window or if the surface + * texture has not been initialized yet. + * + * @see #isAvailable() */ public SurfaceTexture getSurfaceTexture() { return mSurface; diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 6b6aee3c37d0..d1ad1139aafc 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -1004,6 +1004,23 @@ public interface WindowManager extends ViewManager { */ public boolean hasSystemUiListeners; + /** + * When this window has focus, disable touch pad pointer gesture processing. + * The window will receive raw position updates from the touch pad instead + * of pointer movements and synthetic touch events. + * + * @hide + */ + public static final int INPUT_FEATURE_DISABLE_POINTER_GESTURES = 0x00000001; + + /** + * Control special features of the input subsystem. + * + * @see #INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES + * @hide + */ + public int inputFeatures; + public LayoutParams() { super(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT); type = TYPE_APPLICATION; @@ -1086,6 +1103,7 @@ public interface WindowManager extends ViewManager { out.writeInt(systemUiVisibility); out.writeInt(subtreeSystemUiVisibility); out.writeInt(hasSystemUiListeners ? 1 : 0); + out.writeInt(inputFeatures); } public static final Parcelable.Creator<LayoutParams> CREATOR @@ -1124,6 +1142,7 @@ public interface WindowManager extends ViewManager { systemUiVisibility = in.readInt(); subtreeSystemUiVisibility = in.readInt(); hasSystemUiListeners = in.readInt() != 0; + inputFeatures = in.readInt(); } @SuppressWarnings({"PointlessBitwiseExpression"}) @@ -1145,6 +1164,8 @@ public interface WindowManager extends ViewManager { public static final int SYSTEM_UI_VISIBILITY_CHANGED = 1<<13; /** {@hide} */ public static final int SYSTEM_UI_LISTENER_CHANGED = 1<<14; + /** {@hide} */ + public static final int INPUT_FEATURES_CHANGED = 1<<15; // internal buffer to backup/restore parameters under compatibility mode. private int[] mCompatibilityParamsBackup = null; @@ -1256,6 +1277,11 @@ public interface WindowManager extends ViewManager { changes |= SYSTEM_UI_LISTENER_CHANGED; } + if (inputFeatures != o.inputFeatures) { + inputFeatures = o.inputFeatures; + changes |= INPUT_FEATURES_CHANGED; + } + return changes; } @@ -1340,6 +1366,7 @@ public interface WindowManager extends ViewManager { sb.append(" sysuil="); sb.append(hasSystemUiListeners); } + sb.append(" if=0x").append(Integer.toHexString(inputFeatures)); sb.append('}'); return sb.toString(); } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index bfab8a995bab..c56e6db8398a 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -3062,6 +3062,13 @@ public class WebView extends AbsoluteLayout } /** + * @hide + */ + public int getPageBackgroundColor() { + return nativeGetBackgroundColor(); + } + + /** * Pause all layout, parsing, and JavaScript timers for all webviews. This * is a global requests, not restricted to just this webview. This can be * useful if the application has been paused. @@ -9216,4 +9223,5 @@ public class WebView extends AbsoluteLayout * @return True if the layer is successfully scrolled. */ private native boolean nativeScrollLayer(int layer, int newX, int newY); + private native int nativeGetBackgroundColor(); } diff --git a/core/java/android/widget/FastScroller.java b/core/java/android/widget/FastScroller.java index fb57ce05d86c..00ebe0d8976a 100644 --- a/core/java/android/widget/FastScroller.java +++ b/core/java/android/widget/FastScroller.java @@ -468,7 +468,9 @@ class FastScroller { mListAdapter = (BaseAdapter) adapter; mSectionIndexer = (SectionIndexer) adapter; mSections = mSectionIndexer.getSections(); - + if (mSections == null) { + mSections = new String[] { " " }; + } } else { mListAdapter = (BaseAdapter) adapter; mSections = new String[] { " " }; @@ -609,7 +611,7 @@ class FastScroller { final int section = mSectionIndexer.getSectionForPosition(firstVisibleItem); final int sectionPos = mSectionIndexer.getPositionForSection(section); final int nextSectionPos = mSectionIndexer.getPositionForSection(section + 1); - final int sectionCount = mSectionIndexer.getSections().length; + final int sectionCount = mSections.length; final int positionsInSection = nextSectionPos - sectionPos; final View child = mList.getChildAt(0); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 470a23d6a77d..02c2b8f36988 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -8574,11 +8574,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final String originalText = mText.subSequence(spanStart, spanEnd).toString(); ((Editable) mText).replace(spanStart, spanEnd, suggestion); - // Swap text content between actual text and Suggestion span - String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); - suggestions[suggestionInfo.suggestionIndex] = originalText; - - // Notify source IME of the suggestion pick + // Notify source IME of the suggestion pick. Do this before swaping texts. if (!TextUtils.isEmpty( suggestionInfo.suggestionSpan.getNotificationTargetClassName())) { InputMethodManager imm = InputMethodManager.peekInstance(); @@ -8586,6 +8582,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener suggestionInfo.suggestionIndex); } + // Swap text content between actual text and Suggestion span + String[] suggestions = suggestionInfo.suggestionSpan.getSuggestions(); + suggestions[suggestionInfo.suggestionIndex] = originalText; + // Restore previous SuggestionSpans final int lengthDifference = suggestion.length() - (spanEnd - spanStart); for (int i = 0; i < length; i++) { diff --git a/core/java/com/android/internal/statusbar/IStatusBar.aidl b/core/java/com/android/internal/statusbar/IStatusBar.aidl index 7d214896f2b1..2ff04137a1fe 100644 --- a/core/java/com/android/internal/statusbar/IStatusBar.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBar.aidl @@ -31,7 +31,7 @@ oneway interface IStatusBar void animateExpand(); void animateCollapse(); void setLightsOn(boolean on); - void setMenuKeyVisible(boolean visible); + void topAppWindowChanged(boolean menuVisible); void setImeWindowStatus(in IBinder token, int vis, int backDisposition); void setHardKeyboardStatus(boolean available, boolean enabled); void userActivity(); diff --git a/core/java/com/android/internal/statusbar/IStatusBarService.aidl b/core/java/com/android/internal/statusbar/IStatusBarService.aidl index bfc717be3315..3f2b1efab135 100644 --- a/core/java/com/android/internal/statusbar/IStatusBarService.aidl +++ b/core/java/com/android/internal/statusbar/IStatusBarService.aidl @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - + package com.android.internal.statusbar; import com.android.internal.statusbar.IStatusBar; @@ -30,7 +30,7 @@ interface IStatusBarService void setIcon(String slot, String iconPackage, int iconId, int iconLevel); void setIconVisibility(String slot, boolean visible); void removeIcon(String slot); - void setMenuKeyVisible(boolean visible); + void topAppWindowChanged(boolean menuVisible); void setImeWindowStatus(in IBinder token, int vis, int backDisposition); // ---- Methods below are for use by the status bar policy services ---- diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index e3286dda28a3..9d8d361fee2a 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -302,6 +302,9 @@ public class ActionBarView extends AbsActionBarView { } public void setEmbeddedTabView(ScrollingTabContainerView tabs) { + if (mTabScrollView != null) { + removeView(mTabScrollView); + } mTabScrollView = tabs; mIncludeTabs = tabs != null; if (mIncludeTabs && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java index c7d37f21b559..5b4d7ab143fa 100644 --- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java +++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java @@ -236,8 +236,9 @@ public class ScrollingTabContainerView extends HorizontalScrollView { } mTextView.setText(text); mTextView.setVisibility(VISIBLE); - } else { + } else if (mTextView != null) { mTextView.setVisibility(GONE); + mTextView.setText(null); } } } diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index 57a97bd89572..7e82efb302ef 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -667,6 +667,11 @@ static void android_view_GLES20Canvas_drawLayer(JNIEnv* env, jobject clazz, renderer->drawLayer(layer, x, y, paint); } +static jboolean android_view_GLES20Canvas_copyLayer(JNIEnv* env, jobject clazz, + Layer* layer, SkBitmap* bitmap) { + return LayerRenderer::copyLayer(layer, bitmap); +} + #endif // USE_OPENGL_RENDERER // ---------------------------------------------------------------------------- @@ -792,6 +797,7 @@ static JNINativeMethod gMethods[] = { { "nDestroyLayer", "(I)V", (void*) android_view_GLES20Canvas_destroyLayer }, { "nDestroyLayerDeferred", "(I)V", (void*) android_view_GLES20Canvas_destroyLayerDeferred }, { "nDrawLayer", "(IIFFI)V", (void*) android_view_GLES20Canvas_drawLayer }, + { "nCopyLayer", "(II)Z", (void*) android_view_GLES20Canvas_copyLayer }, #endif }; diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml index fffcf26350db..6b4bf5c6fa7c 100644 --- a/core/res/res/values-de/donottranslate-cldr.xml +++ b/core/res/res/values-de/donottranslate-cldr.xml @@ -31,7 +31,7 @@ <string name="month_medium_february">Feb.</string> <string name="month_medium_march">Mär.</string> <string name="month_medium_april">Apr.</string> - <string name="month_medium_may">Mai.</string> + <string name="month_medium_may">Mai</string> <string name="month_medium_june">Jun.</string> <string name="month_medium_july">Jul.</string> <string name="month_medium_august">Aug.</string> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 966dbe58a87b..5e132823fdc6 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2932,4 +2932,19 @@ <!-- Button text for the edit menu in input method extract mode. [CHAR LIMIT=16] --> <string name="extract_edit_menu_button">Edit...</string> + + <!-- Notification title when data usage has exceeded warning threshold. [CHAR LIMIT=32] --> + <string name="data_usage_warning_title">Data usage warning</string> + <!-- Notification body when data usage has exceeded warning threshold. [CHAR LIMIT=32] --> + <string name="data_usage_warning_body">usage exceeds <xliff:g id="size" example="3.8GB">%s</xliff:g></string> + + <!-- Notification title when 2G-3G data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] --> + <string name="data_usage_3g_limit_title">2G-3G data disabled</string> + <!-- Notification title when 4G data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] --> + <string name="data_usage_4g_limit_title">4G data disabled</string> + <!-- Notification title when mobile data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] --> + <string name="data_usage_mobile_limit_title">Mobile data disabled</string> + <!-- Notification body when data usage has exceeded limit threshold, and has been disabled. [CHAR LIMIT=32] --> + <string name="data_usage_limit_body">tap to enable</string> + </resources> diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml index 96db035d3e44..58f158ce410f 100644 --- a/core/tests/bluetoothtests/AndroidManifest.xml +++ b/core/tests/bluetoothtests/AndroidManifest.xml @@ -19,6 +19,7 @@ <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java index 64d2c1257ea0..56e691d8c246 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestRunner.java @@ -47,7 +47,7 @@ import android.util.Log; * [-e pan_address <address>] \ * [-e pair_pin <pin>] \ * [-e pair_passkey <passkey>] \ - * -w com.android.frameworks.coretests/android.bluetooth.BluetoothTestRunner + * -w com.android.bluetooth.tests/android.bluetooth.BluetoothTestRunner * } * </pre> */ diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java index 5f4c226c8d4d..42e5cd17f2a7 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java @@ -290,7 +290,7 @@ public class BluetoothTestUtils extends Assert { @Override public void onReceive(Context context, Intent intent) { - if (AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED.equals(intent.getAction())) { + if (AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED.equals(intent.getAction())) { int state = intent.getIntExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, AudioManager.SCO_AUDIO_STATE_ERROR); assertNotSame(AudioManager.SCO_AUDIO_STATE_ERROR, state); @@ -936,7 +936,13 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.STATE_DISCONNECTED: case BluetoothProfile.STATE_DISCONNECTING: start = System.currentTimeMillis(); - assertTrue(proxy.connect(device)); + if (profile == BluetoothProfile.A2DP) { + assertTrue(((BluetoothA2dp)proxy).connect(device)); + } else if (profile == BluetoothProfile.HEADSET) { + assertTrue(((BluetoothHeadset)proxy).connect(device)); + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + assertTrue(((BluetoothInputDevice)proxy).connect(device)); + } break; default: removeReceiver(receiver); @@ -1005,7 +1011,13 @@ public class BluetoothTestUtils extends Assert { case BluetoothProfile.STATE_CONNECTED: case BluetoothProfile.STATE_CONNECTING: start = System.currentTimeMillis(); - assertTrue(proxy.disconnect(device)); + if (profile == BluetoothProfile.A2DP) { + assertTrue(((BluetoothA2dp)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.HEADSET) { + assertTrue(((BluetoothHeadset)proxy).disconnect(device)); + } else if (profile == BluetoothProfile.INPUT_DEVICE) { + assertTrue(((BluetoothInputDevice)proxy).disconnect(device)); + } break; case BluetoothProfile.STATE_DISCONNECTED: removeReceiver(receiver); diff --git a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java index 5d01ba0e0ee9..e5ab179f470e 100644 --- a/core/tests/coretests/src/android/pim/RecurrenceSetTest.java +++ b/core/tests/coretests/src/android/pim/RecurrenceSetTest.java @@ -21,7 +21,7 @@ import android.pim.ICalendar; import android.pim.RecurrenceSet; import android.test.suitebuilder.annotation.SmallTest; import android.util.Log; -import android.provider.Calendar; +import android.provider.CalendarContract; import junit.framework.TestCase; /** @@ -69,14 +69,14 @@ public class RecurrenceSetTest extends TestCase { RecurrenceSet.populateContentValues(recurrenceComponent, values); Log.d("KS", "values " + values); - assertEquals(rrule, values.get(android.provider.Calendar.Events.RRULE)); - assertEquals(rdate, values.get(android.provider.Calendar.Events.RDATE)); - assertEquals(exrule, values.get(android.provider.Calendar.Events.EXRULE)); - assertEquals(exdate, values.get(android.provider.Calendar.Events.EXDATE)); - assertEquals(dtstart, (long) values.getAsLong(Calendar.Events.DTSTART)); - assertEquals(tzid, values.get(android.provider.Calendar.Events.EVENT_TIMEZONE)); - assertEquals(duration, values.get(android.provider.Calendar.Events.DURATION)); + assertEquals(rrule, values.get(android.provider.CalendarContract.Events.RRULE)); + assertEquals(rdate, values.get(android.provider.CalendarContract.Events.RDATE)); + assertEquals(exrule, values.get(android.provider.CalendarContract.Events.EXRULE)); + assertEquals(exdate, values.get(android.provider.CalendarContract.Events.EXDATE)); + assertEquals(dtstart, (long) values.getAsLong(CalendarContract.Events.DTSTART)); + assertEquals(tzid, values.get(android.provider.CalendarContract.Events.EVENT_TIMEZONE)); + assertEquals(duration, values.get(android.provider.CalendarContract.Events.DURATION)); assertEquals(allDay, - (int) values.getAsInteger(android.provider.Calendar.Events.ALL_DAY)); + (int) values.getAsInteger(android.provider.CalendarContract.Events.ALL_DAY)); } } diff --git a/data/etc/handheld_core_hardware.xml b/data/etc/handheld_core_hardware.xml index 7f87b7923d86..9d2a0cbb60e9 100644 --- a/data/etc/handheld_core_hardware.xml +++ b/data/etc/handheld_core_hardware.xml @@ -31,6 +31,8 @@ <feature name="android.hardware.bluetooth" /> <feature name="android.hardware.touchscreen" /> <feature name="android.hardware.microphone" /> + <feature name="android.hardware.screen.portrait" /> + <feature name="android.hardware.screen.landscape" /> <!-- devices with GPS must include android.hardware.location.gps.xml --> <!-- devices with an autofocus camera and/or flash must include either android.hardware.camera.autofocus.xml or diff --git a/data/etc/tablet_core_hardware.xml b/data/etc/tablet_core_hardware.xml index 952d078253da..bf29fe4b7003 100644 --- a/data/etc/tablet_core_hardware.xml +++ b/data/etc/tablet_core_hardware.xml @@ -32,6 +32,8 @@ <feature name="android.hardware.touchscreen.multitouch" /> <feature name="android.hardware.touchscreen.multitouch.distinct" /> <feature name="android.hardware.microphone" /> + <feature name="android.hardware.screen.portrait" /> + <feature name="android.hardware.screen.landscape" /> <!-- devices with GPS must include android.hardware.location.gps.xml --> <!-- devices with a rear-facing camera must include one of these as appropriate: android.hardware.camera.xml or diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf Binary files differindex ba9d76f0c8fa..03ceae5df855 100644 --- a/data/fonts/DroidSansFallback.ttf +++ b/data/fonts/DroidSansFallback.ttf diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 916da0994d79..55d711f7adf2 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -244,9 +244,6 @@ <li><a href="<?cs var:toroot ?>guide/topics/graphics/opengl.html"> <span class="en">3D with OpenGL</span> </a></li> - <li><a href="<?cs var:toroot ?>guide/topics/graphics/renderscript.html"> - <span class="en">3D with Renderscript</span> - </a></li> <li><a href="<?cs var:toroot ?>guide/topics/graphics/animation.html"> <span class="en">Property Animation</span> </a></li> @@ -255,6 +252,23 @@ </a></li> </ul> </li> + <li class="toggle-list"> + <div><a href="<?cs var:toroot ?>guide/topics/renderscript/index.html"> + <span class="en">RenderScript</span> + </a> + <span class="new-child">new!</span></div> + <ul> + <li><a href="<?cs var:toroot ?>guide/topics/renderscript/graphics.html"> + <span class="en">3D Graphics</span> + </a> + </li> + <li><a href="<?cs var:toroot ?>guide/topics/renderscript/compute.html"> + <span class="en">Compute</span> + </a> + </li> + </ul> + </li> + <li><a href="<?cs var:toroot ?>guide/topics/media/index.html"> <span class="en">Audio and Video</span> </a></li> diff --git a/docs/html/guide/topics/graphics/animation.jd b/docs/html/guide/topics/graphics/animation.jd index 0b02ee767e7d..31e7c4b79e9e 100644 --- a/docs/html/guide/topics/graphics/animation.jd +++ b/docs/html/guide/topics/graphics/animation.jd @@ -81,6 +81,12 @@ parent.link=index.html If view animation accomplishes everything that you need to do, or if your existing code already works the way you want, there is no need to use the property animation system.</p> + <p class="note"><strong>Tip:</strong> To see how the ADT layout editor allows you to develop and +preview animations in your layout, watch the <a +href="http://www.youtube.com/watch?v=Oq05KqjXTvs&feature=player_detailpage#t=1709s">Android +Developer Tools session</a> from Google I/O '11</p> + + <h2 id="what">What is Property Animation?</h2> A property animation changes a property's (a field in an object) value over a specified length of time. To animate something, you specify the @@ -108,6 +114,7 @@ parent.link=index.html default is set to refresh every 10 ms, but the speed in which your application can refresh frames is ultimately dependent on how busy the system is overall and how fast the system can service the underlying timer.</li> </ul> + <h3 id="how">How the property animation system works</h3> @@ -894,99 +901,5 @@ resources.</p> <li>{@link android.animation.AnimatorSet} - <code><set></code></li> </ul> - <p>Both <code><animator></code> ({@link android.animation.ValueAnimator}) and - <code><objectAnimator></code> ({@link android.animation.ObjectAnimator}) have the following - attributes:</p> - - <dl> - <dt><code>android:duration</code></dt> - - <dd>The number of milliseconds that the animation runs. The default is 300 ms.</dd> - - <dt><code>android:valueFrom</code> and <code>android:valueTo</code></dt> - - <dd>The values being animated between. These are restricted to numbers (<code>float</code> or - <code>int</code>) and color values (such as #00ff00). They can be <code>float</code>, <code>int</code>, colors, - or any kind of <code>Object</code> when creating animations programmatically.</dd> - - <dt><code>android:valueType</code></dt> - - <dd>Set to either <code>"floatType"</code> or <code>"intType"</code>. The default is - <code>"floatType"</code> unless you specify something else or if the <code>valuesFrom</code> - and <code>valuesTo</code> values are colors.</dd> - - <dt><code>android:startOffset</code></dt> - - <dd>The delay, in milliseconds, before the animation begins playing (after calling {@link - android.animation.ValueAnimator#start start()}).</dd> +<p>See <a href="{@docRoot}guide/topics/resources/animation-resource.html#Property">Animation Resources</a> - <dt><code>android:repeatCount</code></dt> - - <dd>How many times to repeat an animation. Set to <code>"-1"</code> to infinitely repeat or - to a positive integer. For example, a value of <code>"1"</code> means that the animation is - repeated once after the initial run of the animation, so the animation plays a total of two - times. The default value is <code>"0"</code>, which means no repetition.</dd> - - <dt><code>android:repeatMode</code></dt> - - <dd>How an animation behaves when it reaches the end of the animation. - <code>android:repeatCount</code> must be set to a positive integer or <code>"-1"</code> for - this attribute to have an effect. Set to <code>"reverse"</code> to have the animation reverse - direction with each iteration or <code>"repeat"</code> to have the animation loop from the - beginning each time.</dd> - </dl> - - <p>The <code><objectAnimator></code> ({@link android.animation.ObjectAnimator}) element has the - additional attribute <code>android:propertyName</code>, that lets you specify the name of the -property - being animated. The <code><objectAnimator></code> element does not expose a <code>target</code> - attribute, however, so you cannot set the object to animate in the XML declaration. You have to - inflate the XML resource by calling {@link android.animation.AnimatorInflater#loadAnimator - loadAnimator()} and call {@link android.animation.ObjectAnimator#setTarget setTarget()} to set - the target object unlike the underlying {@link android.animation.ObjectAnimator}, - before calling {@link android.animation.ObjectAnimator#start start()}.</p> - - <p>The <code><set></code> element ({@link android.animation.AnimatorSet}) exposes a single - attribute, <code>android:ordering</code>. Set this attribute to <code>"together"</code> (default) -to play - all the animations in this set at once. Set this attribute to <code>"sequentially"</code> to play - the animations in the order they are declared.</p> - - <p>You can specify nested <code><set></code> elements to further group animations together. -The - animations that you want to group together should be children of the <code><set></code> tag and can - define their own <code>ordering</code> attribute.</p> - - <p>As an example, this XML code creates an {@link android.animation.AnimatorSet} object that - animates x and y at the same time, then runs an animation that fades an object out:</p> - <pre> -<set android:ordering="sequentially"> - <set> - <objectAnimator - android:propertyName="x" - android:duration="500" - android:valueTo="400" - android:valueType="int"/> - <objectAnimator - android:propertyName="y" - android:duration="500" - android:valueTo="300" - android:valueType="int"/> - </set> - <objectAnimator - android:propertyName="alpha" - android:duration="500" - android:valueTo="0f"/> -</set> -</pre> - - <p>In order to run this animation, you must inflate the XML resources in your code to an {@link - android.animation.AnimatorSet} object, and then set the target objects for all of the animations - before starting the animation set. Calling {@link android.animation.AnimatorSet#setTarget - setTarget()} sets a single target object for all children of the {@link - android.animation.AnimatorSet}.</p> - -<p class="note"><strong>Tip:</strong> To see how the ADT layout editor allows you to develop and -preview animations in your layout, watch the <a -href="http://www.youtube.com/watch?v=Oq05KqjXTvs&feature=player_detailpage#t=1709s">Android -Developer Tools session</a> from Google I/O '11</p> diff --git a/docs/html/guide/topics/graphics/renderscript.html b/docs/html/guide/topics/graphics/renderscript.html new file mode 100644 index 000000000000..454d39288d73 --- /dev/null +++ b/docs/html/guide/topics/graphics/renderscript.html @@ -0,0 +1,10 @@ +<html> +<head> +<meta http-equiv="refresh" content="0;url=http://developer.android.com/guide/topics/renderscript/index.html"> +<title>Redirecting...</title> +</head> +<body> +<p>You should be redirected. Please <a +href="http://developer.android.com/guide/topics/renderscript/index.html">click here</a>.</p> +</body> +</html>
\ No newline at end of file diff --git a/docs/html/guide/topics/graphics/renderscript.jd b/docs/html/guide/topics/graphics/renderscript.jd deleted file mode 100644 index 180322f55435..000000000000 --- a/docs/html/guide/topics/graphics/renderscript.jd +++ /dev/null @@ -1,716 +0,0 @@ -page.title=3D Rendering and Computation with Renderscript -parent.title=Graphics -parent.link=index.html -@jd:body - - <div id="qv-wrapper"> - <div id="qv"> - <h2>In this document</h2> - - <ol> - <li><a href="#overview">Renderscript System Overview</a></li> - - <li> - <a href="#api">API Overview</a> - - <ol> - <li><a href="#native-api">Native Renderscript APIs</a></li> - - <li><a href="#reflective-api">Reflected layer APIs</a></li> - - <li><a href="#graphics-api">Graphics APIs</a></li> - </ol> - </li> - - <li> - <a href="#developing">Developing a Renderscript application</a> - - <ol> - <li><a href="#hello-graphics">The Hello Graphics application</a></li> - </ol> - </li> - </ol> - <h2>Related Samples</h2> - <ol> - <li><a href="{@docRoot}resources/samples/Renderscript/Balls/index.html">Balls</a></li> - <li><a href="{@docRoot}resources/samples/Renderscript/Fountain/index.html">Fountain</a></li> - <li><a href="{@docRoot}resources/samples/Renderscript/HelloCompute/index.html">Hello Compute</a></li> - <li><a href="{@docRoot}resources/samples/Renderscript/HelloWorld/index.html">Hello World</a></li> - <li><a href="{@docRoot}resources/samples/Renderscript/Samples/index.html">Samples</a></li> - </ol> - </div> - </div> - - <p>The Renderscript system offers high performance 3D rendering and mathematical computation at - the native level. The Renderscript APIs are intended for developers who are comfortable with - developing in C (C99 standard) and want to maximize performance in their applications. The - Renderscript system improves performance by running as native code on the device, but it also - features cross-platform functionality. To achieve this, the Android build tools compile your - Renderscript <code>.rs</code> file to intermediate bytecode and package it inside your - application's <code>.apk</code> file. On the device, the bytecode is compiled (just-in-time) to - machine code that is further optimized for the device that it is running on. This eliminates the - need to target a specific architecture during the development process. The compiled code on the - device is cached, so subsequent uses of the Renderscript enabled application do not recompile the - intermediate code.</p> - - <p>The disadvantage of the Renderscript system is that it adds complexity to the development and - debugging processes. Debugging visibility can be limited, because the - Renderscript system can execute on processors other than the main CPU (such as the GPU), so if - this occurs, debugging becomes more difficult. The target use is for performance - critical code where the traditional framework APIs (such as using {@link android.opengl}) are not sufficient. - If what you are rendering or computing is very simple and does not require much processing power, you should still use the - traditional framework APIs for ease of development. Remember the tradeoffs between development and - debugging complexity versus performance when deciding to use Renderscript. </p> - - <p>For an example of Renderscript in action, see the 3D carousel view in the Android 3.0 versions - of Google Books and YouTube or install the Renderscript sample applications that are shipped with - the SDK in <code><sdk_root>/samples/android-11/Renderscript</code>.</p> - - <h2 id="overview">Renderscript System Overview</h2> - - <p>The Renderscript system adopts a control and slave architecture where the low-level native - code is controlled by the higher level Android system that runs in the virtual machine (VM). When - you use the Renderscript system, there are three layers that exist:</p> - - <ul> - <li>The native Renderscript layer consists of native libraries that are packaged with the SDK. - The native Renderscript <code>.rs</code> files compute mathematical operations, render graphics, - or both. This layer does the intensive computation or graphics rendering and returns the result - back to the Android VM through the reflected layer.</li> - - <li>The reflected layer is a set of generated Android framework classes reflected from - the native Renderscript code that you wrote. This layer acts as a bridge between the native - Renderscript layer and the Android system layer. The Android build tools automatically generate - the classes for this layer during the build process. This layer also includes a set of Android - framework APIs that provide the memory and resource allocation classes to support this layer.</li> - - <li>The Android system layer consists of the traditional framework APIs, which include the Renderscript - APIs in {@link android.renderscript}. This layer handles things such as the Activity lifecycle - management of your application and calls the reflected layer to communicate with the native Renderscript code.</li> - </ul> - - <p>To fully understand how the Renderscript system works, you must understand how the reflected - layer is generated and how it interacts with the native Renderscript layer and Android system - layer. The reflected layer provides the entry points into the native code, enabling the Android - system to give high level commands like, "rotate the view" or "filter the bitmap" to the - native layer, which does the heavy lifting. To accomplish this, you need to create logic - to hook together all of these layers so that they can correctly communicate.</p> - - <p>At the root of everything is your Renderscript, which is the actual C code that you write and - save to a <code>.rs</code> file in your project. There are two kinds of Renderscripts: compute - and graphics. A compute Renderscript does not do any graphics rendering while a graphics - Renderscript does.</p> - - <p>When you create Renderscript <code>.rs</code> files, equivalent, reflected classes - are generated by the build tools and expose the native functions and data types and structures - to the Android system. The following list describes the major components of your native Renderscript - code that is reflected:</p> - - <ul> - <li>The non-static functions in your Renderscript (<code>.rs</code> file) are reflected into - <code><em>ScriptC_renderscript_filename</em></code> of type {@link - android.renderscript.ScriptC}.</li> - - <li>Any non-static, global Renderscript variables are reflected into - <code><em>ScriptC_renderscript_filename</em></code>. - Accessor methods are generated, so the Android system layer can access the values. - The <code>get</code> method comes with a one-way communication restriction. - The Android system layer always caches the last value that is set and returns that during a call to a <code>get</code> method. - If the native Renderscript code changes the value, the change does not propagate back to the Android system layer. - If the global variables are initialized in the native Renderscript code, those values are used - to initialize the corresponding values in the Android system. If global variables are marked as <code>const</code>, - then a <code>set</code> method is not generated. - </li> - - <li>Structs are reflected into their own classes, one for each struct, into a class named - <code>ScriptField_<em>struct_name</em></code> of type {@link - android.renderscript.Script.FieldBase}.</li> - - <li>Global pointers have a special property. They provide attachment points where the Android system can attach allocations. - If the global pointer is a user defined structure type, it must be a type that is legal for reflection (primitives - or Renderscript data types). The Android system can call the reflected class to allocate memory and - optionally populate data, then attach it to the Renderscript. - For arrays of basic types, the procedure is similar, except a reflected class is not needed. - Renderscripts should not directly set the exported global pointers.</li> - </ul> - - <p>The Android framework API also has a corresponding Renderscript context object, {@link - android.renderscript.RenderScript} (for a compute Renderscript) or {@link - android.renderscript.RenderScriptGL} (for a graphics Renderscript). This context object allows - you to bind to the reflected Renderscript class, so that the Renderscript context knows what its - corresponding native Renderscript is. If you have a graphics Renderscript context, you can also - specify a variety of Programs (stages in the graphics pipeline) to tweek how your graphics are - rendered. A graphics Renderscript context also needs a surface to render on, {@link - android.renderscript.RSSurfaceView}, which gets passed into its constructor.</p> - - <h2 id="api">API overview</h2> - - <p>Renderscript code is compiled and executed in a compact and well defined runtime, which has - access to a limited amount of functions. Renderscript cannot use the NDK or standard C functions, - because these functions are assumed to be running on a standard CPU. The Renderscript runtime - chooses the best processor to execute the code, which may not be the CPU, so it cannot guarantee - support for standard C libraries. What Renderscript does offer is an API that supports intensive - computation with an extensive collection of math APIs. The following sections group the APIs - into three distinct categories.</p> - - - <h3 id="native-api">Native Renderscript APIs</h3> - - <p>The Renderscript headers are located in the <code>include</code> and - <code>clang-include</code> directories in the - <code><sdk_root>/platforms/android-11/renderscript</code> directory of the Android SDK. - The headers are automatically included for you, except for the graphics specific header, - which you can define as follows:</p> - -<pre>#include "rs_graphics.rsh"</pre> - -<p>Some key features of the native Renderscript libraries include: - <ul> - <li>A large collection of math functions with both scalar and vector typed overloaded versions - of many common routines. Operations such as adding, multiplying, dot product, and cross product - are available.</li> - <li>Conversion routines for primitive data types and vectors, matrix routines, date and time - routines, and graphics routines.</li> - <li>Logging functions</li> - <li>Graphics rendering functions</li> - <li>Memory allocation request features</li> - <li>Data types and structures to support the Renderscript system such as - Vector types for defining two-, three-, or four-vectors.</li> - </ul> - - <h3 id="reflective-api">Reflected layer APIs</h3> - - <p>These classes are mainly used by the reflected classes that are generated from your native Renderscript - code. They allocate and manage memory for your Renderscript on the Android system side. - You normally do not need to call these classes directly.</p> - - <p>Because of the constraints of the Renderscript native layer, you cannot do any dynamic - memory allocation in your Renderscript <code>.rs</code> file. - The native Renderscript layer can request memory from the Android system layer, which allocates memory - for you and does reference counting to figure out when to free the memory. A memory allocation - is taken care of by the {@link android.renderscript.Allocation} class and memory is requested - in your Renderscript code with the <code>the rs_allocation</code> type. - All references to Renderscript objects are counted, so when your Renderscript native code - or system code no longer references a particular {@link android.renderscript.Allocation}, it destroys itself. - Alternatively, you can call {@link android.renderscript.Allocation#destroy destroy()} from the - Android system level, which decreases the reference to the {@link android.renderscript.Allocation}. - If no references exist after the decrease, the {@link android.renderscript.Allocation} destroys itself. - The Android system object, which at this point is just an empty shell, is eventually garbage collected. - </p> - - <p>The following classes are mainly used by the reflected layer classes:</p> - - <table> - <tr> - <th>Android Object Type</th> - - <th>Renderscript Native Type</th> - - <th>Description</th> - </tr> - - <tr> - <td>{@link android.renderscript.Element}</td> - - <td>rs_element</td> - - <td> - An {@link android.renderscript.Element} is the most basic element of a memory type. An - element represents one cell of a memory allocation. An element can have two forms: Basic or - Complex. They are typically created from C structures in your Renderscript - code during the reflection process. Elements cannot contain pointers or nested arrays. - The other common source of elements is bitmap formats. - - <p>A basic element contains a single component of data of any valid Renderscript data type. - Examples of basic element data types include a single float value, a float4 vector, or a - single RGB-565 color.</p> - - <p>Complex elements contain a list of sub-elements and names that is basically a reflection - of a C struct. You access the sub-elements by name from a script or vertex program. The - most basic primitive type determines the data alignment of the structure. For example, a - float4 vector is alligned to <code>sizeof(float)</code> and not - <code>sizeof(float4)</code>. The ordering of the elements in memory are the order in which - they were added, with each component aligned as necessary.</p> - </td> - </tr> - - <tr> - <td>{@link android.renderscript.Type}</td> - - <td>rs_type</td> - - <td>A Type is an allocation template that consists of an element and one or more dimensions. - It describes the layout of the memory but does not allocate storage for the data that it - describes. A Type consists of five dimensions: X, Y, Z, LOD (level of detail), and Faces (of - a cube map). You can assign the X,Y,Z dimensions to any positive integer value within the - constraints of available memory. A single dimension allocation has an X dimension of greater - than zero while the Y and Z dimensions are zero to indicate not present. For example, an - allocation of x=10, y=1 is considered two dimensional and x=10, y=0 is considered one - dimensional. The LOD and Faces dimensions are booleans to indicate present or not - present.</td> - </tr> - - <tr> - <td>{@link android.renderscript.Allocation}</td> - - <td>rs_allocation</td> - - <td> - <p>An {@link android.renderscript.Allocation} provides the memory for applications. An {@link - android.renderscript.Allocation} allocates memory based on a description of the memory that - is represented by a {@link android.renderscript.Type}. The type describes an array of elements that - represent the memory to be allocated. Allocations are the primary way data moves into and - out of scripts.</p> - - <p>Memory is user-synchronized and it's possible for allocations to exist in multiple - memory spaces concurrently. For example, if you make a call to the graphics card to load a - bitmap, you give it the bitmap to load from in the system memory. After that call returns, - the graphics memory contains its own copy of the bitmap so you can choose whether or not to - maintain the bitmap in the system memory. If the Renderscript system modifies an allocation - that is used by other targets, it must call {@link android.renderscript#syncAll syncAll()} to push the updates to - the memory. Otherwise, the results are undefined.</p> - - <p>Allocation data is uploaded in one of two primary ways: type checked and type unchecked. - For simple arrays there are <code>copyFrom()</code> functions that take an array from the - Android system and copy it to the native layer memory store. Both type checked and - unchecked copies are provided. The unchecked variants allow the Android system to copy over - arrays of structures because it does not support structures. For example, if - there is an allocation that is an array n floats, you can copy the data contained in a - float[n] array or a byte[n*4] array.</p> - </td> - </tr> - - <tr> - <td>{@link android.renderscript.Script}</td> - - <td>rs_script</td> - - <td>Renderscript scripts do much of the work in the native layer. This class is generated - from a Renderscript file that has the <code>.rs</code> file extension. This class is named - <code>ScriptC_<em>rendersript_filename</em></code> when it gets generated.</td> - </tr> - </table> - - <h3 id="graphics-api">Graphics API</h3> - - <p>Renderscript provides a number of graphics APIs for hardware-accelerated 3D rendering. The - Renderscript graphics APIs include a stateful context, {@link - android.renderscript.RenderScriptGL} that contains the current rendering state. The primary state - consists of the objects that are attached to the rendering context, which are the graphics Renderscript - and the four program types. The main working function of the graphics Renderscript is the code that is - defined in the <code>root()</code> function. The <code>root()</code> function is called each time the surface goes through a frame - refresh. The four program types mirror a traditional graphical rendering pipeline and are:</p> - - <ul> - <li>Vertex</li> - - <li>Fragment</li> - - <li>Store</li> - - <li>Raster</li> - </ul> - - <p>Graphical scripts have more properties beyond a basic computational script, and they call the - 'rsg'-prefixed functions defined in the <code>rs_graphics.rsh</code> header file. A graphics - Renderscript can also set four pragmas that control the default bindings to the {@link - android.renderscript.RenderScriptGL} context when the script is executing:</p> - - <ul> - <li>stateVertex</li> - - <li>stateFragment</li> - - <li>stateRaster</li> - - <li>stateStore</li> - </ul> - - <p>The possible values are <code>parent</code> or <code>default</code> for each pragma. Using - <code>default</code> says that when a script is executed, the bindings to the graphical context - are the system defaults. Using <code>parent</code> says that the state should be the same as it - is in the calling script. If this is a root script, the parent - state is taken from the bind points as set in the {@link android.renderscript.RenderScriptGL} - bind methods in the control environment (VM environment).</p> - - <p>For example, you can define this at the top of your native graphics Renderscript code:</p> - <pre> -#pragma stateVertex(parent) -#pragma stateStore(parent) -</pre> - - <p>The following table describes the major graphics specific APIs that are available to you:</p> - - <table> - <tr> - <th>Android Object Type</th> - - <th>Renderscript Native Type</th> - - <th>Description</th> - </tr> - - <tr> - <td>{@link android.renderscript.ProgramVertex}</td> - - <td>rs_program_vertex</td> - - <td> - <p>The Renderscript vertex program, also known as a vertex shader, describes the stage in the - graphics pipeline responsible for manipulating geometric data in a user-defined way. The - object is constructed by providing Renderscript with the following data:</p> - - <ul> - <li>An Element describing its varying inputs or attributes</li> - - <li>GLSL shader string that defines the body of the program</li> - - <li>a Type that describes the layout of an Allocation containing constant or uniform - inputs</li> - </ul> - - <p>Once the program is created, bind it to the {@link android.renderscript.RenderScriptGL} - graphics context by calling - {@link android.renderscript.RenderScriptGL#bindProgramVertex bindProgramVertex()}. It is then used for all - subsequent draw calls until you bind a new program. If the program has constant inputs, the - user needs to bind an allocation containing those inputs. The allocation's type must match - the one provided during creation. The Renderscript library then does all the necessary - plumbing to send those constants to the graphics hardware. Varying inputs to the shader, - such as position, normal, and texture coordinates are matched by name between the input - Element and the Mesh object being drawn. The signatures don't have to be exact or in any - strict order. As long as the input name in the shader matches a channel name and size - available on the mesh, the run-time would take care of connecting the two. Unlike OpenGL, - there is no need to link the vertex and fragment programs.</p> - <p> To bind shader constructs to the Program, declare a struct containing the necessary shader constants in your native Renderscript code. - This struct is generated into a reflected class that you can use as a constant input element - during the Program's creation. It is an easy way to create an instance of this struct as an allocation. - You would then bind this Allocation to the Program and the Renderscript system sends the data that - is contained in the struct to the hardware when necessary. To update shader constants, you change the values - in the Allocation and notify the native Renderscript code of the change.</p> - </td> - </tr> - - <tr> - <td>{@link android.renderscript.ProgramFragment}</td> - - <td>rs_program_fragment</td> - - <td><p>The Renderscript fragment program, also known as the fragment shader, is responsible for - manipulating pixel data in a user-defined way. It's constructed from a GLSL shader string - containing the program body, textures inputs, and a Type object describing the constants used - by the program. Like the vertex programs, when an allocation with constant input values is - bound to the shader, its values are sent to the graphics program automatically. Note that the - values inside the allocation are not explicitly tracked. If they change between two draw - calls using the same program object, notify the runtime of that change by calling - rsgAllocationSyncAll so it could send the new values to hardware. Communication between the - vertex and fragment programs is handled internally in the GLSL code. For example, if the - fragment program is expecting a varying input called varTex0, the GLSL code inside the - program vertex must provide it.</p> - <p> To bind shader constructs to the this Program, declare a struct containing the necessary shader constants in your native Renderscript code. - This struct is generated into a reflected class that you can use as a constant input element - during the Program's creation. It is an easy way to create an instance of this struct as an allocation. - You would then bind this Allocation to the Program and the Renderscript system sends the data that - is contained in the struct to the hardware when necessary. To update shader constants, you change the values - in the Allocation and notify the native Renderscript code of the change.</p></td> - </tr> - - <tr> - <td>{@link android.renderscript.ProgramStore}</td> - - <td>rs_program_store</td> - - <td>The Renderscript ProgramStore contains a set of parameters that control how the graphics - hardware writes to the framebuffer. It could be used to enable and disable depth writes and - testing, setup various blending modes for effects like transparency and define write masks - for color components.</td> - </tr> - - <tr> - <td>{@link android.renderscript.ProgramRaster}</td> - - <td>rs_program_raster</td> - - <td>Program raster is primarily used to specify whether point sprites are enabled and to - control the culling mode. By default back faces are culled.</td> - </tr> - - <tr> - <td>{@link android.renderscript.Sampler}</td> - - <td>rs_sampler</td> - - <td>A Sampler object defines how data is extracted from textures. Samplers are bound to - Program objects (currently only a Fragment Program) alongside the texture whose sampling they - control. These objects are used to specify such things as edge clamping behavior, whether - mip-maps are used and the amount of anisotropy required. There may be situations where - hardware limitations prevent the exact behavior from being matched. In these cases, the - runtime attempts to provide the closest possible approximation. For example, the user - requested 16x anisotropy, but only 8x was set because it's the best available on the - hardware.</td> - </tr> - - <tr> - <td>{@link android.renderscript.Mesh}</td> - - <td>rs_mesh</td> - - <td>A collection of allocations that represent vertex data (positions, normals, texture - coordinates) and index data such as triangles and lines. Vertex data can be interleaved - within one allocation, provided separately as multiple allocation objects, or done as a - combination of the above. The layout of these allocations will be extracted from their - Elements. When a vertex channel name matches an input in the vertex program, Renderscript - automatically connects the two. Moreover, even allocations that cannot be directly mapped to - graphics hardware can be stored as part of the mesh. Such allocations can be used as a - working area for vertex-related computation and will be ignored by the hardware. Parts of the - mesh could be rendered with either explicit index sets or primitive types.</td> - </tr> - - <tr> - <td>{@link android.renderscript.Font}</td> - - <td>rs_font</td> - - <td> - <p>This class gives you a way to draw hardware accelerated text. Internally, the glyphs are - rendered using the Freetype library, and an internal cache of rendered glyph bitmaps is - maintained. Each font object represents a combination of a typeface and point sizes. - Multiple font objects can be created to represent faces such as bold and italic and to - create different font sizes. During creation, the framework determines the device screen's - DPI to ensure proper sizing across multiple configurations.</p> - - <p>Font rendering can impact performance. Even though though the state changes are - transparent to the user, they are happening internally. It is more efficient to render - large batches of text in sequence, and it is also more efficient to render multiple - characters at once instead of one by one.</p> - - <p>Font color and transparency are not part of the font object and can be freely modified - in the script to suit the your needs. Font colors work as a state machine, and every new - call to draw text will use the last color set in the script.</p> - </td> - </tr> - </table> - - - <h2 id="developing">Developing a Renderscript application</h2> - - <p>The basic workflow of developing a Renderscript application is:</p> - - <ol> - <li>Analyze your application's requirements and figure out what you want to develop with - Renderscript. To take full advantage of the Renderscript system, you want to use it when the computation - or graphics performance you're getting with the traditional framework APIs is - insufficient.</li> - - <li>Design the interface of your Renderscript code and implement it using the native - Renderscript APIs that are included in the Android SDK in - <code><sdk_root>/platforms/android-11/renderscript</code>.</li> - - <li>Create an Android project as you would normally, in Eclipse or with the - <code>android</code> tool.</li> - - <li>Place your Renderscript files in <code>src</code> folder of the Android project so that the - build tools can generate the reflected layer classes.</li> - - <li>Create your application, calling the Renderscript through the reflected class layer when - you need to.</li> - - <li>Build, install, and run your application as you would normally.</li> - </ol> - - <p>To see how a simple Renderscript application is put together, see the - <a href="{@docRoot}resources/samples/Renderscript/index.html">Renderscript samples</a> - and <a href="#hello-graphics">The Hello Graphics Application</a> section of the documentation.</p> - - <h3 id="hello-graphics">The Hello Graphics Application</h3> - - <p>This small application demonstrates the structure of a simple Renderscript application. You - can model your Renderscript application after the basic structure of this application. You can - find the complete source in the SDK in the - <code><android-sdk>/samples/android-11/HelloWorldRS directory</code>. The - application uses Renderscript to draw the string, "Hello World!" to the screen and redraws the - text whenever the user touches the screen at the location of the touch. This application is only - a demonstration and you should not use the Renderscript system to do something this trivial. The - application contains the following source files:</p> - - <ul> - <li><code>HelloWorld</code>: The main Activity for the application. This class is present to - provide Activity lifecycle management. It mainly delegates work to HelloWorldView, which is the - Renderscript surface that the sample actually draws on.</li> - - <li><code>HelloWorldView</code>: The Renderscript surface that the graphics render on. If you - are using Renderscript for graphics rendering, you must have a surface to render on. If you are - using it for computatational operations only, then you do not need this.</li> - - <li><code>HelloWorldRS</code>: The class that calls the native Renderscript code through high - level entry points that are generated by the Android build tools.</li> - - <li><code>helloworld.rs</code>: The Renderscript native code that draws the text on the - screen.</li> - - <li> - <p>The <code><project_root>/gen</code> directory contains the reflected layer classes - that are generated by the Android build tools. You will notice a - <code>ScriptC_helloworld</code> class, which is the reflective version of the Renderscript - and contains the entry points into the <code>helloworld.rs</code> native code. This file does - not appear until you run a build.</p> - </li> - </ul> - - <p>Each file has its own distinct use. The following files comprise the main parts of the sample and - demonstrate in detail how the sample works:</p> - - <dl> - <dt><code>helloworld.rs</code></dt> - - <dd> - The native Renderscript code is contained in the <code>helloworld.rs</code> file. Every - <code>.rs</code> file must contain two pragmas that define the version of Renderscript - that it is using (1 is the only version for now), and the package name that the reflected - classes should be generated with. For example: -<pre> -#pragma version(1) - -#pragma rs java_package_name(com.my.package.name) -</pre> - <p>An <code>.rs</code> file can also declare two special functions:</p> - - <ul> - <li> - <code>init()</code>: This function is called once for each instance of this Renderscript - file that is loaded on the device, before the script is accessed in any other way by the - Renderscript system. The <code>init()</code> is ideal for doing one time setup after the - machine code is loaded such as initializing complex constant tables. The - <code>init()</code> function for the <code>helloworld.rs</code> script sets the initial - location of the text that is rendered to the screen: - <pre> -void init(){ - gTouchX = 50.0f; - gTouchY = 50.0f; -} -</pre> - </li> - - <li> - <code>root()</code>: This function is the default worker function for this Renderscript - file. For graphics Renderscript applications, like this one, the Renderscript system - expects this function to render the frame that is going to be displayed. It is called - every time the frame refreshes. The <code>root()</code> function for the - <code>helloworld.rs</code> script sets the background color of the frame, the color of - the text, and then draws the text where the user last touched the screen: -<pre> -int root(int launchID) { - // Clear the background color - rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f); - // Tell the runtime what the font color should be - rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); - // Introduce ourselves to the world by drawing a greeting - // at the position that the user touched on the screen - rsgDrawText("Hello World!", gTouchX, gTouchY); - - // Return value tells RS roughly how often to redraw - // in this case 20 ms - return 20; -} -</pre> - - <p>The return value, <code>20</code>, is the desired frame refresh rate in milliseconds. - The real screen refresh rate depends on the hardware, computation, and rendering - complexity that the <code>root()</code> function has to execute. A value of - <code>0</code> tells the screen to render only once and to only render again when a - change has been made to one of the properties that are being modified by the Renderscript - code.</p> - - <p>Besides the <code>init()</code> and <code>root()</code> functions, you can define the - other native functions, structs, data types, and any other logic for your Renderscript. - You can even define separate header files as <code>.rsh</code> files.</p> - </li> - </ul> - </dd> - - <dt><code>ScriptC_helloworld</code></dt> - - <dd>This class is generated by the Android build tools and is the reflected version of the - <code>helloworld.rs</code> Renderscript. It provides a high level entry point into the - <code>helloworld.rs</code> native code by defining the corresponding methods that you can call - from the traditional framework APIs.</dd> - - <dt><code>helloworld.bc</code> bytecode</dt> - - <dd>This file is the intermediate, platform-independent bytecode that gets compiled on the - device when the Renderscript application runs. It is generated by the Android build tools and - is packaged with the <code>.apk</code> file and subsequently compiled on the device at runtime. - This file is located in the <code><project_root>/res/raw/</code> directory and is named - <code>rs_filename.bc</code>. You need to bind these files to your Renderscript context before - call any Renderscript code from your Android application. You can reference them in your code - with <code>R.id.rs_filename</code>.</dd> - - <dt><code>HelloWorldView</code> class</dt> - - <dd> - This class represents the Surface View that the Renderscript graphics are drawn on. It does - some administrative tasks in the <code>ensureRenderScript()</code> method that sets up the - Renderscript system. This method creates a {@link android.renderscript.RenderScriptGL} - object, which represents the context of the Renderscript and creates a default surface to - draw on (you can set the surface properties such as alpha and bit depth in the {@link - android.renderscript.RenderScriptGL.SurfaceConfig} class ). When a {@link - android.renderscript.RenderScriptGL} is instantiated, this class calls the - <code>HelloRS</code> class and creates the instance of the actual Renderscript graphics - renderer. - <pre> -// Renderscipt context -private RenderScriptGL mRS; -// Script that does the rendering -private HelloWorldRS mRender; - - private void ensureRenderScript() { - if (mRS == null) { - // Initialize Renderscript with desired surface characteristics. - // In this case, just use the defaults - RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); - mRS = createRenderScriptGL(sc); - - // Create an instance of the Renderscript that does the rendering - mRender = new HelloWorldRS(); - mRender.init(mRS, getResources()); - } - } -</pre> - - <p>This class also handles the important lifecycle events and relays touch events to the - Renderscript renderer. When a user touches the screen, it calls the renderer, - <code>HelloWorldRS</code> and asks it to draw the text on the screen at the new location.</p> - <pre> -public boolean onTouchEvent(MotionEvent ev) { - // Pass touch events from the system to the rendering script - if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mRender.onActionDown((int)ev.getX(), (int)ev.getY()); - return true; - } - return false; -} -</pre> - </dd> - - <dt><code>HelloWorldRS</code></dt> - - <dd> - This class represents the Renderscript renderer for the <code>HelloWorldView</code> Surface - View. It interacts with the native Renderscript code that is defined in - <code>helloworld.rs</code> through the interfaces exposed by <code>ScriptC_helloworld</code>. - To be able to call the native code, it creates an instance of the Renderscript reflected - class, <code>ScriptC_helloworld</code>. The reflected Renderscript object binds the - Renderscript bytecode (<code>R.raw.helloworld</code>) and the Renderscript context, {@link - android.renderscript.RenderScriptGL}, so the context knows to use the right Renderscript to - render its surface. - <pre> -private Resources mRes; -private RenderScriptGL mRS; -private ScriptC_helloworld mScript; - -private void initRS() { - mScript = new ScriptC_helloworld(mRS, mRes, R.raw.helloworld); - mRS.bindRootScript(mScript); -} -</pre> - </dd> - </dl>
\ No newline at end of file diff --git a/docs/html/guide/topics/renderscript/compute.jd b/docs/html/guide/topics/renderscript/compute.jd new file mode 100644 index 000000000000..e4c22830fc32 --- /dev/null +++ b/docs/html/guide/topics/renderscript/compute.jd @@ -0,0 +1,38 @@ +page.title=Compute +parent.title=RenderScript +parent.link=index.html +@jd:body + + <div id="qv-wrapper"> + <div id="qv"> + + <h2>Related Samples</h2> + + <ol> + <li><a href="{@docRoot}resources/samples/RenderScript/HelloCompute/index.html">Hello + Compute</a></li> + <li><a href="{@docRoot}resources/samples/RenderScript/Balls/index.html">Balls</a></li> + </ol> + </div> + </div> + + <p>RenderScript exposes a set of compute APIs that you can use to do intensive computational operations. + You can use the compute APIs in the context of a graphics RenderScript such as calculating the + transformation of many geometric objects in a scene. You can also create a standalone compute RenderScript that does not + draw anything to the screen such as bitmap image processing for a photo editor application. + The RenderScript compute APIs are mainly defined in the <code>rs_cl.rsh</code> header</p> + + <p>Compute RenderScripts are simpler to setup and implement as there is no graphics rendering involved. + You can offload computational aspects of your application to RenderScript by creating a native RenderScript + file (.rs) and using the generated reflected layer class to call functions in the <code>.rs</code> file. + + <p>See the <a href="{@docRoot}resources/samples/RenderScript/HelloCompute/index.html">HelloCompute</a> + sample in the Android SDK for more + information on how to create a simple compute RenderScript.</p> + <p> + See the <a href="{@docRoot}resources/samples/RenderScript/Balls/index.html">Balls</a> + sample in the Android SDK for more + information on how to create a compute RenderScript that is used in a graphics RenderScript. + The compute RenderScript is contained in + <a href="{@docRoot}resources/samples/RenderScript/Balls/src/com/example/android/rs/balls/ball_physics.html">balls_physics.rs</a>. + </p>
\ No newline at end of file diff --git a/docs/html/guide/topics/renderscript/graphics.jd b/docs/html/guide/topics/renderscript/graphics.jd new file mode 100644 index 000000000000..d8be85f124b1 --- /dev/null +++ b/docs/html/guide/topics/renderscript/graphics.jd @@ -0,0 +1,619 @@ +page.title=3D Graphics +parent.title=RenderScript +parent.link=index.html +@jd:body + + <div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + + <ol> + <li> + <a href="#developing">Developing a RenderScript application</a> + + <ol> + <li><a href="#hello-graphics">The Hello Graphics application</a></li> + </ol> + </li> + </ol> + + <h2>Related Samples</h2> + + <ol> + <li><a href="{@docRoot}resources/samples/RenderScript/Balls/index.html">Balls</a></li> + + <li><a href= + "{@docRoot}resources/samples/Renderscript/Fountain/index.html">Fountain</a></li> + + <li><a href="{@docRoot}resources/samples/RenderScript/HelloWorld/index.html">Hello + World</a></li> + + <li><a href="{@docRoot}resources/samples/RenderScript/Samples/index.html">Samples</a></li> + </ol> + </div> + </div> + + <p>RenderScript provides a number of graphics APIs for 3D rendering, both at the Android + framework level as well as at the native level. For instance, the Android framework APIs let you + create meshes and define shaders to customize the graphical rendering pipeline. The native + RenderScript graphics APIs lets you draw the actual meshes to render your scene. In general, you + will need to be familiar with APIs to appropriately render 3D graphics on an Android-powered + device.</p> + + <h2>Creating a Graphics RenderScript</h2> + + <p>Because of the various layers of code when writing a RenderScript application, it is useful to + create the following files for a scene that you want to render:</p> + + <ul> + <li>The native RenderScript <code>.rs</code> file. This file contains the logic to do the + graphics rendering.</li> + + <li>The RenderScript entry point class that allows your view to interact with the code defined + in the <code>.rs</code> file. This class contains a RenderScript object(instance of + <code>ScriptC_<em>renderscript_file</em></code>), which allows your Android framework code to + call the native RenderScript code. This class also creates the {@link + android.renderscript.RenderScriptGL} context object, which contains the current rendering state + of the RenderScript such as programs (vertex and fragment shaders, for example) that you want + to define and bind to the graphics pipeline. The context object attaches to the RenderScript + object (instance of <code><em>ScriptC_renderscript_file</em></code>) that does the rendering. + Our example names this class <code>HelloWorldRS</code>.</li> + + <li>Create a class that extends {@link android.renderscript.RSSurfaceView} to provide a surface + to render on. If you want to implement callbacks from events inherited from {@link + android.view.View}, such as {@link android.view.View#onTouchEvent onTouchEvent()} and {@link + android.view.View#onKeyDown onKeyDown()}, do so in this class as well.</li> + + <li>Create a class that is the main Activity class, like you would with any Android + application. This class sets your {@link android.renderscript.RSSurfaceView} as the content + view for this Activity.</li> + </ul> + + <p>The following sections describe how to implement these three classes by using the HelloWorld + RenderScript sample that is provided in the SDK as a guide (some code has been modified from its + original form for simplicity).</p> + + <h3>Creating the native RenderScript file</h3> + + <p>Your native RenderScript code resides in a <code>.rs</code> file in the + <code><project_root>/src/</code> directory. You can also define <code>.rsh</code> header + files. This code contains the logic to render your graphics and declares all necessary variables + and pointers. Every graphics <code>.rs</code> file generally contains the following items:</p> + + <ul> + <li>A pragma (<code>#pragma rs java_package_name(<em>package.name</em>)</code>) that declares + the package name of the <code>.java</code> reflection of this RenderScript.</li> + + <li>A pragma (<code>#pragma version(1)</code>) that declares the version of RenderScript that + you are using (1 is the only value for now).</li> + + <li>A <code>#include</code> of the rs_graphics.rsh header file.</li> + + <li>A <code>root()</code> function. This is the main worker function for your RenderScript and + calls RenderScript graphics APIs to draw meshes to the surface. This function is called every + time a frame refresh occurs, which is specified as its return value. A <code>0</code> specified + for the return value says to only render the frame when a property of the scene that you are + rendering changes. A non-zero positive integer specifies the refresh rate of the frame in + milliseconds. + + <p class="note"><strong>Note:</strong> The RenderScript runtime makes its best effort to + refresh the frame at the specified rate. For example, if you are creating a live wallpaper + and set the return value to 50, the runtime renders the wallpaper at 20fps if it has just + enough or more resources to do so, and renders as fast as it can if it does not.</p> + + <p>For more + information on using the RenderScript graphics functions, see <a href= + "using-graphics-api">Using the Graphics APIs</a>.</p> + </li> + + <li>An <code>init()</code> function. This allows you to do any initialization of your + RenderScript before the <code>root()</code> function runs, such as initializing variables. This + function runs once and is called automatically when the RenderScript starts, before anything + else in your RenderScript. Creating this function is optional.</li> + + <li>Any variables, pointers, and structures that you wish to use in your RenderScript code (can + be declared in <code>.rsh</code> files if desired)</li> + </ul> + + <p>The following code shows how the <code>helloworld.rs</code> file is implemented:</p> + <pre> +#pragma version(1) + +// Tell which java package name the reflected files should belong to +#pragma rs java_package_name(com.android.rs.helloworld) + +// Built-in header with graphics APIs +#include "rs_graphics.rsh" + +// gTouchX and gTouchY are variables that are reflected for use +// by the Android framework API. This RenderScript uses them to be notified of touch events. +int gTouchX; +int gTouchY; + +// This is invoked automatically when the script is created and initializes the variables +// in the Android framework layer as well. +void init() { + gTouchX = 50.0f; + gTouchY = 50.0f; +} + +int root(int launchID) { + + // Clear the background color + rsgClearColor(0.0f, 0.0f, 0.0f, 0.0f); + // Tell the runtime what the font color should be + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + // Introuduce ourselves to the world by drawing a greeting + // at the position user touched on the screen + rsgDrawText("Hello World!", gTouchX, gTouchY); + + // Return value tells RS roughly how often to redraw + // in this case 20 ms + return 20; +} +</pre> + + <h3>Creating the RenderScript entry point class</h3> + + <p>When you create a RenderScript (<code>.rs</code>) file, it is helpful to create a + corresponding Android framework class that is an entry point into the <code>.rs</code> file. In + this entry point class, you create a RenderScript object by instantiating a + <code>ScriptC_<em>rs_filename</em></code> and binding it to the RenderScript context. The + RenderScript object is attached to the RenderScript bytecode, which is platform-independent and + gets compiled on the device when the RenderScript application runs. Both the + <code>ScriptC_<em>rs_filename</em></code> class and bytecode is generated by the Android build + tools and is packaged with the <code>.apk</code> file. The bytecode file is located in the + <code><project_root>/res/raw/</code> directory and is named <code>rs_filename.bc</code>. + You refer to the bytecode as a resource (<code>R.raw.<em>rs_filename</em></code>). when creating + the RenderScript object..</p> + + <p>You then bind the RenderScript object to the RenderScript context, so that the surface view + knows what code to use to render graphics. The following code shows how the + <code>HelloWorldRS</code> class is implemented:</p> + <pre> +package com.android.rs.helloworld; + +import android.content.res.Resources; +import android.renderscript.*; + +public class HelloWorldRS { + //context and resources are obtained from RSSurfaceView, which calls init() + private Resources mRes; + private RenderScriptGL mRS; + + //Declare the RenderScript object + private ScriptC_helloworld mScript; + + public HelloWorldRS() { + } + + /** + * This provides us with the RenderScript context and resources + * that allow us to create the RenderScript object + */ + public void init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + initRS(); + } + /** + * Calls native RenderScript functions (set_gTouchX and set_gTouchY) + * through the reflected layer class ScriptC_helloworld to pass in + * touch point data. + */ + public void onActionDown(int x, int y) { + mScript.set_gTouchX(x); + mScript.set_gTouchY(y); + } + /** + * Binds the RenderScript object to the RenderScript context + */ + private void initRS() { + //create the RenderScript object + mScript = new ScriptC_helloworld(mRS, mRes, R.raw.helloworld); + //bind the RenderScript object to the RenderScript context + mRS.bindRootScript(mScript); + } +} + +</pre> + + <h3>Creating the surface view</h3> + + <p>To create a surface view to render graphics on, create a class that extends {@link + android.renderscript.RSSurfaceView}. This class also creates a RenderScript context object + ({@link android.renderscript.RenderScriptGL} and passes it to the Rendscript entry point class to + bind the two. The following code shows how the <code>HelloWorldView</code> class is + implemented:</p> + <pre> +package com.android.rs.helloworld; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScriptGL; +import android.content.Context; +import android.view.MotionEvent; + +public class HelloWorldView extends RSSurfaceView { + // RenderScript context + private RenderScriptGL mRS; + // RenderScript entry point object that does the rendering + private HelloWorldRS mRender; + + public HelloWorldView(Context context) { + super(context); + initRS(); + } + + private void initRS() { + if (mRS == null) { + // Initialize RenderScript with default surface characteristics. + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + //Create the RenderScript context + mRS = createRenderScriptGL(sc); + // Create an instance of the RenderScript entry point class + mRender = new HelloWorldRS(); + // Call the entry point class to bind it to this context + mRender.init(mRS, getResources()); + } + } + + /** + * Rebind everything when the window becomes attached + */ + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + initRS(); + } + + /** + * Stop rendering when window becomes detached + */ + protected void onDetachedFromWindow() { + // Handle the system event and clean up + mRender = null; + if (mRS != null) { + mRS = null; + destroyRenderScriptGL(); + } + } + + /** + * Use callbacks to relay data to RenderScript entry point class + */ + public boolean onTouchEvent(MotionEvent ev) { + // Pass touch events from the system to the rendering script + if (ev.getAction() == MotionEvent.ACTION_DOWN) { + mRender.onActionDown((int)ev.getX(), (int)ev.getY()); + return true; + } + + return false; + } +} + +</pre> + + <h3>Creating the Activity</h3> + + <p>Applications that use RenderScript still adhere to activity lifecyle, and are part of the same + view hierarchy as traditional Android applications, which is handled by the Android VM. This + Activity class sets its view to be the {@link android.renderscript.RSSurfaceView} and handles + lifecycle callback events appropriately. The following code shows how the <code>HelloWorld</code> + class is implemented:</p> + <pre> +public class HelloWorldActivity extends Activity { + + //Custom view to use with RenderScript + private HelloWorldView view; + + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + // Create surface view and set it as the content of our Activity + mView = new HelloWorldView(this); + setContentView(view); + } + + protected void onResume() { + // Ideally an app should implement onResume() and onPause() + // to take appropriate action when the activity loses focus + super.onResume(); + view.resume(); + } + + protected void onPause() { + // Ideally an app should implement onResume() and onPause() + // to take appropriate action when the activity loses focus + super.onPause(); + view.pause(); + } +} +</pre> + + <h2>Drawing</h2> + + <h3>Drawing using the rsgDraw functions</h3> + + <p>The native RenderScript APIs provide a few convenient functions to easily draw a polygon to + the screen. You call these in your <code>root()</code> function to have them render to the + surface view. These functions are available for simple drawing and should not be used for complex + graphics rendering:</p> + + <ul> + <li><code>rsgDrawRect()</code>: Sets up a mesh and draws a rectangle to the screen. It uses the + top left vertex and bottom right vertex of the rectangle to draw.</li> + + <li><code>rsgDrawQuad()</code>: Sets up a mesh and draws a quadrilateral to the screen.</li> + + <li><code>rsgDrawQuadTexCoords()</code>: Sets up a mesh and draws a textured quadrilateral to + the screen.</li> + </ul> + + <h3>Drawing with a mesh</h3> + + <p>When you want to draw complex shapes and textures to the screen, instantiate a {@link + android.renderscript.Mesh} and draw it to the screen with <code>rsgDrawMesh()</code>. A {@link + android.renderscript.Mesh} is a collection of allocations that represent vertex data (positions, + normals, texture coordinates) and index data such as triangles and lines. You can build a Mesh in + three different ways:</p> + + <ul> + <li>Build the mesh with the {@link android.renderscript.Mesh.TriangleMeshBuilder} class, which + allows you to specify a set of vertices and indices for each triangle that you want to draw. + The downside of doing it this way is there is no way to specify the vertices in your native + RenderScript code.</li> + + <li>Build the mesh using an {@link android.renderscript.Allocation} or a set of {@link + android.renderscript.Allocation}s with the {@link android.renderscript.Mesh.AllocationBuilder} + class. This allows you to build a mesh with vertices already stored in memory, which allows you + to set the vertices in native or Android code.</li> + + <li>Build the mesh with the {@link android.renderscript.Mesh.Builder} class. This is a + convenience method for when you know what data types you want to use to build your mesh, but + don't want to make separate memory allocations like with {@link + android.renderscript.Mesh.AllocationBuilder}. You can specify the types that you want and this + mesh builder automatically creates the memory allocations for you.</li> + </ul> + + <p>To create a mesh using the {@link android.renderscript.Mesh.TriangleMeshBuilder}, you need to + supply it with a set of vertices and the indices for the vertices that comprise the triangle. For + example, the following code specifies three vertices, which are added to an internal array, + indexed in the order they were added. The call to {@link + android.renderscript.Mesh.TriangleMeshBuilder#addTriangle addTriangle()} draws the triangle with + vertex 0, 1, and 2 (the vertices are drawn counter-clockwise).</p> + <pre> +int float2VtxSize = 2; +Mesh.TriangleMeshBuilder triangle = new Mesh.TriangleMeshBuilder(renderscriptGL, +float2VtxSize, Mesh.TriangleMeshBuilder.COLOR); +triangles.addVertex(300.f, 300.f); +triangles.addVertex(150.f, 450.f); +triangles.addVertex(450.f, 450.f); +triangles.addTriangle(0 , 1, 2); +Mesh smP = triangle.create(true); +script.set_mesh(smP); +</pre> + + <p>To draw a mesh using the {@link android.renderscript.Mesh.AllocationBuilder}, you need to + supply it with one or more allocations that contain the vertex data:</p> + <pre> +Allocation vertices; + +... +Mesh.AllocationBuilder triangle = new Mesh.AllocationBuilder(mRS); +smb.addVertexAllocation(vertices.getAllocation()); +smb.addIndexSetType(Mesh.Primitive.TRIANGLE); +Mesh smP = smb.create(); +script.set_mesh(smP); +</pre> + + <p>In your native RenderScript code, draw the built mesh to the screen:</p> + <pre> +rs_mesh mesh; +... + +int root(){ +... +rsgDrawMesh(mesh); +... +return 0; //specify a non zero, positive integer to specify the frame refresh. + //0 refreshes the frame only when the mesh changes. +} +</pre> + + <h2 id="shaders">Shaders</h2> + + <p>You can attach four program objects to the {@link android.renderscript.RenderScriptGL} context + to customize the rendering pipeline. For example, you can create vertex and fragment shaders in + GLSL or build a raster program object with provided methods without writing GLSL code. The four + program objects mirror a traditional graphical rendering pipeline:</p> + + <table> + <tr> + <th>Android Object Type</th> + + <th>RenderScript Native Type</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramVertex}</td> + + <td>rs_program_vertex</td> + + <td> + <p>The RenderScript vertex program, also known as a vertex shader, describes the stage in + the graphics pipeline responsible for manipulating geometric data in a user-defined way. + The object is constructed by providing RenderScript with the following data:</p> + + <ul> + <li>An Element describing its varying inputs or attributes</li> + + <li>GLSL shader string that defines the body of the program</li> + + <li>a Type that describes the layout of an Allocation containing constant or uniform + inputs</li> + </ul> + + <p>Once the program is created, bind it to the {@link android.renderscript.RenderScriptGL} + graphics context by calling {@link android.renderscript.RenderScriptGL#bindProgramVertex + bindProgramVertex()}. It is then used for all subsequent draw calls until you bind a new + program. If the program has constant inputs, the user needs to bind an allocation + containing those inputs. The allocation's type must match the one provided during creation. + The RenderScript library then does all the necessary plumbing to send those constants to + the graphics hardware. Varying inputs to the shader, such as position, normal, and texture + coordinates are matched by name between the input Element and the Mesh object being drawn. + The signatures don't have to be exact or in any strict order. As long as the input name in + the shader matches a channel name and size available on the mesh, the run-time would take + care of connecting the two. Unlike OpenGL, there is no need to link the vertex and fragment + programs.</p> + + <p>To bind shader constructs to the Program, declare a struct containing the necessary + shader constants in your native RenderScript code. This struct is generated into a + reflected class that you can use as a constant input element during the Program's creation. + It is an easy way to create an instance of this struct as an allocation. You would then + bind this Allocation to the Program and the RenderScript system sends the data that is + contained in the struct to the hardware when necessary. To update shader constants, you + change the values in the Allocation and notify the native RenderScript code of the + change.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramFragment}</td> + + <td>rs_program_fragment</td> + + <td> + <p>The RenderScript fragment program, also known as the fragment shader, is responsible for + manipulating pixel data in a user-defined way. It's constructed from a GLSL shader string + containing the program body, textures inputs, and a Type object describing the constants + used by the program. Like the vertex programs, when an allocation with constant input + values is bound to the shader, its values are sent to the graphics program automatically. + Note that the values inside the allocation are not explicitly tracked. If they change + between two draw calls using the same program object, notify the runtime of that change by + calling rsgAllocationSyncAll so it could send the new values to hardware. Communication + between the vertex and fragment programs is handled internally in the GLSL code. For + example, if the fragment program is expecting a varying input called varTex0, the GLSL code + inside the program vertex must provide it.</p> + + <p>To bind shader constants to this program, declare a struct containing the necessary + shader constants in your native RenderScript code. This struct is generated into a + reflected class that you can use as a constant input element during the Program's creation. + It is an easy way to create an instance of this struct as an allocation. You would then + bind this Allocation to the Program and the RenderScript system sends the data that is + contained in the struct to the hardware when necessary. To update shader constants, you + change the values in the Allocation and notify the native RenderScript code of the + change.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramStore}</td> + + <td>rs_program_store</td> + + <td>The RenderScript ProgramStore contains a set of parameters that control how the graphics + hardware writes to the framebuffer. It could be used to enable and disable depth writes and + testing, setup various blending modes for effects like transparency and define write masks + for color components.</td> + </tr> + + <tr> + <td>{@link android.renderscript.ProgramRaster}</td> + + <td>rs_program_raster</td> + + <td>Program raster is primarily used to specify whether point sprites are enabled and to + control the culling mode. By default back faces are culled.</td> + </tr> + </table> + + <p>The following example defines a vertex shader in GLSL and binds it to the RenderScript:</p> + <pre> + private RenderScriptGL glRenderer; //rendering context + private ScriptField_Point mPoints; //vertices + private ScriptField_VpConsts mVpConsts; //shader constants + + ... + + ProgramVertex.Builder sb = new ProgramVertex.Builder(glRenderer); + String t = "varying vec4 varColor;\n" + + "void main() {\n" + + " vec4 pos = vec4(0.0, 0.0, 0.0, 1.0);\n" + + " pos.xy = ATTRIB_position;\n" + + " gl_Position = UNI_MVP * pos;\n" + + " varColor = vec4(1.0, 1.0, 1.0, 1.0);\n" + + " gl_PointSize = ATTRIB_size;\n" + + "}\n"; + sb.setShader(t); + sb.addConstant(mVpConsts.getType()); + sb.addInput(mPoints.getElement()); + ProgramVertex pvs = sb.create(); + pvs.bindConstants(mVpConsts.getAllocation(), 0); + glRenderer.bindProgramVertex(pvs); + + +</pre> + + <p>The <a href= + "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html"> + RsRenderStatesRS</a> sample has many examples on how to create a shader without writing GLSL.</p> + + <h3>Shader bindings</h3> + + <p>You can also set four pragmas that control the shaders' default bindings to the {@link + android.renderscript.RenderScriptGL} context when the script is executing:</p> + + <ul> + <li>stateVertex</li> + + <li>stateFragment</li> + + <li>stateRaster</li> + + <li>stateStore</li> + </ul> + + <p>The possible values for each pragma are <code>parent</code> or <code>default</code>. Using + <code>default</code> binds the shaders to the graphical context with the system defaults. The + default shader is defined below:</p> + <pre> +("varying vec4 varColor;\n"); +("varying vec2 varTex0;\n"); +("void main() {\n"); +(" gl_Position = UNI_MVP * ATTRIB_position;\n"); +(" gl_PointSize = 1.0;\n"); +(" varColor = ATTRIB_color;\n"); +(" varTex0 = ATTRIB_texture0;\n"); +("}\n"); +</pre> + + <p>Using <code>parent</code> binds the shaders in the same manner as it is bound in the calling + script. If this is the root script, the parent state is taken from the bind points that are set + by the {@link android.renderscript.RenderScriptGL} bind methods.</p> + + <p>For example, you can define this at the top of your native graphics RenderScript code to have + the Vertex and Store shaders inherent the bind properties from their parent scripts:</p> + <pre> +#pragma stateVertex(parent) +#pragma stateStore(parent) +</pre> + + <h3>Defining a sampler</h3> + + <p>A {@link android.renderscript.Sampler} object defines how data is extracted from textures. + Samplers are bound to Program objects (currently only a Fragment Program) alongside the texture + whose sampling they control. These objects are used to specify such things as edge clamping + behavior, whether mip-maps are used, and the amount of anisotropy required. There might be + situations where hardware does not support the desired behavior of the sampler. In these cases, + the runtime attempts to provide the closest possible approximation. For example, the user + requested 16x anisotropy, but only 8x was set because it's the best available on the + hardware.</p> + + <p>The <a href= + "{@docRoot}resources/samples/RenderScript/MiscSamples/src/com/example/android/rs/miscsamples/RsRenderStatesRS.html"> + RsRenderStatesRS</a> sample has many examples on how to create a sampler and bind it to a + Fragment program.</p> + +</body> +</html> diff --git a/docs/html/guide/topics/renderscript/index.jd b/docs/html/guide/topics/renderscript/index.jd new file mode 100644 index 000000000000..eb773109e427 --- /dev/null +++ b/docs/html/guide/topics/renderscript/index.jd @@ -0,0 +1,640 @@ +page.title=RenderScript +@jd:body + + <div id="qv-wrapper"> + <div id="qv"> + <h2>In this document</h2> + + <ol> + <li><a href="#overview">RenderScript System Overview</a></li> + <li> + <ol> + <li><a href="#native">Native RenderScript layer</a></li> + + <li><a href="#reflected">Reflected layer</a></li> + + <li><a href="#framework">Android framework layer</a></li> + </ol> + </li> + + <li> + <a href="#mem-allocation">Memory Allocation APIs</a> + </li> + <li> + <a href="#dynamic">Dynamic Memory Allocations</a> + <ol> + <li><a href="#pointers">Declaring pointers</a></li> + + <li><a href="#struct-pointer-reflection">How pointers are reflected</a></li> + + <li><a href="#binding">Allocating and binding memory to the RenderScript</a></li> + + <li><a href="#read-write-dynamic">Reading and writing to memory</a></li> + + </ol> + </li> + <li> + <a href="#static">Static Memory Allocations</a> + </li> + </ol> + </div> + </div> + + <p>RenderScript offers a high performance 3D graphics rendering and compute API at the native + level, which you write in the C (C99 standard). The main advantages of RenderScript are:</p> + <ul> + <li>Portability: RenderScript is designed to run on many types of devices with different CPU + and GPU architectures. It supports all of these architectures without having to target each + device, because the code is compiled and cached on the device at runtime.</li> + + <li>Performance: RenderScript provides similar performance to OpenGL with the NDK while + offering the portability of the OpenGL APIs provided by the Android framework ({@link + android.opengl}). In addition, it also offers a high performance compute API that is not + offered by OpenGL.</li> + + <li>Usability: RenderScript simplifies development when possible, such as eliminating JNI glue code + and simplifying mesh setup.</li> + </ul> + + <p>The main disadvantages are:</p> + + <ul> + <li>Development complexity: RenderScript introduces a new set of APIs that you have to learn. + RenderScript also handles memory differently compared to OpenGL with the Android framework APIs + or NDK.</li> + + <li>Debugging visibility: RenderScript can potentially execute (planned feature for later releases) + on processors other than the main CPU (such as the GPU), so if this occurs, debugging becomes more difficult. + </li> + + <li>Less features: RenderScript does not provide as many features as OpenGL such as all the compressed + texture formats or GL extensions.</li> + </ul> + + <p>You need to consider all of the aspects of RenderScript before deciding when to use it. The following list describes + general guidelines on when to use OpenGL (framework APIs or NDK) or RenderScript:</p> + <ul> + <li>If you are doing simple graphics rendering and performance is not critical, you probably want to use the + Android framework OpenGL APIs, which still provide adequate performance, to eliminate the added coding and debugging complexity of + RenderScript.</li> + + <li>If you want the most flexibility and features while maintaining relatively good debugging + support, you probably want to use OpenGL and the NDK. Applications that require this are high end + or complicated games, for example.</li> + + <li>If you want a solution that is portable, has good performance, + and you don't need the full feature set of OpenGL, RenderScript is a good solution. If you also + need a high performance compute language, then RenderScript offers that as well. + Good candidates for RenderScript are graphics intensive UIs that require 3D rendering, live wallpapers, + or applications that require intensive mathematical computation.</li> + </ul> + + <p>For an example of RenderScript in action, install the RenderScript sample applications that + are shipped with the SDK in <code><sdk_root>/samples/android-11/RenderScript</code>. + You can also see a typical use of RenderScript with the 3D carousel view in the Android 3.x + versions of Google Books and YouTube.</p> + + <h2 id="overview">RenderScript System Overview</h2> + + <p>The RenderScript system adopts a control and slave architecture where the low-level native + code is controlled by the higher level Android system that runs in a virtual machine (VM). The + Android VM still retains all control of memory and lifecycle management and calls the native + RenderScript code when necessary. The native code is compiled to intermediate bytecode (LLVM) and + packaged inside your application's <code>.apk</code> file. On the device, the bytecode is + compiled (just-in-time) to machine code that is further optimized for the device that it is + running on. The compiled code on the device is cached, so subsequent uses of the RenderScript + enabled application do not recompile the intermediate code. RenderScript has three layers of code + to enable communication between the native and Android framework code:</p> + + <ul> + <li>The native RenderScript layer does the intensive computation or graphics rendering. You + define your native code in <code>.rs</code> and <code>.rsh</code> files.</li> + + <li>The reflected layer is a set of classes that are reflected from the native code. It is basically + a wrapper around the native code that allows the Android framework to interact with native RenderScripts. + The Android build tools automatically generate the classes for this layer during + the build process and eliminates the need to write JNI glue code, like with the NDK.</li> + + <li>The Android framework layer is comprised of the Android framework + APIs, which include the {@link android.renderscript} package. This layer gives high level commands + like, "rotate the view" or "filter the bitmap", by calling the reflected layer, which in turn calls + the native layer. </li> + </ul> + + <h3 id="native">Native RenderScript layer</h3> + + <p>The native RenderScript layer consists of your RenderScript code, which is compiled and + executed in a compact and well defined runtime. Your RenderScript code has access to a limited + amount of functions because it cannot access the NDK or standard C functions, since they must be guaranteed to + run on a standard CPU. The RenderScript runtime was designed to run on different types of processors, + which may not be the CPU, so it cannot guarantee support for standard C libraries. What + RenderScript does offer is an API that supports intensive computation and graphics rendering with a collection of math + and graphics APIs.</p> + + <p>Some key features of the native RenderScript libraries include:</p> + + <ul> + <li>A large collection of math functions with both scalar and vector typed overloaded versions + of many common routines. Operations such as adding, multiplying, dot product, and cross product + are available.</li> + + <li>Conversion routines for primitive data types and vectors, matrix routines, date and time + routines, and graphics routines.</li> + + <li>Logging functions</li> + + <li>Graphics rendering functions</li> + + <li>Memory allocation request features</li> + + <li>Data types and structures to support the RenderScript system such as Vector types for + defining two-, three-, or four-vectors.</li> + </ul> + + <p>The <a href="{@docRoot}guide/topics/renderscript/rs-api/files.html">RenderScript header files</a> + and LLVM front-end libraries are located in the <code>include</code> and + <code>clang-include</code> directories in the + <code><sdk_root>/platforms/android-11/renderscript</code> directory of the Android SDK. The + headers are automatically included for you, except for the RenderScript graphics specific header file, which + you can include as follows:</p> + <pre> +#include "rs_graphics.rsh" +</pre> + + <h3 id="reflected">Reflected layer</h3> + + <p>The reflected layer is a set of classes that the Android build tools generate to allow access + to the native RenderScript code from the Android VM. This layer defines entry points for + RenderScript functions and variables, so that you can interact with them with the Android + framework. This layer also provides methods and constructors that allow you to allocate memory + for pointers that are defined in your RenderScript code. The following list describes the major + components that are reflected:</p> + + <ul> + <li>Every <code>.rs</code> file that you create is generated into a class named + <code>ScriptC_<em>renderscript_filename</em></code> of type {@link + android.renderscript.ScriptC}. This is the <code>.java</code> version of your <code>.rs</code> + file, which you can call from the Android framework. This class contains the following + reflections: + + <ul> + <li>Non-static functions in your <code>.rs</code> file.</li> + + <li>Non-static, global RenderScript variables. Accessor methods are generated for each + variable, so you can read and write the natively declared variables from the Android + framework. The <code>get</code> method comes with a one-way communication restriction. The + last value that is set from the Android framework is always returned during a call to a + <code>get</code> method. If the native RenderScript code changes the value, the change does + not propagate back to the Android framework layer. + If the global variables are initialized + in the native RenderScript code, those values are used to initialize the corresponding + values in the Android framework layer. If global variables are marked as + <code>const</code>, then a <code>set</code> method is not generated.</li> + <li>Global pointers generate a special method named <code>bind_<em>pointer_name</em></code> + instead of a <code>set()</code> method. This method allows you to bind the memory that is + allocated in the Android VM for the pointer to the native RenderScript (you cannot allocate + memory in your <code>.rs</code> file). You can read and write to this memory from both the + Android framework and RenderScript code. For more information, see <a href="mem-mgmt">Working + with Memory and Data</a></li> + </ul> + </li> + + <li>A <code>struct</code> is reflected into its own class named + <code>ScriptField_<em>struct_name</em></code>, which extends {@link + android.renderscript.Script.FieldBase}. This class represents an array of the + <code>struct</code>, which allows you to allocate memory for one or more instances of this + <code>struct</code>.</li> + </ul> + + <h3 id="framework">Android framework layer</h3> + + <p>The Android framework layer consists of the usual Android framework APIs, which include the + RenderScript APIs in {@link android.renderscript}. This layer handles things such as the + Activity lifecycle and memory management of your application. It issues high level commands to + the native RenderScript code through the reflected layer and receives events from the user such + as touch and input events and relays them to your RenderScript code, if needed. + </p> + + <h2 id="mem-allocation">Memory Allocation APIs</h2> + + <p>Before you begin writing your first RenderScript application, you must understand how + memory is allocated for your RenderScript code and how data is shared between the native and VM + spaces. RenderScript allows you to access allocated memory in both the native layer + and Android system layer. All dynamic and static memory is allocated by the Android VM. + The Android VM also does reference counting and garbage collection for you. + You can also explicitly free memory that you no longer need.</p> + + <p class="note"><strong>Note:</strong> To declare temporary memory in your native RenderScript + code without allocating it in the Android VM, you can still do things like instantiate a scratch + buffer using an array.</p> + + <p>The following classes support the memory management features of RenderScript in the Android + VM. You normally do not need to work with these classes directly, because the reflected layer + classes provide constructors and methods that set up the memory allocation for you. There are + some situations where you would want to use these classes directly to allocate memory on your + own, such as loading a bitmap from a resource or when you want to allocate memory for pointers to + primitive types.</p> + + <table id="mem-mgmt-table"> + <tr> + <th>Android Object Type</th> + + <th>Description</th> + </tr> + + <tr> + <td>{@link android.renderscript.Element}</td> + + <td> + <p>An element represents one cell of a memory allocation and can have two forms: Basic or + Complex.</p> + + <p>A basic element contains a single component of data of any valid RenderScript data type. + Examples of basic element data types include a single float value, a float4 vector, or a + single RGB-565 color.</p> + + <p>Complex elements contain a list of basic elements and are created from + <code>struct</code>s that you declare in your RenderScript code. The most basic primitive + type determines the data alignment of the memory. For example, a float4 vector subelement + is alligned to <code>sizeof(float)</code> and not <code>sizeof(float4)</code>. The ordering + of the elements in memory are the order in which they were added, with each component + aligned as necessary.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.Type}</td> + + <td> + A type is a memory allocation template and consists of an element and one or more + dimensions. It describes the layout of the memory (basically an array of {@link + android.renderscript.Element}s) but does not allocate the memory for the data that it + describes. + + <p>A type consists of five dimensions: X, Y, Z, LOD (level of detail), and Faces (of a cube + map). You can assign the X,Y,Z dimensions to any positive integer value within the + constraints of available memory. A single dimension allocation has an X dimension of + greater than zero while the Y and Z dimensions are zero to indicate not present. For + example, an allocation of x=10, y=1 is considered two dimensional and x=10, y=0 is + considered one dimensional. The LOD and Faces dimensions are booleans to indicate present + or not present.</p> + </td> + </tr> + + <tr> + <td>{@link android.renderscript.Allocation}</td> + + <td> + <p>An allocation provides the memory for applications based on a description of the memory + that is represented by a {@link android.renderscript.Type}. Allocated memory can exist in + many memory spaces concurrently. If memory is modified in one space, you must explicitly + synchronize the memory, so that it is updated in all the other spaces that it exists + in.</p> + + <p>Allocation data is uploaded in one of two primary ways: type checked and type unchecked. + For simple arrays there are <code>copyFrom()</code> functions that take an array from the + Android system and copy it to the native layer memory store. The unchecked variants allow + the Android system to copy over arrays of structures because it does not support + structures. For example, if there is an allocation that is an array of n floats, the data + contained in a float[n] array or a byte[n*4] array can be copied.</p> + </td> + </tr> + </table> + + <h2 id="dynamic">Working with dynamic memory allocations</h2> + + <p>RenderScript has support for pointers, but you must allocate the memory in your Android framework + code. When you declare a global pointer in your <code>.rs</code> file, you allocate memory + through the appropriate reflected layer class and bind that memory to the native + RenderScript layer. You can read and write to this memory from the Android framework layer as well as the + RenderScript layer, which offers you the flexibility to modify variables in the most appropriate + layer. The following sections show you how to work with pointers, allocate memory for them, and + read and write to the memory.</p> + + <h3 id="pointers">Declaring pointers</h3> + + <p>Because RenderScript is written in C99, declaring a pointer is done in a familiar way. You can + declare pointers to a <code>struct</code> or a primitive type, but a <code>struct</code> cannot + contain pointers or nested arrays. The following code declares a <code>struct</code>, a pointer + to that <code>struct</code>, and a pointer of primitive type <code>int32_t</code> in an <code>.rs</code> file:</p> + <pre> +#pragma version(1) +#pragma rs java_package_name(com.example.renderscript) + +... + +typedef struct Point { + float2 point; + } Point_t; + + Point_t *touchPoints; + int32_t *intPointer; + +... +</pre> + +<p>You cannot allocate memory for these pointers in your RenderScript code, but the Android +build tools generate classes for you that allow you to allocate memory in the Android VM for use by +your RenderScript code. These classes also let you read and write to the memory. The next section +describes how these classes are generated through reflection.</p> + + <h3>How pointers are reflected</h3> + + <p>Global variables have a getter and setter method generated. A global pointer generates a + <code>bind_pointerName()</code> method instead of a set() method. This method allows you to bind + the memory that is allocated in the Android VM to the native RenderScript. For example, the two + pointers in the previous section generate the following accessor methods in the <code>ScriptC_<em>rs_filename</em></code> file:</p> + <pre> + + private ScriptField_Point mExportVar_touchPoints; + public void bind_touchPoints(ScriptField_Point v) { + mExportVar_touchPoints = v; + if (v == null) bindAllocation(null, mExportVarIdx_touchPoints); + else bindAllocation(v.getAllocation(), mExportVarIdx_touchPoints); + } + + public ScriptField_Point get_touchPoints() { + return mExportVar_touchPoints; + } + + private Allocation mExportVar_intPointer; + public void bind_intPointer(Allocation v) { + mExportVar_intPointer = v; + if (v == null) bindAllocation(null, mExportVarIdx_intPointer); + else bindAllocation(v, mExportVarIdx_intPointer); + } + + public Allocation get_intPointer() { + return mExportVar_intPointer; + } + +</pre> + + <h3>Allocating and binding memory to the RenderScript</h3> + + <p>When the build tools generate the reflected layer, you can use the appropriate class + (<code>ScriptField_Point</code>, in our example) to allocate memory for a pointer. To do this, + you call the constructor for the {@link android.renderscript.Script.FieldBase} class and specify + the amount of structures that you want to allocate memory for. To allocate memory for a primitive + type pointer, you must build an allocation manually, using the memory management classes + described in <a href="mem-mgmt-table">Table 1</a>. The example below allocates memory for both + the <code>intPointer</code> and <code>touchPoints</code> pointer and binds it to the + RenderScript:</p> + <pre> +private RenderScriptGL glRenderer; +private ScriptC_example script; +private Resources resources; + +public void init(RenderScriptGL rs, Resources res) { + //get the rendering context and resources from the calling method + glRenderer = rs; + resources = res; + + //allocate memory for the struct pointer, calling the constructor + ScriptField_Point touchPoints = new ScriptField_Point(glRenderer, 2); + + //Create an element manually and allocate memory for the int pointer + intPointer = Allocation.createSized(glRenderer, Element.I32(glRenderer), 2); + + //create an instance of the RenderScript, pointing it to the bytecode resource + mScript = new ScriptC_example(glRenderer, resources, R.raw.example); + + // bind the struct and int pointers to the RenderScript + mScript.bind_touchPoints(touchPoints); + script.bind_intPointer(intPointer); + + //bind the RenderScript to the rendering context + glRenderer.bindRootScript(script); +} +</pre> + + <h3>Reading and writing to memory</h3> + + <p>Although you have to allocate memory within the Android VM, you can work with the memory both + in your native RenderScript code and in your Android code. Once memory is bound, the native + RenderScript can read and write to the memory directly. You can also just use the accessor + methods in the reflected classes to access the memory. If you modify memory in the Android + framework, it gets automatically synchronized to the native layer. If you modify memory in the <code>.rs</code> + file, these changes do not get propagated back to the Android framework. + For example, you can modify the struct in your Android code like this:</p> + <pre> +int index = 0; +boolean copyNow = true; +Float2 point = new Float2(0.0f, 0.0f); +touchPoints.set_point(index, point, copyNow); +</pre>then read it in your native RenderScript code like this: + <pre> +rsDebug("Printing out a Point", touchPoints[0].point.x, touchPoints[0].point.y); +</pre> + + <h2>Working with statically allocated memory</h2> + + <p>Non-static, global primitives and structs that you declare in your RenderScript are easier to work with, + because the memory is statically allocated at compile time. Accessor methods to set and get these + variables are generated when the Android build tools generate the reflected layer classes. You + can get and set these variables using the provided accessor methods. + <p class="note"><strong>Note:</strong> The <code>get</code> method comes with a one-way communication restriction. The last value + that is set from the Android framework is always returned during a call to a <code>get</code> + method. If the native RenderScript code changes the value, the change does not propagate back to + the Android framework layer. If the global variables are initialized in the native RenderScript + code, those values are used to initialize the corresponding values in the Android framework + layer. If global variables are marked as <code>const</code>, then a <code>set</code> method is + not generated.</p> + </p> + + <p>For example, if you declare the following primitive in your RenderScript code:</p> + <pre> + uint32_t unsignedInteger = 1; + +</pre> +<p>then the following code is generated in <code>ScriptC_<em>script_name</em>.java</code>:</p> + <pre> + private final static int mExportVarIdx_unsignedInteger = 9; + private long mExportVar_unsignedInteger; + public void set_unsignedInteger(long v) { + mExportVar_unsignedInteger = v; + setVar(mExportVarIdx_unsignedInteger, v); + } + + public long get_unsignedInteger() { + return mExportVar_unsignedInteger; + } +</pre> + + <p class="note"><strong>Note:</strong> The mExportVarIdx_unsignedInteger variable represents the + index of the <code>unsignedInteger</code>'s in an array of statically allocated primitives. You do + not need to work with or be aware of this index.</p> + + <p>For a <code>struct</code>, the Android build tools generate a class named + <code><project_root>/gen/com/example/renderscript/ScriptField_struct_name</code>. This + class represents an array of the <code>struct</code> and allows you to allocate memory for a + specified number of <code>struct</code>s. This class defines:</p> + + <ul> + <li>Overloaded constructors that allow you to allocate memory. The + <code>ScriptField_<em>struct_name</em>(RenderScript rs, int count)</code> constructor allows + you to define the number of structures that you want to allocate memory for with the + <code>count</code> parameter. The <code>ScriptField_<em>struct_name</em>(RenderScript rs, int + count, int usages)</code> constructor defines an extra parameter, <code>usages</code>, that + lets you specify the memory space of this memory allocation. There are four memory space + possibilities: + + <ul> + <li>{@link android.renderscript.Allocation#USAGE_SCRIPT}: Allocates in the script memory + space. This is the default memory space if you do not specify a memory space.</li> + + <li>{@link android.renderscript.Allocation#USAGE_GRAPHICS_TEXTURE}: Allocates in the + texture memory space of the GPU.</li> + + <li>{@link android.renderscript.Allocation#USAGE_GRAPHICS_VERTEX}: Allocates in the vertex + memory space of the GPU.</li> + + <li>{@link android.renderscript.Allocation#USAGE_GRAPHICS_CONSTANTS}: Allocates in the + constants memory space of the GPU that is used by the various program objects.</li> + </ul> + + <p>You can specify one or all of these memory spaces by OR'ing them together. Doing so notifies + the RenderScript runtime that you intend on accessing the data in the specified memory spaces. The following + example allocates memory for a custom data type in both the script and vertex memory spaces:</p> +<pre> +ScriptField_Point touchPoints = new ScriptField_Point(glRenderer, 2, +Allocation.USAGE_SCRIPT | Allocation.USAGE_GRAPHICS_VERTEX); +</pre> + + <p>If you modify the memory in one memory space and want to push the updates to the rest of + the memory spaces, call <code>rsgAllocationSyncAll()</code> in your RenderScript code to + synchronize the memory.</p> + </li> + + <li>A static nested class, <code>Item</code>, allows you to create an instance of the + <code>struct</code>, in the form of an object. This is useful if it makes more sense to work + with the <code>struct</code> in your Android code. When you are done manipulating the object, + you can push the object to the allocated memory by calling <code>set(Item i, int index, boolean + copyNow)</code> and setting the <code>Item</code> to the desired position in the array. The + native RenderScript code automatically has access to the newly written memory. + + <li>Accessor methods to get and set the values of each field in a struct. Each of these + accessor methods have an <code>index</code> parameter to specify the <code>struct</code> in the + array that you want to read or write to. Each setter method also has a <code>copyNow</code> + parameter that specifies whether or not to immediately sync this memory to the native + RenderScript layer. To sync any memory that has not been synced, call <code>copyAll()</code>.</li> + + <li>The createElement() method creates an object that describes the memory layout of the struct.</li> + + <li>resize() works much like a <code>realloc</code>, allowing you to expand previously + allocated memory, maintaining the current values that were previously set.</li> + + <li>copyAll() synchronizes memory that was set on the framework level to the native level. When you call + a set accessor method on a member, there is an optional <code>copyNow</code> boolean parameter that you can specify. Specifying + <code>true</code> synchronizes the memory when you call the method. If you specify false, you can call <code>copyAll()</code> + once, and it synchronizes memory for the all the properties that are not synchronized.</li> + </ul> + + <p>The following example shows the reflected class, <code>ScriptField_Point.java</code> that is + generated from the Point <code>struct</code>.</p> + <pre> +package com.example.renderscript; + +import android.renderscript.*; +import android.content.res.Resources; + + +public class ScriptField_Point extends android.renderscript.Script.FieldBase { + static public class Item { + public static final int sizeof = 8; + + Float2 point; + + Item() { + point = new Float2(); + } + + } + + private Item mItemArray[]; + private FieldPacker mIOBuffer; + public static Element createElement(RenderScript rs) { + Element.Builder eb = new Element.Builder(rs); + eb.add(Element.F32_2(rs), "point"); + return eb.create(); + } + + public ScriptField_Point(RenderScript rs, int count) { + mItemArray = null; + mIOBuffer = null; + mElement = createElement(rs); + init(rs, count); + } + + public ScriptField_Point(RenderScript rs, int count, int usages) { + mItemArray = null; + mIOBuffer = null; + mElement = createElement(rs); + init(rs, count, usages); + } + + private void copyToArray(Item i, int index) { + if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */); + mIOBuffer.reset(index * Item.sizeof); + mIOBuffer.addF32(i.point); + } + + public void set(Item i, int index, boolean copyNow) { + if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */]; + mItemArray[index] = i; + if (copyNow) { + copyToArray(i, index); + mAllocation.setFromFieldPacker(index, mIOBuffer); + } + + } + + public Item get(int index) { + if (mItemArray == null) return null; + return mItemArray[index]; + } + + public void set_point(int index, Float2 v, boolean copyNow) { + if (mIOBuffer == null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */)fnati; + if (mItemArray == null) mItemArray = new Item[getType().getX() /* count */]; + if (mItemArray[index] == null) mItemArray[index] = new Item(); + mItemArray[index].point = v; + if (copyNow) { + mIOBuffer.reset(index * Item.sizeof); + mIOBuffer.addF32(v); + FieldPacker fp = new FieldPacker(8); + fp.addF32(v); + mAllocation.setFromFieldPacker(index, 0, fp); + } + + } + + public Float2 get_point(int index) { + if (mItemArray == null) return null; + return mItemArray[index].point; + } + + public void copyAll() { + for (int ct = 0; ct < mItemArray.length; ct++) copyToArray(mItemArray[ct], ct); + mAllocation.setFromFieldPacker(0, mIOBuffer); + } + + public void resize(int newSize) { + if (mItemArray != null) { + int oldSize = mItemArray.length; + int copySize = Math.min(oldSize, newSize); + if (newSize == oldSize) return; + Item ni[] = new Item[newSize]; + System.arraycopy(mItemArray, 0, ni, 0, copySize); + mItemArray = ni; + } + + mAllocation.resize(newSize); + if (mIOBuffer != null) mIOBuffer = new FieldPacker(Item.sizeof * getType().getX()/* count */); + } + +} +</pre> + +</body> +</html> diff --git a/docs/html/guide/topics/resources/animation-resource.jd b/docs/html/guide/topics/resources/animation-resource.jd index 972dd729cb1d..3df669c06eb7 100644 --- a/docs/html/guide/topics/resources/animation-resource.jd +++ b/docs/html/guide/topics/resources/animation-resource.jd @@ -5,28 +5,348 @@ parent.link=available-resources.html <div id="qv-wrapper"> <div id="qv"> + <h2>In this document</h2> + <ol> + <li><a href="#Property">Property Animation</a></li> + <li><a href="#View">View Animation</a> + <ol> + <li><a href="Tween">Tween animation</li> + <li><a href="Frame">Frame animation</li> + </ol> + </li> + </ol> <h2>See also</h2> <ol> - <li><a href="{@docRoot}guide/topics/graphics/2d-graphics.html#tween-animation">2D -Graphics</a></li> + <li><a href="{@docRoot}guide/topics/graphics/view-animation.html">View Animation</a></li> + <li><a href="{@docRoot}guide/topics/graphics/animation.html">Property Animation</a></li> </ol> </div> </div> <p>An animation resource can define one of two types of animations:</p> + <dl> - <dt><a href="#Tween">Tween Animation</a></dt> - <dd>Creates an animation by performing a series of transformations on a single image. - An {@link android.view.animation.Animation}.</dd> - <dt><a href="#Frame">Frame Animation</a></dt> - <dd>Creates an animation by showing a sequence of images in order. - An {@link android.graphics.drawable.AnimationDrawable}.</dd> + <dt><a href="#Property">Property Animation</a></dt> + <dd>Creates an animation by modifying an object's property values over a set period + of time with an {@link android.animation.Animator}.</dd> + <dt><a href="#View">View Animation</a></dt> + <dd> + <p>There are two types of animations that you can do with the view animation framework:</p> + <ul> + <li><a href="#Tween">Tween animation</a>: Creates an animation by performing a series of transformations on a single image + with an {@link android.view.animation.Animation}</li> + <li><a href="#Frame">Frame animation</a>: or creates an animation by showing a sequence of images in order + with an {@link android.graphics.drawable.AnimationDrawable}.</li> + </ul> + </dd> </dl> +<h2 id="Property">Property Animation</h2> +<p>An animation defined in XML that modifies properties of the target object, such as +background color or alpha value, over a set amount of time.</p> -<h2 id="Tween">Tween Animation</h2> +<dl class="xml"> + +<dt>file location:</dt> +<dd><code>res/animator/<em>filename</em>.xml</code><br/> +The filename will be used as the resource ID.</dd> + +<dt>compiled resource datatype:</dt> +<dd>Resource pointer to a {@link android.animation.ValueAnimator}, {@link android.animation.ObjectAnimator}, +or {@link android.animation.AnimatorSet}.</dd> + +<dt>resource reference:</dt> +<dd> +In Java: <code>R.animator.<em>filename</em></code><br/> +In XML: <code>@[<em>package</em>:]animator/<em>filename</em></code> +</dd> + +<dt>syntax:</dt> +<dd> +<pre class="stx"> +<<a href="#animator-set-element">set</a> + android:ordering=["together" | "sequentially"]> + + <<a href="#obj-animator-element">objectAnimator</a> + android:propertyName="<em>string</em>" + android:duration="<em>int</em>" + android:valueFrom="<em>float</em> | <em>int</em> | <em>color</em>" + android:valueTo="<em>float</em> | <em>int</em> | <em>color</em>" + android:startOffset="<em>int</em>" + android:repeatCount="<em>int</em>" + android:repeatMode=["repeat" | "reverse"] + android:valueType=["intType" | "floatType"]/> + + <<a href="#val-animator-element">animator</a> + android:duration="<em>int</em>" + android:valueFrom="<em>float</em> | <em>int</em> | <em>color</em>" + android:valueTo="<em>float</em> | <em>int</em> | <em>color</em>" + android:startOffset="<em>int</em>" + android:repeatCount="<em>int</em>" + android:repeatMode=["repeat" | "reverse"] + android:valueType=["intType" | "floatType"]/> + + <<a href="#animator-set-element">set</a>> + ... + </set> +</set> +</pre> + +<p>The file must have a single root element: either +<code><set></code>, <code><objectAnimator></code>, or <code><valueAnimator></code>. You can +group animation elements together inside the <code><set></code> element, including other +<code><set></code> elements. +</p> +</dd> + +<dt>elements:</dt> + <dd> + <dl class="tag-list"> + <dt id="animator-set-element"><code><set></code></dt> + <dd>A container that holds other animation elements (<code><objectAnimator></code>, + <code><valueAnimator></code>, or other <code><set></code> elements). Represents + an {@link android.animation.AnimatorSet}. + <p>You can specify nested <code><set></code> tags to further + group animations together. Each <code><set></code> can define its own + <code>ordering</code> attribute.</p> + + <p class="caps">attributes:</p> + <dl class="atn-list"> + <dt> + <code>android:ordering</code> + </dt> + <dd> + <em>Keyword</em>. Specifies the play ordering of animations in this set. + <table> + <tr><th>Value</th><th>Description</th></tr> + <tr><td><code>sequentially</code></td><td>Play animations in this set sequentially</td></tr> + <tr><td><code>together</code> (default)</td><td>Play animations in this set at the same time.</td></tr> + </table> + </dd> + </dl> + </dd> + + <dt id="obj-animator-element"><code><objectAnimator></code></dt> + <dd>Animates a specific property of an object over a specific amount of time. Represents + an {@link android.animation.ObjectAnimator}.</p> + + <p class="caps">attributes:</p> + <dl class="atn-list"> + <dt> + <code>android:propertyName</code> + </dt> + <dd> + <em>String</em>. <strong>Required</strong>. The object's property to animate, referenced by its name. For example you can specify + <code>"alpha"</code> or <code>"backgroundColor"</code> for a View object. + The <code>objectAnimator</code> element does not expose a <code>target</code> + attribute, however, so you cannot set the object to animate in the XML declaration. You have to + inflate your animation XML resource by calling {@link android.animation.AnimatorInflater#loadAnimator + loadAnimator()} and call {@link android.animation.ObjectAnimator#setTarget setTarget()} to set + the target object that contains this property. + </dd> + + <dt> + <code>android:valueTo</code> + </dt> + <dd> + <em>float, int, or color</em>. <strong>Required</strong>. The value where the animated property ends. Colors are represented + as six digit hexadecimal numbers (for example, #333333). + </dd> + + <dt> + <code>android:valueFrom</code> + </dt> + <dd> + <em>float, int, or color</em>. The value where the animated property starts. If not + specified, the animation starts at the value obtained by the property's get method. Colors are represented + as six digit hexadecimal numbers (for example, #333333). + </dd> + + <dt> + <code>android:duration</code> + </dt> + <dd> + <em>int</em>. The time in milliseconds of the animation. 300 milliseconds is the default. + </dd> + + <dt> + <code>android:startOffset</code> + </dt> + <dd> + <em>int</em>. The amount of milliseconds the animation delays after + {@link android.animation.ObjectAnimator#start start()} is called. + </dd> + + <dt> + <code>android:repeatCount</code> + </dt> + <dd> + <em>int</em>. How many times to repeat an animation. Set to <code>"-1"</code> to infinitely + repeat or to a positive integer. For example, a value of <code>"1"</code> means that the animation + is repeated once after the initial run of the animation, so the animation plays a total + of two times. The default value is <code>"0"</code>, which means no repetition. + + </dd> + + <dt> + <code>android:repeatMode</code> + </dt> + <dd> + <em>int</em>. How an animation behaves when it reaches the end of the animation. <code>android:repeatCount</code> + must be set to a positive integer or <code>"-1"</code> for this attribute to have an effect. Set to <code>"reverse"</code> + to have the animation reverse direction with each iteration or <code>"repeat"</code> to have the animation + loop from the beginning each time. + </dd> + + <dt> + <code>android:valueType</code> + </dt> + <dd> + <em>Keyword</em>. Do not specify this attribute if the value is a color. The animation framework automatically handles color + values + <table> + <tr><th>Value</th><th>Description</th></tr> + <tr><td><code>intType</code></td><td>Specifies that the animated values are integers</td></tr> + <tr><td><code>floatType</code> (default)</td><td>Specifies that the animated values are floats</td></tr> + </table> + </dd> + + </dl> + </dd> + +<dt id="val-animator-element"><code><animator></code></dt> + <dd>Animates a over a specified amount of time. + Represents a {@link android.animation.ValueAnimator}. + + <p class="caps">attributes:</p> + <dl class="atn-list"> + <dt> + <code>android:valueTo</code> + </dt> + <dd> + <em>float, int, or color</em>. <strong>Required</strong>. The value where the animation ends. Colors are represented + as six digit hexadecimal numbers (for example, #333333). + </dd> + + <dt> + <code>android:valueFrom</code> + </dt> + <dd> + <em>float, int, or color</em>. <strong>Required</strong>. The value where the animation starts. Colors are represented + as six digit hexadecimal numbers (for example, #333333). + </dd> + + <dt> + <code>android:duration</code> + </dt> + <dd> + <em>int</em>. The time in milliseconds of the animation. 300ms is the default. + </dd> + + <dt> + <code>android:startOffset</code> + </dt> + <dd> + <em>int</em>. The amount of milliseconds the animation delays after + {@link android.animation.ValueAnimator#start start()} is called. + </dd> + + <dt> + <code>android:repeatCount</code> + </dt> + <dd> + <em>int</em>. How many times to repeat an animation. Set to <code>"-1"</code> to infinitely + repeat or to a positive integer. For example, a value of <code>"1"</code> means that the animation + is repeated once after the initial run of the animation, so the animation plays a total + of two times. The default value is <code>"0"</code>, which means no repetition. + </dd> + + <dt> + <code>android:repeatMode</code> + </dt> + <dd> + <em>int</em>. How an animation behaves when it reaches the end of the animation. <code>android:repeatCount</code> + must be set to a positive integer or <code>"-1"</code> for this attribute to have an effect. Set to <code>"reverse"</code> + to have the animation reverse direction with each iteration or <code>"repeat"</code> to have the animation + loop from the beginning each time. + </dd> + + <dt> + <code>android:valueType</code> + </dt> + <dd> + <em>Keyword</em>. Do not specify this attribute if the value is a color. The animation framework automatically handles color + values. + <table> + <tr><th>Value</th><th>Description</th></tr> + <tr><td><code>intType</code></td><td>Specifies that the animated values are integers</td></tr> + <tr><td><code>floatType</code> (default)</td><td>Specifies that the animated values are floats</td></tr> + </table> + </dd> + + </dl> + </dd> + </dl> + +</dd> <!-- end elements and attributes --> + +<dt>example:</dt> +<dd> + <pp>XML file saved at <code>res/animator/property_animator.xml</code>:</p> +<pre> +<set android:ordering="sequentially"> + <set> + <objectAnimator + android:propertyName="x" + android:duration="500" + android:valueTo="400" + android:valueType="intType"/> + <objectAnimator + android:propertyName="y" + android:duration="500" + android:valueTo="300" + android:valueType="intType"/> + </set> + <objectAnimator + android:propertyName="alpha" + android:duration="500" + android:valueTo="1f"/> +</set> +</pre> + <p>In order to run this animation, you must inflate the XML resources in your code to an {@link + android.animation.AnimatorSet} object, and then set the target objects for all of the animations + before starting the animation set. Calling {@link android.animation.AnimatorSet#setTarget + setTarget()} sets a single target object for all children of the {@link + android.animation.AnimatorSet} as a convenience. The following code shows how to do this:</p> + +<pre> +AnimatorSet set = (AnimatorSet) AnimatorInflater.loadAnimator(myContext, + R.anim.property_animator); +set.setTarget(myObject); +set.start(); +</pre> + + +</dd> <!-- end example --> + +<dt>see also:</dt> +<dd> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/animation.html">Property Animation</a></li> + <li><a href="http://zoso:8080/resources/samples/ApiDemos/src/com/example/android/apis/animation/index.html">API Demos</a> for examples + on how to use the property animation system.</li> +</ul> +</dd> + +</dl> + +<h2 id="View">View Animation</h2> +The view animation framework supports both tween and frame by frame animations, which can both be declared +in XML. The following sections describe how to use both methods. + +<h3 id="Tween">Tween animation</h3> <p>An animation defined in XML that performs transitions such as rotating, fading, moving, and stretching on a graphic. @@ -254,18 +574,14 @@ image.{@link android.view.View#startAnimation(Animation) startAnimation}(hypersp <dt>see also:</dt> <dd> <ul> - <li><a href="{@docRoot}guide/topics/graphics/2d-graphics.html#tween-animation">2D + <li><a href="{@docRoot}guide/topics/graphics/view-animation.html#tween-animation">2D Graphics: Tween Animation</a></li> </ul> </dd> </dl> - - - - -<h3 id="Interpolators">Interpolators</h3> +<h4 id="Interpolators">Interpolators</h4> <p>An interpolator is an animation modifier defined in XML that affects the rate of change in an animation. This allows your existing animation effects to be accelerated, decelerated, repeated, @@ -456,22 +772,7 @@ sinusoidal pattern. </dl> - - - - - - - - - - - - - - - -<h2 id="Frame">Frame Animation</h2> +<h3 id="Frame">Frame animation</h3> <p>An animation defined in XML that shows a sequence of images in order (like a film). </p> @@ -562,7 +863,11 @@ rocketAnimation.{@link android.graphics.drawable.AnimationDrawable#start()}; </dl> </dd> <!-- end example --> +<dt>see also:</dt> +<dd> +<ul> + <li><a href="{@docRoot}guide/topics/graphics/view-animation.html#frame-animation">2D +Graphics: Frame Animation</a></li> +</ul> +</dd> </dl> - - - diff --git a/docs/html/guide/topics/resources/providing-resources.jd b/docs/html/guide/topics/resources/providing-resources.jd index 59f2e731dba5..a996ccc08d76 100644 --- a/docs/html/guide/topics/resources/providing-resources.jd +++ b/docs/html/guide/topics/resources/providing-resources.jd @@ -333,6 +333,86 @@ your application for other languages.</p> indicates the current locale.</p> </td> </tr> + <tr id="SmallestScreenWidthQualifier"> + <td>Smallest screen width</td> + <td>Examples:<br/> + <code>sw320dp</code><br/> + <code>sw600dp</code><br/> + <code>sw720dp</code><br/> + etc. + </td> + <td> + <p>Specifies a minimum "smallest screen width," in "dp" units, at which the resource + should be used. This configuration value represents the base screen size + of the device, regardless of the orientation of the display. It is based + on the smallest width the application will have in which to perform its + UI layout (in dp units) regardless of the orientation of the screen. The + value here takes into account screen decorations so if the device has some + persistent UI elements on the left or right edge of the display it must + present a value here that is smaller than the real screen size, accounting + for these UI elements reducing the application's available space.</p> + <p>Some values you may use here for common screen sizes:</p> + <ul> + <li>240x320 ldpi (QVGA phone): 320 + <li>320x480 mdpi (phone): 320 + <li>480x800 hdpi (high density phone): 320 + <li>480x800 mdpi (tablet/phone): 480 + <li>600x1024 mdpi (7" tablet): 600 + <li>720x1280 mdpi (10" tablet): 720 + </ul> + <p><em>Added in API Level 13.</em></p> + <p>Also see the {@link android.content.res.Configuration#smallestScreenWidthDp} + configuration field, which holds the current smallest screen width.</p> + </td> + </tr> + <tr id="ScreenWidthQualifier"> + <td>Screen width</td> + <td>Examples:<br/> + <code>w720dp</code><br/> + <code>w1024dp</code><br/> + etc. + </td> + <td> + <p>Specifies a minimum screen width, in "dp" units, at which the resource + should be used. This configuration value will change when the orientation + changes between landscape and portrait to match the current actual width. + When multiple screen width configurations are available, the closest to + the current screen width will be used. The + value here takes into account screen decorations so if the device has some + persistent UI elements on the left or right edge of the display it must + present a value here that is smaller than the real screen size, accounting + for these UI elements reducing the application's available space.</p> + <p><em>Added in API Level 13.</em></p> + <p>Also see the {@link android.content.res.Configuration#screenWidthDp} + configuration field, which holds the current screen width.</p> + </td> + </tr> + <tr id="ScreenHeightQualifier"> + <td>Screen height</td> + <td>Examples:<br/> + <code>h720dp</code><br/> + <code>h1024dp</code><br/> + etc. + </td> + <td> + <p>Specifies a minimum screen height, in "dp" units, at which the resource + should be used. This configuration value will change when the orientation + changes between landscape and portrait to match the current actual height. + When multiple screen height configurations are available, the closest to + the current screen height will be used. The + value here takes into account screen decorations so if the device has some + persistent UI elements on the left or right edge of the display it must + present a value here that is smaller than the real screen size, accounting + for these UI elements reducing the application's available space. Screen + decorations that are not fixed (such as a phone status bar that can be + hidden when full screen) are <em>not</em> accounted for here, nor are + window decorations like title bar, so applications must be prepared to + deal with a somewhat smaller space than they specify. + <p><em>Added in API Level 13.</em></p> + <p>Also see the {@link android.content.res.Configuration#screenHeightDp} + configuration field, which holds the current screen width.</p> + </td> + </tr> <tr id="ScreenSizeQualifier"> <td>Screen size</td> <td> @@ -392,46 +472,6 @@ is not related to the screen orientation.</p> which indicates whether the screen is long.</p> </td> </tr> - <tr id="ScreenWidthQualifier"> - <td>Screen width</td> - <td>Examples:<br/> - <code>w720dp</code><br/> - <code>w1024dp</code><br/> - etc. - </td> - <td> - <p>Specifies a minimum screen width, in "dp" units, at which the resource - should be used. This configuration value will change when the orientation - changes between landscape and portrait to match the current actual width. - When multiple screen width configurations are available, the closest to - the current screen width will be used. The value specified here is - approximate; screen decorations like a status bar or system bar may cause - the actual space available in your UI to be slightly smaller. - <p><em>Added in API Level 13.</em></p> - <p>Also see the {@link android.content.res.Configuration#screenWidthDp} - configuration field, which holds the current screen width.</p> - </td> - </tr> - <tr id="ScreenHeightQualifier"> - <td>Screen height</td> - <td>Examples:<br/> - <code>h720dp</code><br/> - <code>h1024dp</code><br/> - etc. - </td> - <td> - <p>Specifies a minimum screen height, in "dp" units, at which the resource - should be used. This configuration value will change when the orientation - changes between landscape and portrait to match the current actual height. - When multiple screen height configurations are available, the closest to - the current screen height will be used. The value specified here is - approximate; screen decorations like a status bar or system bar may cause - the actual space available in your UI to be slightly smaller. - <p><em>Added in API Level 13.</em></p> - <p>Also see the {@link android.content.res.Configuration#screenHeightDp} - configuration field, which holds the current screen width.</p> - </td> - </tr> <tr id="OrientationQualifier"> <td>Screen orientation</td> <td> diff --git a/docs/html/guide/topics/usb/adk.jd b/docs/html/guide/topics/usb/adk.jd index 8aaa65c9fd0b..e4e12157468b 100644 --- a/docs/html/guide/topics/usb/adk.jd +++ b/docs/html/guide/topics/usb/adk.jd @@ -24,8 +24,7 @@ page.title=Android Open Accessory Development Kit </li> <li> - <a href="#how">How an Accessory Communicates with an Android-powered Device in Accessory - Mode</a> + <a href="#accessory-protocol">Implementing the Android Accessory Protocol</a> <ol> <li><a href="#wait">Wait for and detect connected devices</a></li> @@ -40,8 +39,7 @@ page.title=Android Open Accessory Development Kit </li> <li> - <a href="#firmware">How the ADK board communicates with an Android-powered Device in - Accessory Mode</a> + <a href="#firmware">How the ADK board implements the Android Accessory Protocol</a> <ol> <li><a href="#wait-adk">Wait for and detect connected devices</a></li> @@ -56,7 +54,7 @@ page.title=Android Open Accessory Development Kit </li> </ol> - + <h2>See also</h2> @@ -64,7 +62,7 @@ page.title=Android Open Accessory Development Kit <li><a href="http://www.youtube.com/watch?v=s7szcpXf2rE">Google I/O Session Video</a></li> <li><a href="{@docRoot}guide/topics/usb/accessory.html">USB Accessory Dev Guide</a></li> </ol> - + <h2>Where to buy</h2> <ol> @@ -78,6 +76,9 @@ page.title=Android Open Accessory Development Kit <li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone"> DIY Drones</a></li> + + <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board"> + Modern Device</a></li> </ol> </div> </div> @@ -86,7 +87,7 @@ page.title=Android Open Accessory Development Kit support, which allows external USB hardware (an Android USB accessory) to interact with an Android-powered device in a special "accessory" mode. When an Android-powered powered device is in accessory mode, the connected accessory acts as the USB host (powers the bus and enumerates - devices) and the Android-powered device acts as the device. Android USB accessories are + devices) and the Android-powered device acts as the USB device. Android USB accessories are specifically designed to attach to Android-powered devices and adhere to a simple protocol (Android accessory protocol) that allows them to detect Android-powered devices that support accessory mode. Accessories must also provide 500mA at 5V for charging power. Many previously @@ -112,6 +113,8 @@ page.title=Android Open Accessory Development Kit <li><a href="https://store.diydrones.com/ProductDetails.asp?ProductCode=BR-PhoneDrone">DIY Drones</a> provides an Arduino-compatible board geared towards RC (radio controlled) and UAV (unmanned aerial vehicle) enthusiasts.</li> + <li><a href="http://shop.moderndevice.com/products/freeduino-usb-host-board">Modern + Device</a> provides an Arduino-compatible board that supports the ADK firmware.</li> </ul> <p>We expect more hardware distributers to create a variety of kits, so please stay tuned for @@ -380,14 +383,11 @@ page.title=Android Open Accessory Development Kit accessories communicate with Android-powered devices describe much of what you should be doing in your own accessory.</p> - <h2 id="how">How an Accessory Communicates with an Android-powered Device in Accessory Mode</h2> + <h2 id="accessory-protocol">Implementing the Android Accessory Protocol</h2> - <p>When you connect an accessory to an Android-powered device, the accessory's firmware must - carry out some standard steps to set up communication with the Android-powered device. If you are - building an accessory along with an application, this section goes over some general steps that - your firmware should carry out.</p> - - <p>In general, an accessory should carry out the following steps:</p> + <p>An Android USB accessory must adhere to Android Accessory Protocol, which defines how + an accessory detects and sets up communication with an Android-powered device. In general, an + accessory should carry out the following steps:</p> <ol> <li>Wait for and detect connected devices</li> @@ -399,6 +399,8 @@ page.title=Android Open Accessory Development Kit <li>Establish communication with the device if it supports the Android accessory protocol</li> </ol> + <p>The following sections go into depth about how to implement these steps.</p> + <h3 id="wait">Wait for and detect connected devices</h3> <p>Your accessory should have logic to continuously check @@ -476,12 +478,12 @@ data zero terminated UTF8 string sent from accessory to device <p>The following string IDs are supported, with a maximum size of 256 bytes for each string (must be zero terminated with \0).</p> <pre> -manufacturer name: 1 -model name: 2 -description: 3 -version: 4 -URI: 5 -serial number: 6 +manufacturer name: 0 +model name: 1 +description: 2 +version: 3 +URI: 4 +serial number: 5 </pre> </li> @@ -520,12 +522,11 @@ data: none device's configuration to a value of 1 with a SET_CONFIGURATION (0x09) device request, then communicate using the endpoints.</p> - <h2 id="firmware">How the ADK board communicates with an Android-powered Device in Accessory - Mode</h2> + <h2 id="firmware">How the ADK board implements the Android Accessory protocol</h2> <p>If you have access to the ADK board and shield, the following sections describe the firmware code that you installed onto the ADK board. The firmware demonstrates a practical example of how - to communicate with an Android-powered device. Even if you do not have the ADK board and shield, + to implement the Android Accessory protocol. Even if you do not have the ADK board and shield, reading through how the hardware detects and interacts with devices in accessory mode is still useful if you want to port the code over for your own accessories.</p> @@ -540,8 +541,7 @@ data: none sections.</p> <p>The following sections describe the firmware code in the context of the algorithm described in - <a href="#how">How an Accessory Communicates with an Android-powered Device in Accessory - Mode</a>.</p> + <a href="#accessory-protocol">Implementing the Android Accessory Protocol</a>.</p> <h3 id="wait-adk">Wait for and detect connected devices</h3> @@ -559,13 +559,13 @@ AndroidAccessory acc("Google, Inc.", "1.0", "http://www.android.com", "0000000012345678"); - + ... void loop() { ... - if (acc.isConnected()) { - //communicate with Android application + if (acc.isConnected()) { + //communicate with Android application } else{ //set the accessory to its default state @@ -728,7 +728,7 @@ bool AndroidAccessory::findEndpoints(byte addr, EP_RECORD *inEp, EP_RECORD *outE Serial.print("Can't get config descriptor\n"); return false; } - + ... </pre> @@ -859,13 +859,13 @@ bool AndroidAccessory::configureAndroid(void) changes the state of the accessory, such as lighting up or changing the color of the LED lights.</p> <pre> -int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit) { - return usb.newInTransfer(1, in, len, (char *)buff, nakLimit); } - -int AndroidAccessory::write(void *buff, int len) { - usb.outTransfer(1, out, len, (char *)buff); +int AndroidAccessory::read(void *buff, int len, unsigned int nakLimit) { + return usb.newInTransfer(1, in, len, (char *)buff, nakLimit); } + +int AndroidAccessory::write(void *buff, int len) { + usb.outTransfer(1, out, len, (char *)buff); return len; } - + </pre> <p>See the <code>firmware/demokit/demokit.pde</code> file for information about how the ADK board diff --git a/docs/html/sdk/ndk/index.jd b/docs/html/sdk/ndk/index.jd index bc9ba4be6259..e980ca5261d2 100644 --- a/docs/html/sdk/ndk/index.jd +++ b/docs/html/sdk/ndk/index.jd @@ -1,16 +1,16 @@ ndk=true -ndk.win_download=android-ndk-r5b-windows.zip -ndk.win_bytes=61299831 -ndk.win_checksum=87745ada305ab639399161ab4faf684c +ndk.win_download=android-ndk-r5c-windows.zip +ndk.win_bytes=61627716 +ndk.win_checksum=2c7423842fa0f46871eab118495d4b45 -ndk.mac_download=android-ndk-r5b-darwin-x86.tar.bz2 -ndk.mac_bytes=50210863 -ndk.mac_checksum=019a14622a377b3727ec789af6707037 +ndk.mac_download=android-ndk-r5c-darwin-x86.tar.bz2 +ndk.mac_bytes=50714712 +ndk.mac_checksum=183bfbbd85cf8e4c0bd7531e8803e75d -ndk.linux_download=android-ndk-r5b-linux-x86.tar.bz2 -ndk.linux_bytes=44138539 -ndk.linux_checksum=4c0045ddc2bfd657be9d5177d0e0b7e7 +ndk.linux_download=android-ndk-r5c-linux-x86.tar.bz2 +ndk.linux_bytes=44539890 +ndk.linux_checksum=7659dfdc97026ed1d913e224d0531f61 page.title=Android NDK @jd:body @@ -52,14 +52,97 @@ padding: .25em 1em; text-decoration:underline; } .toggleable.closed .toggleme { - display:none; -} + display:none;} #jd-content .toggle-img { margin:0; } </style> + <div class="toggleable open"> + <a href="#" onclick="return toggleDiv(this)"><img src= + "{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px"> + Android NDK, Revision 5c</a> <em>(June 2011)</em> + + <div class="toggleme"> + <p>This release of the NDK does not include any new features compared to r5b. The r5c release + addresses the following problems in the r5b release:</p> + <dl> + <dt>Important bug fixes:</dt> + <dd> + <ul> + <li><code>ndk-build</code>: Fixed a rare bug that appeared when trying to perform parallel + builds of debuggable projects.</li> + + <li>Fixed a typo that prevented <code>LOCAL_WHOLE_STATIC_LIBRARIES</code> to work + correctly with the new toolchain and added documentation for this in + <code>docs/ANDROID-MK.html</code>.</li> + + <li>Fixed a bug where code linked against <code>gnustl_static</code> crashed when run on + platform releases older than API level 8 (Android 2.2).</li> + + <li><code>ndk-gdb</code>: Fixed a bug that caused a segmentation fault when debugging Android 3.0 + or newer devices.</li> + + <li><code><android/input.h></code>: Two functions that were introduced in API level + 9 (Android 2.3) were incorrect and are fixed. While this breaks the source API, the + binary interface to the system is unchanged. The incorrect functions were missing a + <code>history_index</code> parameter, and the correct definitions are shown below: +<pre> +float AMotionEvent_getHistoricalRawX(const AInputEvent* motion_event, + size_t pointer_index, + size_t history_index); + +float AMotionEvent_getHistoricalRawY(const AInputEvent* motion_event, + size_t pointer_index, + size_t history_index); +</pre> + </li> + + <li>Updated the C library ARM binary for API level 9 (Android 2.3) to correctly expose at + link time new functions that were added in that API level (for example, + <code>pthread_rwlock_init</code>).</li> + + </ul> + </dd> + + <dt>Minor improvements and fixes:</dt> + <dd> + <ul> + <li>Object files are now always linked in the order they appear in + <code>LOCAL_SRC_FILES</code>. This was not the case previously because the files were + grouped by source extensions instead.</li> + + <li>When <code>import-module</code> fails, it now prints the list of directories that + were searched. This is useful to check that the <code>NDK_MODULE_PATH</code> definition + used by the build system is correct.</li> + + <li>When <code>import-module</code> succeeds, it now prints the directory where the + module was found to the log (visible with <code>NDK_LOG=1</code>).</li> + + <li>Increased the build speed of debuggable applications when there is a very large number + of include directories in the project.</li> + + <li><code>ndk-gdb</code>: Better detection of <code>adb shell</code> failures and improved + error messages.</li> + + <li><code><pthread.h></code>: Fixed the definition of + <code>PTHREAD_RWLOCK_INITIALIZER</code> for API level 9 (Android 2.3) and higher.</li> + + <li>Fixed an issue where a module could import itself, resulting in an infinite loop in + GNU Make.</li> + + <li>Fixed a bug that caused the build to fail if <code>LOCAL_ARM_NEON</code> was set to + true (typo in <code>build/core/build-binary.mk</code>).</li> + + <li>Fixed a bug that prevented the compilation of </code>.s</code> assembly files + (<code>.S</code> files were okay).</li> + </ul> + </dd> + </div> +</div> + +<div class="toggleable closed"> <a href="#" onclick="return toggleDiv(this)"><img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index 5b90551385ed..5750c815b1d4 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -175,7 +175,7 @@ class="new">new!</span></li> <span style="display:none" class="zh-TW"></span> </h2> <ul> - <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5b</a> + <li><a href="<?cs var:toroot ?>sdk/ndk/index.html">Android NDK, r5c</a> </li> <li><a href="<?cs var:toroot ?>sdk/ndk/overview.html">What is the NDK?</a></li> </ul> diff --git a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp index 3cbd8f1ff14f..07b3b47ee931 100644 --- a/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp +++ b/drm/libdrmframework/plugins/forward-lock/FwdLockEngine/src/FwdLockEngine.cpp @@ -53,11 +53,11 @@ extern "C" void destroy(IDrmEngine* plugIn) { } FwdLockEngine::FwdLockEngine() { - LOGD("FwdLockEngine Construction"); + LOGV("FwdLockEngine Construction"); } FwdLockEngine::~FwdLockEngine() { - LOGD("FwdLockEngine Destruction"); + LOGV("FwdLockEngine Destruction"); convertSessionMap.destroyMap(); decodeSessionMap.destroyMap(); @@ -91,7 +91,7 @@ int FwdLockEngine::getConvertedStatus(FwdLockConv_Status_t status) { DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* path, int action) { DrmConstraints* drmConstraints = NULL; - LOGD("FwdLockEngine::onGetConstraints"); + LOGV("FwdLockEngine::onGetConstraints"); if (NULL != path && (RightsStatus::RIGHTS_VALID == onCheckRightsStatus(uniqueId, *path, action))) { @@ -105,7 +105,7 @@ DrmConstraints* FwdLockEngine::onGetConstraints(int uniqueId, const String8* pat DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) { DrmMetadata* drmMetadata = NULL; - LOGD("FwdLockEngine::onGetMetadata"); + LOGV("FwdLockEngine::onGetMetadata"); if (NULL != path) { // Returns empty metadata to show no error condition. @@ -116,11 +116,11 @@ DrmMetadata* FwdLockEngine::onGetMetadata(int uniqueId, const String8* path) { } android::status_t FwdLockEngine::onInitialize(int uniqueId) { - LOGD("FwdLockEngine::onInitialize"); + LOGV("FwdLockEngine::onInitialize"); if (FwdLockGlue_InitializeKeyEncryption()) { - LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded"); + LOGV("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption succeeded"); } else { LOGD("FwdLockEngine::onInitialize -- FwdLockGlue_InitializeKeyEncryption failed:" "errno = %d", errno); @@ -132,13 +132,13 @@ android::status_t FwdLockEngine::onInitialize(int uniqueId) { android::status_t FwdLockEngine::onSetOnInfoListener(int uniqueId, const IDrmEngine::OnInfoListener* infoListener) { // Not used - LOGD("FwdLockEngine::onSetOnInfoListener"); + LOGV("FwdLockEngine::onSetOnInfoListener"); return DRM_NO_ERROR; } android::status_t FwdLockEngine::onTerminate(int uniqueId) { - LOGD("FwdLockEngine::onTerminate"); + LOGV("FwdLockEngine::onTerminate"); return DRM_NO_ERROR; } @@ -146,7 +146,7 @@ android::status_t FwdLockEngine::onTerminate(int uniqueId) { DrmSupportInfo* FwdLockEngine::onGetSupportInfo(int uniqueId) { DrmSupportInfo* pSupportInfo = new DrmSupportInfo(); - LOGD("FwdLockEngine::onGetSupportInfo"); + LOGV("FwdLockEngine::onGetSupportInfo"); // fill all Forward Lock mimetypes and extensions if (NULL != pSupportInfo) { @@ -182,7 +182,7 @@ DrmInfoStatus* FwdLockEngine::onProcessDrmInfo(int uniqueId, const DrmInfo* drmI drmInfoStatus = new DrmInfoStatus((int)DrmInfoStatus::STATUS_OK, 0, NULL, String8("")); - LOGD("FwdLockEngine::onProcessDrmInfo"); + LOGV("FwdLockEngine::onProcessDrmInfo"); return drmInfoStatus; } @@ -193,7 +193,7 @@ status_t FwdLockEngine::onSaveRights( const String8& rightsPath, const String8& contentPath) { // No rights to save. Return - LOGD("FwdLockEngine::onSaveRights"); + LOGV("FwdLockEngine::onSaveRights"); return DRM_ERROR_UNKNOWN; } @@ -201,7 +201,7 @@ DrmInfo* FwdLockEngine::onAcquireDrmInfo(int uniqueId, const DrmInfoRequest* drm DrmInfo* drmInfo = NULL; // Nothing to be done for Forward Lock file - LOGD("FwdLockEngine::onAcquireDrmInfo"); + LOGV("FwdLockEngine::onAcquireDrmInfo"); return drmInfo; } @@ -211,7 +211,7 @@ int FwdLockEngine::onCheckRightsStatus(int uniqueId, int action) { int result = RightsStatus::RIGHTS_INVALID; - LOGD("FwdLockEngine::onCheckRightsStatus"); + LOGV("FwdLockEngine::onCheckRightsStatus"); // Only Transfer action is not allowed for forward Lock files. if (onCanHandle(uniqueId, path)) { @@ -241,7 +241,7 @@ status_t FwdLockEngine::onConsumeRights(int uniqueId, int action, bool reserve) { // No rights consumption - LOGD("FwdLockEngine::onConsumeRights"); + LOGV("FwdLockEngine::onConsumeRights"); return DRM_NO_ERROR; } @@ -249,14 +249,14 @@ bool FwdLockEngine::onValidateAction(int uniqueId, const String8& path, int action, const ActionDescription& description) { - LOGD("FwdLockEngine::onValidateAction"); + LOGV("FwdLockEngine::onValidateAction"); // For the forwardlock engine checkRights and ValidateAction are the same. return (onCheckRightsStatus(uniqueId, path, action) == RightsStatus::RIGHTS_VALID); } String8 FwdLockEngine::onGetOriginalMimeType(int uniqueId, const String8& path) { - LOGD("FwdLockEngine::onGetOriginalMimeType"); + LOGV("FwdLockEngine::onGetOriginalMimeType"); String8 mimeString = String8(""); int fileDesc = FwdLockFile_open(path.string()); @@ -280,7 +280,7 @@ int FwdLockEngine::onGetDrmObjectType(int uniqueId, const String8& mimeType) { String8 mimeStr = String8(mimeType); - LOGD("FwdLockEngine::onGetDrmObjectType"); + LOGV("FwdLockEngine::onGetDrmObjectType"); mimeStr.toLower(); @@ -301,13 +301,13 @@ int FwdLockEngine::onGetDrmObjectType(int uniqueId, status_t FwdLockEngine::onRemoveRights(int uniqueId, const String8& path) { // No Rights to remove - LOGD("FwdLockEngine::onRemoveRights"); + LOGV("FwdLockEngine::onRemoveRights"); return DRM_NO_ERROR; } status_t FwdLockEngine::onRemoveAllRights(int uniqueId) { // No rights to remove - LOGD("FwdLockEngine::onRemoveAllRights"); + LOGV("FwdLockEngine::onRemoveAllRights"); return DRM_NO_ERROR; } @@ -319,14 +319,14 @@ status_t FwdLockEngine::onSetPlaybackStatus(int uniqueId, DecryptHandle* decrypt int playbackStatus, int position) { #endif // Not used - LOGD("FwdLockEngine::onSetPlaybackStatus"); + LOGV("FwdLockEngine::onSetPlaybackStatus"); return DRM_NO_ERROR; } status_t FwdLockEngine::onOpenConvertSession(int uniqueId, int convertId) { status_t result = DRM_ERROR_UNKNOWN; - LOGD("FwdLockEngine::onOpenConvertSession"); + LOGV("FwdLockEngine::onOpenConvertSession"); if (!convertSessionMap.isCreated(convertId)) { ConvertSession *newSession = new ConvertSession(); if (FwdLockConv_Status_OK == @@ -383,7 +383,7 @@ DrmConvertedStatus* FwdLockEngine::onCloseConvertSession(int uniqueId, DrmBuffer *convResult = new DrmBuffer(NULL, 0); int offset = -1; - LOGD("FwdLockEngine::onCloseConvertSession"); + LOGV("FwdLockEngine::onCloseConvertSession"); if (convertSessionMap.isCreated(convertId)) { ConvertSession *convSession = convertSessionMap.getValue(convertId); @@ -424,7 +424,7 @@ status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, status_t result = DRM_ERROR_CANNOT_HANDLE; int fileDesc = -1; - LOGD("FwdLockEngine::onOpenDecryptSession"); + LOGV("FwdLockEngine::onOpenDecryptSession"); if ((-1 < fd) && (NULL != decryptHandle) && @@ -463,7 +463,7 @@ status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, ::close(fileDesc); } - LOGD("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result); + LOGV("FwdLockEngine::onOpenDecryptSession Exit. result = %d", result); return result; } @@ -500,7 +500,7 @@ status_t FwdLockEngine::onOpenDecryptSession(int uniqueId, status_t FwdLockEngine::onCloseDecryptSession(int uniqueId, DecryptHandle* decryptHandle) { status_t result = DRM_ERROR_UNKNOWN; - LOGD("FwdLockEngine::onCloseDecryptSession"); + LOGV("FwdLockEngine::onCloseDecryptSession"); if (NULL != decryptHandle && decodeSessionMap.isCreated(decryptHandle->decryptId)) { DecodeSession* session = decodeSessionMap.getValue(decryptHandle->decryptId); @@ -512,7 +512,7 @@ status_t FwdLockEngine::onCloseDecryptSession(int uniqueId, } } - LOGD("FwdLockEngine::onCloseDecryptSession Exit"); + LOGV("FwdLockEngine::onCloseDecryptSession Exit"); return result; } @@ -520,13 +520,13 @@ status_t FwdLockEngine::onInitializeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* headerInfo) { - LOGD("FwdLockEngine::onInitializeDecryptUnit"); + LOGV("FwdLockEngine::onInitializeDecryptUnit"); return DRM_ERROR_UNKNOWN; } status_t FwdLockEngine::onDecrypt(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer, DrmBuffer* IV) { - LOGD("FwdLockEngine::onDecrypt"); + LOGV("FwdLockEngine::onDecrypt"); return DRM_ERROR_UNKNOWN; } @@ -535,14 +535,14 @@ status_t FwdLockEngine::onDecrypt(int uniqueId, int decryptUnitId, const DrmBuffer* encBuffer, DrmBuffer** decBuffer) { - LOGD("FwdLockEngine::onDecrypt"); + LOGV("FwdLockEngine::onDecrypt"); return DRM_ERROR_UNKNOWN; } status_t FwdLockEngine::onFinalizeDecryptUnit(int uniqueId, DecryptHandle* decryptHandle, int decryptUnitId) { - LOGD("FwdLockEngine::onFinalizeDecryptUnit"); + LOGV("FwdLockEngine::onFinalizeDecryptUnit"); return DRM_ERROR_UNKNOWN; } diff --git a/graphics/java/android/renderscript/Allocation.java b/graphics/java/android/renderscript/Allocation.java index 3c8aba36563a..a63abb9f9bc7 100644 --- a/graphics/java/android/renderscript/Allocation.java +++ b/graphics/java/android/renderscript/Allocation.java @@ -588,13 +588,29 @@ public class Allocation extends BaseObj { * * @param off The offset of the first element to be copied. * @param count The number of elements to be copied. - * @param d the source data array + * @param d the source data array. */ public void copy1DRangeFrom(int off, int count, float[] d) { validateIsFloat32(); copy1DRangeFromUnchecked(off, count, d); } + /** + * Copy part of an allocation from another allocation. + * + * @param off The offset of the first element to be copied. + * @param count The number of elements to be copied. + * @param data the source data allocation. + * @param dataOff off The offset of the first element in data to + * be copied. + */ + public void copy1DRangeFrom(int off, int count, Allocation data, int dataOff) { + mRS.nAllocationData2D(getID(), off, 0, + 0, Type.CubemapFace.POSITVE_X.mID, + count, 1, data.getID(), dataOff, 0, + 0, Type.CubemapFace.POSITVE_X.mID); + } + private void validate2DRange(int xoff, int yoff, int w, int h) { if (xoff < 0 || yoff < 0) { throw new RSIllegalArgumentException("Offset cannot be negative."); @@ -609,9 +625,8 @@ public class Allocation extends BaseObj { } /** - * Copy a rectanglular region from the array into the - * allocation. The incoming array is assumed to be tightly - * packed. + * Copy a rectangular region from the array into the allocation. + * The incoming array is assumed to be tightly packed. * * @param xoff X offset of the region to update * @param yoff Y offset of the region to update @@ -644,6 +659,28 @@ public class Allocation extends BaseObj { } /** + * Copy a rectangular region into the allocation from another + * allocation. + * + * @param xoff X offset of the region to update. + * @param yoff Y offset of the region to update. + * @param w Width of the incoming region to update. + * @param h Height of the incoming region to update. + * @param data source allocation. + * @param dataXoff X offset in data of the region to update. + * @param dataYoff Y offset in data of the region to update. + */ + public void copy2DRangeFrom(int xoff, int yoff, int w, int h, + Allocation data, int dataXoff, int dataYoff) { + mRS.validate(); + validate2DRange(xoff, yoff, w, h); + mRS.nAllocationData2D(getID(), xoff, yoff, + 0, Type.CubemapFace.POSITVE_X.mID, + w, h, data.getID(), dataXoff, dataYoff, + 0, Type.CubemapFace.POSITVE_X.mID); + } + + /** * Copy a bitmap into an allocation. The height and width of * the update will use the height and width of the incoming * bitmap. diff --git a/graphics/java/android/renderscript/AllocationAdapter.java b/graphics/java/android/renderscript/AllocationAdapter.java index f2fedea3a530..07a1f5ddc423 100644 --- a/graphics/java/android/renderscript/AllocationAdapter.java +++ b/graphics/java/android/renderscript/AllocationAdapter.java @@ -33,7 +33,7 @@ public class AllocationAdapter extends Allocation { private Allocation mAlloc; private int mSelectedLOD = 0; - private Type.CubemapFace mSelectedFace = Type.CubemapFace.POSITVE_X;; + private Type.CubemapFace mSelectedFace = Type.CubemapFace.POSITVE_X; AllocationAdapter(int id, RenderScript rs, Allocation alloc) { super(id, rs, null, alloc.mUsage); @@ -163,15 +163,54 @@ public class AllocationAdapter extends Allocation { mRS.nAllocationData1D(getID(), off, mSelectedLOD, count, d, dataSize); } + /** + * Copy part of an allocation from another allocation. + * + * @param off The offset of the first element to be copied. + * @param count The number of elements to be copied. + * @param data the source data allocation. + * @param dataOff off The offset of the first element in data to + * be copied. + */ + public void subData1D(int off, int count, AllocationAdapter data, int dataOff) { + mRS.nAllocationData2D(getID(), off, 0, + mSelectedLOD, mSelectedFace.mID, + count, 1, data.getID(), dataOff, 0, + data.mSelectedLOD, data.mSelectedFace.mID); + } + public void subData2D(int xoff, int yoff, int w, int h, int[] d) { mRS.validate(); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, d, d.length * 4); + mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, + w, h, d, d.length * 4); } public void subData2D(int xoff, int yoff, int w, int h, float[] d) { mRS.validate(); - mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, w, h, d, d.length * 4); + mRS.nAllocationData2D(getID(), xoff, yoff, mSelectedLOD, mSelectedFace.mID, + w, h, d, d.length * 4); + } + + /** + * Copy a rectangular region into the allocation from another + * allocation. + * + * @param xoff X offset of the region to update. + * @param yoff Y offset of the region to update. + * @param w Width of the incoming region to update. + * @param h Height of the incoming region to update. + * @param data source allocation. + * @param dataXoff X offset in data of the region to update. + * @param dataYoff Y offset in data of the region to update. + */ + public void subData2D(int xoff, int yoff, int w, int h, + AllocationAdapter data, int dataXoff, int dataYoff) { + mRS.validate(); + mRS.nAllocationData2D(getID(), xoff, yoff, + mSelectedLOD, mSelectedFace.mID, + w, h, data.getID(), dataXoff, dataYoff, + data.mSelectedLOD, data.mSelectedFace.mID); } public void readData(int[] d) { @@ -185,12 +224,15 @@ public class AllocationAdapter extends Allocation { } public void setLOD(int lod) { + mSelectedLOD = lod; } public void setFace(Type.CubemapFace cf) { + mSelectedFace = cf; } public void setY(int y) { + mSelectedDimY = y; } public void setZ(int z) { diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index 17f0fa649527..2110e37582ae 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -295,6 +295,26 @@ public class RenderScript { rsnAllocationElementData1D(mContext, id, xoff, mip, compIdx, d, sizeBytes); } + native void rsnAllocationData2D(int con, + int dstAlloc, int dstXoff, int dstYoff, + int dstMip, int dstFace, + int width, int height, + int srcAlloc, int srcXoff, int srcYoff, + int srcMip, int srcFace); + synchronized void nAllocationData2D(int dstAlloc, int dstXoff, int dstYoff, + int dstMip, int dstFace, + int width, int height, + int srcAlloc, int srcXoff, int srcYoff, + int srcMip, int srcFace) { + validate(); + rsnAllocationData2D(mContext, + dstAlloc, dstXoff, dstYoff, + dstMip, dstFace, + width, height, + srcAlloc, srcXoff, srcYoff, + srcMip, srcFace); + } + native void rsnAllocationData2D(int con, int id, int xoff, int yoff, int mip, int face, int w, int h, byte[] d, int sizeBytes); synchronized void nAllocationData2D(int id, int xoff, int yoff, int mip, int face, int w, int h, byte[] d, int sizeBytes) { validate(); diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 60b39b01162a..7e53cc4df237 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -468,7 +468,7 @@ nAllocationCopyFromBitmap(JNIEnv *_env, jobject _this, RsContext con, jint alloc bitmap.lockPixels(); const void* ptr = bitmap.getPixels(); rsAllocation2DData(con, (RsAllocation)alloc, 0, 0, - 0, RS_ALLOCATION_CUBMAP_FACE_POSITVE_X, + 0, RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X, w, h, ptr, bitmap.getSize()); bitmap.unlockPixels(); } @@ -589,6 +589,30 @@ nAllocationData2D_f(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jint } static void +nAllocationData2D_alloc(JNIEnv *_env, jobject _this, RsContext con, + jint dstAlloc, jint dstXoff, jint dstYoff, + jint dstMip, jint dstFace, + jint width, jint height, + jint srcAlloc, jint srcXoff, jint srcYoff, + jint srcMip, jint srcFace) +{ + LOG_API("nAllocation2DData_s, con(%p), dstAlloc(%p), dstXoff, dstYoff," + " dstMip(%i), dstFace(%i), width(%i), height(%i)," + " srcAlloc(%p), srcXoff(%i), srcYoff(%i), srcMip(%i), srcFace(%i)", + con, (RsAllocation)dstAlloc, dstXoff, dstYoff, dstMip, dstFace, + width, height, (RsAllocation)srcAlloc, srcXoff, srcYoff, srcMip, srcFace); + + rsAllocationCopy2DRange(con, + (RsAllocation)dstAlloc, + dstXoff, dstYoff, + dstMip, dstFace, + width, height, + (RsAllocation)srcAlloc, + srcXoff, srcYoff, + srcMip, srcFace); +} + +static void nAllocationRead_i(JNIEnv *_env, jobject _this, RsContext con, jint alloc, jintArray data) { jint len = _env->GetArrayLength(data); @@ -1217,6 +1241,7 @@ static JNINativeMethod methods[] = { {"rsnAllocationData2D", "(IIIIIIII[SI)V", (void*)nAllocationData2D_s }, {"rsnAllocationData2D", "(IIIIIIII[BI)V", (void*)nAllocationData2D_b }, {"rsnAllocationData2D", "(IIIIIIII[FI)V", (void*)nAllocationData2D_f }, +{"rsnAllocationData2D", "(IIIIIIIIIIIII)V", (void*)nAllocationData2D_alloc }, {"rsnAllocationRead", "(II[I)V", (void*)nAllocationRead_i }, {"rsnAllocationRead", "(II[S)V", (void*)nAllocationRead_s }, {"rsnAllocationRead", "(II[B)V", (void*)nAllocationRead_b }, diff --git a/libs/gui/tests/SurfaceTextureClient_test.cpp b/libs/gui/tests/SurfaceTextureClient_test.cpp index e1a85f324856..a4c5b36288b7 100644 --- a/libs/gui/tests/SurfaceTextureClient_test.cpp +++ b/libs/gui/tests/SurfaceTextureClient_test.cpp @@ -32,6 +32,7 @@ protected: virtual void SetUp() { mST = new SurfaceTexture(123); mSTC = new SurfaceTextureClient(mST); + mANW = mSTC; // We need a valid GL context so we can test updateTexImage() // This initializes EGL and create a dummy GL context with a @@ -69,6 +70,8 @@ protected: virtual void TearDown() { mST.clear(); mSTC.clear(); + mANW.clear(); + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext(mEglDisplay, mEglContext); eglDestroySurface(mEglDisplay, mEglSurface); @@ -86,6 +89,8 @@ protected: sp<SurfaceTexture> mST; sp<SurfaceTextureClient> mSTC; + sp<ANativeWindow> mANW; + EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLContext mEglContext; @@ -97,31 +102,26 @@ TEST_F(SurfaceTextureClientTest, GetISurfaceTextureIsNotNull) { } TEST_F(SurfaceTextureClientTest, QueuesToWindowCompositorIsFalse) { - sp<ANativeWindow> anw(mSTC); int result = -123; - int err = anw->query(anw.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, + int err = mANW->query(mANW.get(), NATIVE_WINDOW_QUEUES_TO_WINDOW_COMPOSER, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(0, result); } TEST_F(SurfaceTextureClientTest, ConcreteTypeIsSurfaceTextureClient) { - sp<ANativeWindow> anw(mSTC); int result = -123; - int err = anw->query(anw.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); + int err = mANW->query(mANW.get(), NATIVE_WINDOW_CONCRETE_TYPE, &result); EXPECT_EQ(NO_ERROR, err); EXPECT_EQ(NATIVE_WINDOW_SURFACE_TEXTURE_CLIENT, result); } TEST_F(SurfaceTextureClientTest, ANativeWindowLockFails) { - sp<ANativeWindow> anw(mSTC); ANativeWindow_Buffer buf; - ASSERT_EQ(BAD_VALUE, ANativeWindow_lock(anw.get(), &buf, NULL)); + ASSERT_EQ(BAD_VALUE, ANativeWindow_lock(mANW.get(), &buf, NULL)); } TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { - sp<ANativeWindow> anw(mSTC); - EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_DISPLAY, dpy); @@ -147,7 +147,7 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { &numConfigs)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, anw.get(), + EGLSurface eglSurface = eglCreateWindowSurface(dpy, myConfig, mANW.get(), NULL); EXPECT_NE(EGL_NO_SURFACE, eglSurface); EXPECT_EQ(EGL_SUCCESS, eglGetError()); @@ -156,269 +156,246 @@ TEST_F(SurfaceTextureClientTest, EglCreateWindowSurfaceSucceeds) { } TEST_F(SurfaceTextureClientTest, BufferGeometryInvalidSizesFail) { - sp<ANativeWindow> anw(mSTC); - - EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1, 0, 0)); - EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 0, -1, 0)); - EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, -1)); - EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), -1, -1, 0)); - EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 0, 8, 0)); - EXPECT_GT(OK, native_window_set_buffers_geometry(anw.get(), 8, 0, 0)); + EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), -1, 0, 0)); + EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), 0, -1, 0)); + EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, -1)); + EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), -1, -1, 0)); + EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), 0, 8, 0)); + EXPECT_GT(OK, native_window_set_buffers_geometry(mANW.get(), 8, 0, 0)); } TEST_F(SurfaceTextureClientTest, DefaultGeometryValues) { - sp<ANativeWindow> anw(mSTC); ANativeWindowBuffer* buf; - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, BufferGeometryCanBeSet) { - sp<ANativeWindow> anw(mSTC); ANativeWindowBuffer* buf; - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, PIXEL_FORMAT_RGB_565)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, PIXEL_FORMAT_RGB_565)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, BufferGeometryDefaultSizeSetFormat) { - sp<ANativeWindow> anw(mSTC); ANativeWindowBuffer* buf; - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, PIXEL_FORMAT_RGB_565)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySetSizeDefaultFormat) { - sp<ANativeWindow> anw(mSTC); ANativeWindowBuffer* buf; - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeUnset) { - sp<ANativeWindow> anw(mSTC); ANativeWindowBuffer* buf; - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, 0)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, 0)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, BufferGeometrySizeCanBeChangedWithoutFormat) { - sp<ANativeWindow> anw(mSTC); ANativeWindowBuffer* buf; - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, PIXEL_FORMAT_RGB_565)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 0, 0, PIXEL_FORMAT_RGB_565)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(1, buf->width); EXPECT_EQ(1, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 16, 8, 0)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 16, 8, 0)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGB_565, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSize) { - sp<ANativeWindow> anw(mSTC); sp<SurfaceTexture> st(mST); ANativeWindowBuffer* buf; EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf)); EXPECT_EQ(16, buf->width); EXPECT_EQ(8, buf->height); EXPECT_EQ(PIXEL_FORMAT_RGBA_8888, buf->format); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf)); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf)); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeAfterDequeue) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); ANativeWindowBuffer* buf[2]; - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1])); - EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); + EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(16, buf[0]->width); EXPECT_EQ(16, buf[1]->width); EXPECT_EQ(8, buf[0]->height); EXPECT_EQ(8, buf[1]->height); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSetDefaultSizeVsGeometry) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); ANativeWindowBuffer* buf[2]; - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - EXPECT_EQ(OK, st->setDefaultBufferSize(16, 8)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + EXPECT_EQ(OK, mST->setDefaultBufferSize(16, 8)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(16, buf[0]->width); EXPECT_EQ(16, buf[1]->width); EXPECT_EQ(8, buf[0]->height); EXPECT_EQ(8, buf[1]->height); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1])); - EXPECT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 12, 24, 0)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); + EXPECT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 12, 24, 0)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); EXPECT_EQ(12, buf[0]->width); EXPECT_EQ(12, buf[1]->width); EXPECT_EQ(24, buf[0]->height); EXPECT_EQ(24, buf[1]->height); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[1])); } TEST_F(SurfaceTextureClientTest, SurfaceTextureTooManyUpdateTexImage) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, st->setSynchronousMode(false)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); + ASSERT_EQ(OK, mST->setSynchronousMode(false)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(OK, st->updateTexImage()); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(OK, st->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(OK, mST->updateTexImage()); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeSlowRetire) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeFastRetire) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeDQQR) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[0]); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[0]); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); EXPECT_NE(buf[0], buf[1]); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); EXPECT_NE(buf[1], buf[2]); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[2]); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[2]); } // XXX: We currently have no hardware that properly handles dequeuing the // buffer that is currently bound to the texture. TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; android_native_buffer_t* firstBuf; - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &firstBuf)); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), firstBuf)); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), firstBuf); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &firstBuf)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), firstBuf)); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), firstBuf); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); EXPECT_NE(buf[0], buf[1]); EXPECT_NE(buf[1], buf[2]); EXPECT_NE(buf[2], buf[0]); @@ -426,41 +403,36 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeDequeueCurrent) } TEST_F(SurfaceTextureClientTest, SurfaceTextureSyncModeMinUndequeued) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); - // We should be able to dequeue all the buffers before we've queued any. - EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); - EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + // We should be able to dequeue all the buffers before we've queued mANWy. + EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); - EXPECT_EQ(OK, st->updateTexImage()); - EXPECT_EQ(st->getCurrentBuffer().get(), buf[1]); + EXPECT_EQ(OK, mST->updateTexImage()); + EXPECT_EQ(mST->getCurrentBuffer().get(), buf[1]); - EXPECT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); + EXPECT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); // Once we've queued a buffer, however we should not be able to dequeue more // than (buffer-count - MIN_UNDEQUEUED_BUFFERS), which is 2 in this case. - EXPECT_EQ(-EBUSY, anw->dequeueBuffer(anw.get(), &buf[1])); + EXPECT_EQ(-EBUSY, mANW->dequeueBuffer(mANW.get(), &buf[1])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, anw->cancelBuffer(anw.get(), buf[2])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mANW->cancelBuffer(mANW.get(), buf[2])); } // XXX: This is not expected to pass until the synchronization hacks are removed // from the SurfaceTexture class. TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); - class MyThread : public Thread { - sp<SurfaceTexture> st; + sp<SurfaceTexture> mST; EGLContext ctx; EGLSurface sur; EGLDisplay dpy; @@ -470,14 +442,14 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { eglMakeCurrent(dpy, sur, sur, ctx); usleep(20000); Mutex::Autolock _l(mLock); - st->updateTexImage(); + mST->updateTexImage(); mBufferRetired = true; eglMakeCurrent(dpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); return false; } public: - MyThread(const sp<SurfaceTexture>& st) - : st(st), mBufferRetired(false) { + MyThread(const sp<SurfaceTexture>& mST) + : mST(mST), mBufferRetired(false) { ctx = eglGetCurrentContext(); sur = eglGetCurrentSurface(EGL_DRAW); dpy = eglGetCurrentDisplay(); @@ -493,37 +465,35 @@ TEST_F(SurfaceTextureClientTest, DISABLED_SurfaceTextureSyncModeWaitRetire) { }; android_native_buffer_t* buf[3]; - ASSERT_EQ(OK, st->setSynchronousMode(true)); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 3)); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 3)); // dequeue/queue/update so we have a current buffer - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - st->updateTexImage(); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + mST->updateTexImage(); - MyThread* thread = new MyThread(st); + MyThread* thread = new MyThread(mST); sp<Thread> threadBase(thread); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); thread->run(); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[1])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[1])); - //ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[2])); - //ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[2])); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[1])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[1])); + //ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[2])); + //ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[2])); thread->bufferDequeued(); thread->requestExitAndWait(); } TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; float mtx[16] = {}; - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, st->updateTexImage()); - st->getTransformMatrix(mtx); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mST->updateTexImage()); + mST->getTransformMatrix(mtx); EXPECT_EQ(1.f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); @@ -547,16 +517,14 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixReturnsVerticalFlip) { } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; float mtx[16] = {}; - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, st->updateTexImage()); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers - st->getTransformMatrix(mtx); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mST->updateTexImage()); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers + mST->getTransformMatrix(mtx); EXPECT_EQ(1.f, mtx[0]); EXPECT_EQ(0.f, mtx[1]); @@ -580,8 +548,6 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffers) } TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWithCrop) { - sp<ANativeWindow> anw(mSTC); - sp<SurfaceTexture> st(mST); android_native_buffer_t* buf[3]; float mtx[16] = {}; android_native_rect_t crop; @@ -590,14 +556,14 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi crop.right = 5; crop.bottom = 5; - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 4)); - ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 8, 8, 0)); - ASSERT_EQ(OK, anw->dequeueBuffer(anw.get(), &buf[0])); - ASSERT_EQ(OK, native_window_set_crop(anw.get(), &crop)); - ASSERT_EQ(OK, anw->queueBuffer(anw.get(), buf[0])); - ASSERT_EQ(OK, st->updateTexImage()); - ASSERT_EQ(OK, native_window_set_buffer_count(anw.get(), 6)); // frees buffers - st->getTransformMatrix(mtx); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 4)); + ASSERT_EQ(OK, native_window_set_buffers_geometry(mANW.get(), 8, 8, 0)); + ASSERT_EQ(OK, mANW->dequeueBuffer(mANW.get(), &buf[0])); + ASSERT_EQ(OK, native_window_set_crop(mANW.get(), &crop)); + ASSERT_EQ(OK, mANW->queueBuffer(mANW.get(), buf[0])); + ASSERT_EQ(OK, mST->updateTexImage()); + ASSERT_EQ(OK, native_window_set_buffer_count(mANW.get(), 6)); // frees buffers + mST->getTransformMatrix(mtx); // This accounts for the 1 texel shrink for each edge that's included in the // transform matrix to avoid texturing outside the crop region. @@ -622,4 +588,29 @@ TEST_F(SurfaceTextureClientTest, GetTransformMatrixSucceedsAfterFreeingBuffersWi EXPECT_EQ(1.f, mtx[15]); } +// This test verifies that the buffer format can be queried immediately after +// it is set. +TEST_F(SurfaceTextureClientTest, DISABLED_QueryFormatAfterSettingWorks) { + sp<ANativeWindow> anw(mSTC); + int fmts[] = { + // RGBA_8888 should not come first, as it's the default + HAL_PIXEL_FORMAT_RGBX_8888, + HAL_PIXEL_FORMAT_RGBA_8888, + HAL_PIXEL_FORMAT_RGB_888, + HAL_PIXEL_FORMAT_RGB_565, + HAL_PIXEL_FORMAT_BGRA_8888, + HAL_PIXEL_FORMAT_RGBA_5551, + HAL_PIXEL_FORMAT_RGBA_4444, + HAL_PIXEL_FORMAT_YV12, + }; + + const int numFmts = (sizeof(fmts) / sizeof(fmts[0])); + for (int i = 0; i < numFmts; i++) { + int fmt = -1; + ASSERT_EQ(OK, native_window_set_buffers_geometry(anw.get(), 0, 0, fmts[i])); + ASSERT_EQ(OK, anw->query(anw.get(), NATIVE_WINDOW_FORMAT, &fmt)); + EXPECT_EQ(fmts[i], fmt); + } +} + } // namespace android diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 56c1702929bd..50af3bbbacd0 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -46,8 +46,6 @@ protected: } virtual void SetUp() { - EGLBoolean returnValue; - mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); @@ -59,9 +57,8 @@ protected: RecordProperty("EglVersionMajor", majorVersion); RecordProperty("EglVersionMajor", minorVersion); - EGLConfig myConfig = {0}; EGLint numConfigs = 0; - EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig, + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, 1, &numConfigs)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); @@ -88,12 +85,12 @@ protected: ASSERT_TRUE(mSurfaceControl->isValid()); ASSERT_EQ(NO_ERROR, mComposerClient->openTransaction()); - ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(30000)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); ASSERT_EQ(NO_ERROR, mComposerClient->closeTransaction()); sp<ANativeWindow> window = mSurfaceControl->getSurface(); - mEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig, + mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, window.get(), NULL); } else { EGLint pbufferAttribs[] = { @@ -101,13 +98,13 @@ protected: EGL_HEIGHT, getSurfaceHeight(), EGL_NONE }; - mEglSurface = eglCreatePbufferSurface(mEglDisplay, myConfig, + mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, pbufferAttribs); } ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_SURFACE, mEglSurface); - mEglContext = eglCreateContext(mEglDisplay, myConfig, EGL_NO_CONTEXT, + mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, getContextAttribs()); ASSERT_EQ(EGL_SUCCESS, eglGetError()); ASSERT_NE(EGL_NO_CONTEXT, mEglContext); @@ -329,6 +326,7 @@ protected: EGLDisplay mEglDisplay; EGLSurface mEglSurface; EGLContext mEglContext; + EGLConfig mGlConfig; }; // XXX: Code above this point should live elsewhere @@ -401,6 +399,18 @@ protected: glBindTexture(GL_TEXTURE_EXTERNAL_OES, TEX_ID); ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + // XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as + // they're setting the defautls for that target, but when hacking things + // to use GL_TEXTURE_2D they are needed to achieve the same behavior. + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + GLfloat texMatrix[16]; mST->getTransformMatrix(texMatrix); glUniformMatrix4fv(mTexMatrixHandle, 1, GL_FALSE, texMatrix); @@ -474,12 +484,26 @@ void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, } } +void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { + const size_t PIXEL_SIZE = 4; + for (int x = 0; x < w; x++) { + for (int y = 0; y < h; y++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + for (int c = 0; c < 4; c++) { + int parityX = (x / (1 << (c+2))) & 1; + int parityY = (y / (1 << (c+2))) & 1; + buf[offset + c] = (parityX ^ parityY) ? 231 : 35; + } + } + } +} + TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { - const int yuvTexWidth = 64; - const int yuvTexHeight = 66; + const int texWidth = 64; + const int texHeight = 66; ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12)); + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); @@ -493,7 +517,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride()); + fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); @@ -523,11 +547,11 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { // I just copied them from the npot test above and haven't bothered to figure // out the correct values. TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) { - const int yuvTexWidth = 64; - const int yuvTexHeight = 64; + const int texWidth = 64; + const int texHeight = 64; ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12)); + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); @@ -541,7 +565,7 @@ TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) { // Fill the buffer with the a checkerboard pattern uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillYV12Buffer(img, yuvTexWidth, yuvTexHeight, buf->getStride()); + fillYV12Buffer(img, texWidth, texHeight, buf->getStride()); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); @@ -567,11 +591,11 @@ TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledYV12BufferPow2) { } TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { - const int yuvTexWidth = 64; - const int yuvTexHeight = 66; + const int texWidth = 64; + const int texHeight = 66; ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - yuvTexWidth, yuvTexHeight, HAL_PIXEL_FORMAT_YV12)); + texWidth, texHeight, HAL_PIXEL_FORMAT_YV12)); ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); @@ -579,8 +603,8 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { {4, 6, 22, 36}, {0, 6, 22, 36}, {4, 0, 22, 36}, - {4, 6, yuvTexWidth, 36}, - {4, 6, 22, yuvTexHeight}, + {4, 6, texWidth, 36}, + {4, 6, 22, texHeight}, }; for (int i = 0; i < 5; i++) { @@ -599,7 +623,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { uint8_t* img = NULL; buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); - fillYV12BufferRect(img, yuvTexWidth, yuvTexHeight, buf->getStride(), crop); + fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop); buf->unlock(); ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); @@ -625,6 +649,189 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { } } +// XXX: This test is disabled because there are currently no drivers that can +// handle RGBA textures with the GL_TEXTURE_EXTERNAL_OES target. +TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledRGBABufferNpot) { + const int texWidth = 64; + const int texHeight = 66; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillRGBA8Buffer(img, texWidth, texHeight, buf->getStride()); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + mST->updateTexImage(); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel(63, 0, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel( 0, 63, 35, 35, 35, 35)); + + EXPECT_TRUE(checkPixel(15, 10, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(24, 63, 35, 231, 231, 35)); + EXPECT_TRUE(checkPixel(19, 40, 87, 179, 35, 35)); + EXPECT_TRUE(checkPixel(38, 30, 231, 35, 35, 35)); + EXPECT_TRUE(checkPixel(42, 54, 35, 35, 35, 231)); + EXPECT_TRUE(checkPixel(37, 33, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(31, 8, 231, 35, 35, 231)); + EXPECT_TRUE(checkPixel(36, 47, 231, 35, 231, 231)); + EXPECT_TRUE(checkPixel(24, 63, 35, 231, 231, 35)); + EXPECT_TRUE(checkPixel(48, 3, 231, 231, 35, 35)); + EXPECT_TRUE(checkPixel(54, 50, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(24, 25, 191, 191, 231, 231)); + EXPECT_TRUE(checkPixel(10, 9, 93, 93, 231, 231)); + EXPECT_TRUE(checkPixel(29, 4, 35, 35, 35, 231)); + EXPECT_TRUE(checkPixel(56, 31, 35, 231, 231, 35)); + EXPECT_TRUE(checkPixel(58, 55, 35, 35, 231, 231)); +} + +// XXX: This test is disabled because there are currently no drivers that can +// handle RGBA textures with the GL_TEXTURE_EXTERNAL_OES target. +TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromCpuFilledRGBABufferPow2) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with the a checkerboard pattern + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillRGBA8Buffer(img, texWidth, texHeight, buf->getStride()); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + mST->updateTexImage(); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel(63, 0, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel(63, 63, 231, 231, 231, 231)); + EXPECT_TRUE(checkPixel( 0, 63, 35, 35, 35, 35)); + + EXPECT_TRUE(checkPixel(12, 46, 231, 231, 231, 35)); + EXPECT_TRUE(checkPixel(16, 1, 231, 231, 35, 231)); + EXPECT_TRUE(checkPixel(21, 12, 231, 35, 35, 231)); + EXPECT_TRUE(checkPixel(26, 51, 231, 35, 231, 35)); + EXPECT_TRUE(checkPixel( 5, 32, 35, 231, 231, 35)); + EXPECT_TRUE(checkPixel(13, 8, 35, 231, 231, 231)); + EXPECT_TRUE(checkPixel(46, 3, 35, 35, 231, 35)); + EXPECT_TRUE(checkPixel(30, 33, 35, 35, 35, 35)); + EXPECT_TRUE(checkPixel( 6, 52, 231, 231, 35, 35)); + EXPECT_TRUE(checkPixel(55, 33, 35, 231, 35, 231)); + EXPECT_TRUE(checkPixel(16, 29, 35, 35, 231, 231)); + EXPECT_TRUE(checkPixel( 1, 30, 35, 35, 35, 231)); + EXPECT_TRUE(checkPixel(41, 37, 35, 35, 231, 231)); + EXPECT_TRUE(checkPixel(46, 29, 231, 231, 35, 35)); + EXPECT_TRUE(checkPixel(15, 25, 35, 231, 35, 231)); + EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); +} + +// XXX: This test is disabled because there are currently no drivers that can +// handle RGBA textures with the GL_TEXTURE_EXTERNAL_OES target. +TEST_F(SurfaceTextureGLTest, DISABLED_TexturingFromGLFilledRGBABufferPow2) { + const int texWidth = 64; + const int texHeight = 64; + + mST->setDefaultBufferSize(texWidth, texHeight); + + // Do the producer side of things + EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, + mANW.get(), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + glClearColor(0.6, 0.6, 0.6, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(4, 4, 4, 4); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(24, 48, 4, 4); + glClearColor(0.0, 1.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(37, 17, 4, 4); + glClearColor(0.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + eglSwapBuffers(mEglDisplay, stcEglSurface); + + // Do the consumer side of things + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + glDisable(GL_SCISSOR_TEST); + + mST->updateTexImage(); + + glClearColor(0.2, 0.2, 0.2, 0.2); + glClear(GL_COLOR_BUFFER_BIT); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); + + EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255)); + EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255)); + EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255)); + EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); +} + /* * This test is for testing GL -> GL texture streaming via SurfaceTexture. It * contains functionality to create a producer thread that will perform GL diff --git a/libs/hwui/LayerRenderer.cpp b/libs/hwui/LayerRenderer.cpp index f316ba79a1ea..146e7894aa73 100644 --- a/libs/hwui/LayerRenderer.cpp +++ b/libs/hwui/LayerRenderer.cpp @@ -315,5 +315,95 @@ void LayerRenderer::destroyLayerDeferred(Layer* layer) { } } +bool LayerRenderer::copyLayer(Layer* layer, SkBitmap* bitmap) { + Caches& caches = Caches::getInstance(); + if (layer && layer->isTextureLayer && bitmap->width() <= caches.maxTextureSize && + bitmap->height() <= caches.maxTextureSize) { + + GLuint fbo = caches.fboCache.get(); + if (!fbo) { + LOGW("Could not obtain an FBO"); + return false; + } + + GLuint texture; + GLuint previousFbo; + + GLenum format; + GLenum type; + + switch (bitmap->config()) { + case SkBitmap::kA8_Config: + format = GL_ALPHA; + type = GL_UNSIGNED_BYTE; + break; + case SkBitmap::kRGB_565_Config: + format = GL_RGB; + type = GL_UNSIGNED_SHORT_5_6_5; + break; + case SkBitmap::kARGB_4444_Config: + format = GL_RGBA; + type = GL_UNSIGNED_SHORT_4_4_4_4; + break; + case SkBitmap::kARGB_8888_Config: + default: + format = GL_RGBA; + type = GL_UNSIGNED_BYTE; + break; + } + + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint*) &previousFbo); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + + glGenTextures(1, &texture); + + glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + + glTexImage2D(GL_TEXTURE_2D, 0, format, bitmap->width(), bitmap->height(), + 0, format, type, NULL); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, texture, 0); + + glBindTexture(GL_TEXTURE_2D, layer->texture); + + float alpha = layer->alpha; + SkXfermode::Mode mode = layer->mode; + + layer->mode = SkXfermode::kSrc_Mode; + layer->alpha = 255; + layer->fbo = fbo; + + LayerRenderer renderer(layer); + renderer.setViewport(bitmap->width(), bitmap->height()); + renderer.OpenGLRenderer::prepareDirty(0.0f, 0.0f, + bitmap->width(), bitmap->height(), !layer->blend); + + Rect bounds; + bounds.set(0.0f, 0.0f, bitmap->width(), bitmap->height()); + renderer.drawTextureLayer(layer, bounds); + + SkAutoLockPixels alp(*bitmap); + glReadPixels(0, 0, bitmap->width(), bitmap->height(), format, type, bitmap->getPixels()); + + glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); + + layer->mode = mode; + layer->alpha = alpha; + layer->fbo = 0; + glDeleteTextures(1, &texture); + caches.fboCache.put(fbo); + + return true; + } + return false; +} + }; // namespace uirenderer }; // namespace android diff --git a/libs/hwui/LayerRenderer.h b/libs/hwui/LayerRenderer.h index 59cab963ca0a..797dfc63e5bb 100644 --- a/libs/hwui/LayerRenderer.h +++ b/libs/hwui/LayerRenderer.h @@ -20,6 +20,8 @@ #include "OpenGLRenderer.h" #include "Layer.h" +#include <SkBitmap.h> + namespace android { namespace uirenderer { @@ -60,6 +62,7 @@ public: GLenum renderTarget, float* transform); static void destroyLayer(Layer* layer); static void destroyLayerDeferred(Layer* layer); + static bool copyLayer(Layer* layer, SkBitmap* bitmap); private: void generateMesh(); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index b9e3ddc87b68..0a3d509058fc 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -178,6 +178,14 @@ protected: return 0; } + /** + * Renders the specified layer as a textured quad. + * + * @param layer The layer to render + * @param rect The bounds of the layer + */ + void drawTextureLayer(Layer* layer, const Rect& rect); + private: /** * Saves the current state of the renderer as a new snapshot. @@ -256,14 +264,6 @@ private: void clearLayerRegions(); /** - * Renders the specified layer as a textured quad. - * - * @param layer The layer to render - * @param rect The bounds of the layer - */ - void drawTextureLayer(Layer* layer, const Rect& rect); - - /** * Mark the layer as dirty at the specified coordinates. The coordinates * are transformed with the supplied matrix. */ diff --git a/libs/rs/RenderScriptDefines.h b/libs/rs/RenderScriptDefines.h index ee9645c37bcd..d092520feb81 100644 --- a/libs/rs/RenderScriptDefines.h +++ b/libs/rs/RenderScriptDefines.h @@ -110,12 +110,12 @@ enum RsAllocationMipmapControl { }; enum RsAllocationCubemapFace { - RS_ALLOCATION_CUBMAP_FACE_POSITVE_X = 0, - RS_ALLOCATION_CUBMAP_FACE_NEGATIVE_X = 1, - RS_ALLOCATION_CUBMAP_FACE_POSITVE_Y = 2, - RS_ALLOCATION_CUBMAP_FACE_NEGATIVE_Y = 3, - RS_ALLOCATION_CUBMAP_FACE_POSITVE_Z = 4, - RS_ALLOCATION_CUBMAP_FACE_NEGATIVE_Z = 5 + RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X = 0, + RS_ALLOCATION_CUBEMAP_FACE_NEGATIVE_X = 1, + RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_Y = 2, + RS_ALLOCATION_CUBEMAP_FACE_NEGATIVE_Y = 3, + RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_Z = 4, + RS_ALLOCATION_CUBEMAP_FACE_NEGATIVE_Z = 5 }; enum RsDataType { diff --git a/libs/rs/driver/rsdAllocation.cpp b/libs/rs/driver/rsdAllocation.cpp index 2e13e9d0b5a0..8bfc1857d4bf 100644 --- a/libs/rs/driver/rsdAllocation.cpp +++ b/libs/rs/driver/rsdAllocation.cpp @@ -378,6 +378,32 @@ void rsdAllocationData3D(const Context *rsc, const Allocation *alloc, } +void rsdAllocationData1D_alloc(const android::renderscript::Context *rsc, + const android::renderscript::Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstLod, uint32_t count, + const android::renderscript::Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcLod){ +} + +void rsdAllocationData2D_alloc(const android::renderscript::Context *rsc, + const android::renderscript::Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod, + RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h, + const android::renderscript::Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod, + RsAllocationCubemapFace srcFace) { +} + +void rsdAllocationData3D_alloc(const android::renderscript::Context *rsc, + const android::renderscript::Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, uint32_t dstZoff, + uint32_t dstLod, RsAllocationCubemapFace dstFace, + uint32_t w, uint32_t h, uint32_t d, + const android::renderscript::Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, uint32_t srcZoff, + uint32_t srcLod, RsAllocationCubemapFace srcFace) { +} + void rsdAllocationElementData1D(const Context *rsc, const Allocation *alloc, uint32_t x, const void *data, uint32_t cIdx, uint32_t sizeBytes) { diff --git a/libs/rs/driver/rsdAllocation.h b/libs/rs/driver/rsdAllocation.h index d7385cee64cf..7555c4ab0711 100644 --- a/libs/rs/driver/rsdAllocation.h +++ b/libs/rs/driver/rsdAllocation.h @@ -80,6 +80,27 @@ void rsdAllocationData3D(const android::renderscript::Context *rsc, uint32_t lod, RsAllocationCubemapFace face, uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes); +void rsdAllocationData1D_alloc(const android::renderscript::Context *rsc, + const android::renderscript::Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstLod, uint32_t count, + const android::renderscript::Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcLod); +void rsdAllocationData2D_alloc(const android::renderscript::Context *rsc, + const android::renderscript::Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod, + RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h, + const android::renderscript::Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod, + RsAllocationCubemapFace srcFace); +void rsdAllocationData3D_alloc(const android::renderscript::Context *rsc, + const android::renderscript::Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, uint32_t dstZoff, + uint32_t dstLod, RsAllocationCubemapFace dstFace, + uint32_t w, uint32_t h, uint32_t d, + const android::renderscript::Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, uint32_t srcZoff, + uint32_t srcLod, RsAllocationCubemapFace srcFace); + void rsdAllocationElementData1D(const android::renderscript::Context *rsc, const android::renderscript::Allocation *alloc, uint32_t x, diff --git a/libs/rs/driver/rsdCore.cpp b/libs/rs/driver/rsdCore.cpp index 01cc369a8e01..38f6895f26fd 100644 --- a/libs/rs/driver/rsdCore.cpp +++ b/libs/rs/driver/rsdCore.cpp @@ -74,6 +74,9 @@ static RsdHalFunctions FunctionTable = { rsdAllocationData1D, rsdAllocationData2D, rsdAllocationData3D, + rsdAllocationData1D_alloc, + rsdAllocationData2D_alloc, + rsdAllocationData3D_alloc, rsdAllocationElementData1D, rsdAllocationElementData2D }, diff --git a/libs/rs/driver/rsdRuntimeStubs.cpp b/libs/rs/driver/rsdRuntimeStubs.cpp index 9cbff9526b16..bd8b3c325dce 100644 --- a/libs/rs/driver/rsdRuntimeStubs.cpp +++ b/libs/rs/driver/rsdRuntimeStubs.cpp @@ -88,6 +88,26 @@ static void SC_AllocationSyncAll(Allocation *a) { rsrAllocationSyncAll(rsc, sc, a, RS_ALLOCATION_USAGE_SCRIPT); } +static void SC_AllocationCopy1DRange(Allocation *dstAlloc, + uint32_t dstOff, + uint32_t dstMip, + uint32_t count, + Allocation *srcAlloc, + uint32_t srcOff, uint32_t srcMip) { + GET_TLS(); +} + +static void SC_AllocationCopy2DRange(Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, + uint32_t dstMip, uint32_t dstFace, + uint32_t width, uint32_t height, + Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, + uint32_t srcMip, uint32_t srcFace) { + GET_TLS(); +} + + const Allocation * SC_getAllocation(const void *ptr) { GET_TLS(); return rsrGetAllocation(rsc, sc, ptr); @@ -566,8 +586,11 @@ static RsdSymbolTable gSyms[] = { { "_Z21rsAllocationMarkDirty13rs_allocation", (void *)&SC_AllocationSyncAll, true }, { "_Z20rsgAllocationSyncAll13rs_allocation", (void *)&SC_AllocationSyncAll, false }, { "_Z20rsgAllocationSyncAll13rs_allocationj", (void *)&SC_AllocationSyncAll2, false }, + { "_Z20rsgAllocationSyncAll13rs_allocation24rs_allocation_usage_type", (void *)&SC_AllocationSyncAll2, false }, { "_Z15rsGetAllocationPKv", (void *)&SC_GetAllocation, true }, + { "_Z23rsAllocationCopy1DRange13rs_allocationjjjS_jj", (void *)&SC_AllocationCopy1DRange, false }, + { "_Z23rsAllocationCopy2DRange13rs_allocationjjj26rs_allocation_cubemap_facejjS_jjjS0_", (void *)&SC_AllocationCopy2DRange, false }, // Messaging diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 87d764d2f006..963a6e78dc29 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -209,6 +209,21 @@ AllocationResize2D { param uint32_t dimY } +AllocationCopy2DRange { + param RsAllocation dest + param uint32_t destXoff + param uint32_t destYoff + param uint32_t destMip + param uint32_t destFace + param uint32_t width + param uint32_t height + param RsAllocation src + param uint32_t srcXoff + param uint32_t srcYoff + param uint32_t srcMip + param uint32_t srcFace + } + SamplerCreate { param RsSamplerValue magFilter param RsSamplerValue minFilter diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index bff36603e9d9..f3e0c0a75291 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -532,6 +532,23 @@ RsAllocation rsi_AllocationCubeCreateFromBitmap(Context *rsc, RsType vtype, return texAlloc; } +void rsi_AllocationCopy2DRange(Context *rsc, + RsAllocation dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, + uint32_t dstMip, uint32_t dstFace, + uint32_t width, uint32_t height, + RsAllocation srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, + uint32_t srcMip, uint32_t srcFace) { + Allocation *dst = static_cast<Allocation *>(dstAlloc); + Allocation *src= static_cast<Allocation *>(srcAlloc); + rsc->mHal.funcs.allocation.allocData2D(rsc, dst, dstXoff, dstYoff, dstMip, + (RsAllocationCubemapFace)dstFace, + width, height, + src, srcXoff, srcYoff,srcMip, + (RsAllocationCubemapFace)srcFace); +} + } } diff --git a/libs/rs/rsRuntime.h b/libs/rs/rsRuntime.h index 6d45285e6d9f..cb962a815dc3 100644 --- a/libs/rs/rsRuntime.h +++ b/libs/rs/rsRuntime.h @@ -85,6 +85,21 @@ void rsrMeshComputeBoundingBox(Context *, Script *, Mesh *, void rsrColor(Context *, Script *, float r, float g, float b, float a); void rsrFinish(Context *, Script *); void rsrAllocationSyncAll(Context *, Script *, Allocation *); + +void rsrAllocationCopy1DRange(Context *, Allocation *dstAlloc, + uint32_t dstOff, + uint32_t dstMip, + uint32_t count, + Allocation *srcAlloc, + uint32_t srcOff, uint32_t srcMip); +void rsrAllocationCopy2DRange(Context *, Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, + uint32_t dstMip, uint32_t dstFace, + uint32_t width, uint32_t height, + Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, + uint32_t srcMip, uint32_t srcFace); + void rsrClearColor(Context *, Script *, float r, float g, float b, float a); void rsrClearDepth(Context *, Script *, float v); uint32_t rsrGetWidth(Context *, Script *); diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp index 4ee0a3ec4e5e..ec15bc0233a5 100644 --- a/libs/rs/rsScriptC_Lib.cpp +++ b/libs/rs/rsScriptC_Lib.cpp @@ -164,6 +164,29 @@ void rsrAllocationSyncAll(Context *rsc, Script *sc, Allocation *a, RsAllocationU a->syncAll(rsc, usage); } +void rsrAllocationCopy1DRange(Context *rsc, Allocation *dstAlloc, + uint32_t dstOff, + uint32_t dstMip, + uint32_t count, + Allocation *srcAlloc, + uint32_t srcOff, uint32_t srcMip) { + rsi_AllocationCopy2DRange(rsc, dstAlloc, dstOff, 0, + dstMip, 0, count, 1, + srcAlloc, srcOff, 0, srcMip, 0); +} + +void rsrAllocationCopy2DRange(Context *rsc, Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, + uint32_t dstMip, uint32_t dstFace, + uint32_t width, uint32_t height, + Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, + uint32_t srcMip, uint32_t srcFace) { + rsi_AllocationCopy2DRange(rsc, dstAlloc, dstXoff, dstYoff, + dstMip, dstFace, width, height, + srcAlloc, srcXoff, srcYoff, srcMip, srcFace); +} + const Allocation * rsrGetAllocation(Context *rsc, Script *s, const void *ptr) { ScriptC *sc = (ScriptC *)s; return sc->ptrToAllocation(ptr); diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h index 7bb09bbf5597..928dca5376ed 100644 --- a/libs/rs/rs_hal.h +++ b/libs/rs/rs_hal.h @@ -112,6 +112,27 @@ typedef struct { uint32_t lod, RsAllocationCubemapFace face, uint32_t w, uint32_t h, uint32_t d, const void *data, uint32_t sizeBytes); + // Allocation to allocation copies + void (*allocData1D)(const Context *rsc, + const Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstLod, uint32_t count, + const Allocation *srcAlloc, uint32_t srcXoff, uint32_t srcLod); + void (*allocData2D)(const Context *rsc, + const Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, uint32_t dstLod, + RsAllocationCubemapFace dstFace, uint32_t w, uint32_t h, + const Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, uint32_t srcLod, + RsAllocationCubemapFace srcFace); + void (*allocData3D)(const Context *rsc, + const Allocation *dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, uint32_t dstZoff, + uint32_t dstLod, RsAllocationCubemapFace dstFace, + uint32_t w, uint32_t h, uint32_t d, + const Allocation *srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, uint32_t srcZoff, + uint32_t srcLod, RsAllocationCubemapFace srcFace); + void (*elementData1D)(const Context *rsc, const Allocation *alloc, uint32_t x, const void *data, uint32_t elementOff, uint32_t sizeBytes); void (*elementData2D)(const Context *rsc, const Allocation *alloc, uint32_t x, uint32_t y, diff --git a/libs/rs/scriptc/rs_graphics.rsh b/libs/rs/scriptc/rs_graphics.rsh index d53bc952ded5..9a8a4e66a03d 100644 --- a/libs/rs/scriptc/rs_graphics.rsh +++ b/libs/rs/scriptc/rs_graphics.rsh @@ -144,6 +144,17 @@ extern void __attribute__((overloadable)) rsgAllocationSyncAll(rs_allocation alloc); /** + * Sync the contents of an allocation from memory space + * specified by source. + * + * @param alloc + * @param source + */ +extern void __attribute__((overloadable)) + rsgAllocationSyncAll(rs_allocation alloc, + rs_allocation_usage_type source); + +/** * Low performance utility function for drawing a simple rectangle. Not * intended for drawing large quantities of geometry. * diff --git a/libs/rs/scriptc/rs_math.rsh b/libs/rs/scriptc/rs_math.rsh index 6e3cfdb77529..584317e0476c 100644 --- a/libs/rs/scriptc/rs_math.rsh +++ b/libs/rs/scriptc/rs_math.rsh @@ -136,6 +136,58 @@ extern uint32_t __attribute__((overloadable)) extern uint32_t __attribute__((overloadable)) rsAllocationGetDimFaces(rs_allocation); +/** + * Copy part of an allocation from another allocation. + * + * @param dstAlloc Allocation to copy data into. + * @param dstOff The offset of the first element to be copied in + * the destination allocation. + * @param dstMip Mip level in the destination allocation. + * @param count The number of elements to be copied. + * @param srcAlloc The source data allocation. + * @param srcOff The offset of the first element in data to be + * copied in the source allocation. + * @param srcMip Mip level in the source allocation. + */ +extern void __attribute__((overloadable)) + rsAllocationCopy1DRange(rs_allocation dstAlloc, + uint32_t dstOff, uint32_t dstMip, + uint32_t count, + rs_allocation srcAlloc, + uint32_t srcOff, uint32_t srcMip); + +/** + * Copy a rectangular region into the allocation from another + * allocation. + * + * @param dstAlloc allocation to copy data into. + * @param dstXoff X offset of the region to update in the + * destination allocation. + * @param dstYoff Y offset of the region to update in the + * destination allocation. + * @param dstMip Mip level in the destination allocation. + * @param dstFace Cubemap face of the destination allocation, + * ignored for allocations that aren't cubemaps. + * @param width Width of the incoming region to update. + * @param height Height of the incoming region to update. + * @param srcAlloc The source data allocation. + * @param srcXoff X offset in data of the source allocation. + * @param srcYoff Y offset in data of the source allocation. + * @param srcMip Mip level in the source allocation. + * @param srcFace Cubemap face of the source allocation, + * ignored for allocations that aren't cubemaps. + */ +extern void __attribute__((overloadable)) + rsAllocationCopy2DRange(rs_allocation dstAlloc, + uint32_t dstXoff, uint32_t dstYoff, + uint32_t dstMip, + rs_allocation_cubemap_face dstFace, + uint32_t width, uint32_t height, + rs_allocation srcAlloc, + uint32_t srcXoff, uint32_t srcYoff, + uint32_t srcMip, + rs_allocation_cubemap_face srcFace); + // Extract a single element from an allocation. extern const void * __attribute__((overloadable)) rsGetElementAt(rs_allocation, uint32_t x); diff --git a/libs/rs/scriptc/rs_types.rsh b/libs/rs/scriptc/rs_types.rsh index d9f4b4b9b948..536d1f04da48 100644 --- a/libs/rs/scriptc/rs_types.rsh +++ b/libs/rs/scriptc/rs_types.rsh @@ -88,4 +88,21 @@ typedef float4 rs_quaternion; #define RS_PACKED __attribute__((packed, aligned(4))) +typedef enum { + RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_X = 0, + RS_ALLOCATION_CUBEMAP_FACE_NEGATIVE_X = 1, + RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_Y = 2, + RS_ALLOCATION_CUBEMAP_FACE_NEGATIVE_Y = 3, + RS_ALLOCATION_CUBEMAP_FACE_POSITIVE_Z = 4, + RS_ALLOCATION_CUBEMAP_FACE_NEGATIVE_Z = 5 +} rs_allocation_cubemap_face; + +typedef enum { + RS_ALLOCATION_USAGE_SCRIPT = 0x0001, + RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE = 0x0002, + RS_ALLOCATION_USAGE_GRAPHICS_VERTEX = 0x0004, + RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS = 0x0008, + RS_ALLOCATION_USAGE_GRAPHICS_RENDER_TARGET = 0x0010 +} rs_allocation_usage_type; + #endif diff --git a/libs/utils/Threads.cpp b/libs/utils/Threads.cpp index c74822866340..15bb1d22627c 100644 --- a/libs/utils/Threads.cpp +++ b/libs/utils/Threads.cpp @@ -335,10 +335,17 @@ int androidSetThreadPriority(pid_t tid, int pri) pthread_once(&gDoSchedulingGroupOnce, checkDoSchedulingGroup); if (gDoSchedulingGroup) { + // set_sched_policy does not support tid == 0 + int policy_tid; + if (tid == 0) { + policy_tid = androidGetTid(); + } else { + policy_tid = tid; + } if (pri >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(tid, SP_BACKGROUND); + rc = set_sched_policy(policy_tid, SP_BACKGROUND); } else if (getpriority(PRIO_PROCESS, tid) >= ANDROID_PRIORITY_BACKGROUND) { - rc = set_sched_policy(tid, SP_FOREGROUND); + rc = set_sched_policy(policy_tid, SP_FOREGROUND); } } diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index e34d75ccff32..255773067919 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -52,6 +52,7 @@ import java.lang.ref.WeakReference; * <li><a href="#StateDiagram">State Diagram</a> * <li><a href="#Valid_and_Invalid_States">Valid and Invalid States</a> * <li><a href="#Permissions">Permissions</a> + * <li><a href="#Callbacks">Register informational and error callbacks</a> * </ol> * * <a name="StateDiagram"></a> @@ -459,6 +460,25 @@ import java.lang.ref.WeakReference; * android.R.styleable#AndroidManifestUsesPermission <uses-permission>} * element. * + * <a name="Callbacks"></a> + * <h3>Callbacks</h3> + * <p>Applications may want to register for informational and error + * events in order to be informed of some internal state update and + * possible runtime errors during playback or streaming. Registration for + * these events is done by properly setting the appropriate listeners (via calls + * to + * {@link #setOnPreparedListener(OnPreparedListener)}setOnPreparedListener, + * {@link #setOnVideoSizeChangedListener(OnVideoSizeChangedListener)}setOnVideoSizeChangedListener, + * {@link #setOnSeekCompleteListener(OnSeekCompleteListener)}setOnSeekCompleteListener, + * {@link #setOnCompletionListener(OnCompletionListener)}setOnCompletionListener, + * {@link #setOnBufferingUpdateListener(OnBufferingUpdateListener)}setOnBufferingUpdateListener, + * {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener, + * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener, etc). + * In order to receive the respective callback + * associated with these listeners, applications are required to create + * MediaPlayer objects on a thread with its own Looper running (main UI + * thread by default has a Looper running). + * */ public class MediaPlayer { diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 1478a6dddf58..dd4511163c9e 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -51,6 +51,16 @@ import java.lang.ref.WeakReference; * recorder.release(); // Now the object cannot be reused * </pre> * + * <p>Applications may want to register for informational and error + * events in order to be informed of some internal update and possible + * runtime errors during recording. Registration for such events is + * done by setting the appropriate listeners (via calls + * (to {@link #setOnInfoListener(OnInfoListener)}setOnInfoListener and/or + * {@link #setOnErrorListener(OnErrorListener)}setOnErrorListener). + * In order to receive the respective callback associated with these listeners, + * applications are required to create MediaRecorder objects on threads with a + * Looper running (the main UI thread by default already has a Looper running). + * * <p>See the <a href="{@docRoot}guide/topics/media/index.html">Audio and Video</a> * documentation for additional help with using MediaRecorder. * <p>Note: Currently, MediaRecorder does not work on the emulator. diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 3689557bbf92..cc2257477071 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -486,7 +486,8 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { if (mForceRead) { LOGV("dataCallbackTimestamp timelapse: forced read"); mForceRead = false; - *timestampUs = mLastFrameTimestampUs; + *timestampUs = + mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs; return false; } } diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index b8ae79c757d9..28add18333c7 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -1981,7 +1981,7 @@ status_t MPEG4Writer::Track::threadEntry() { } else { prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0); } - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); sp<MetaData> meta_data; diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp index 5a453e96a499..a08eb7b371be 100644 --- a/media/libstagefright/TimedEventQueue.cpp +++ b/media/libstagefright/TimedEventQueue.cpp @@ -210,8 +210,7 @@ void *TimedEventQueue::ThreadWrapper(void *me) { vm->AttachCurrentThread(&env, NULL); #endif - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_FOREGROUND); - set_sched_policy(androidGetTid(), SP_FOREGROUND); + androidSetThreadPriority(0, ANDROID_PRIORITY_FOREGROUND); static_cast<TimedEventQueue *>(me)->threadEntry(); diff --git a/media/libstagefright/chromium_http/support.cpp b/media/libstagefright/chromium_http/support.cpp index 805bd4876cf0..ed6846c4e406 100644 --- a/media/libstagefright/chromium_http/support.cpp +++ b/media/libstagefright/chromium_http/support.cpp @@ -40,7 +40,7 @@ namespace android { static Mutex gNetworkThreadLock; static base::Thread *gNetworkThread = NULL; -static scoped_refptr<URLRequestContext> gReqContext; +static scoped_refptr<net::URLRequestContext> gReqContext; static void InitializeNetworkThreadIfNecessary() { Mutex::Autolock autoLock(gNetworkThreadLock); @@ -214,7 +214,7 @@ void SfDelegate::OnSetCookie( } void SfDelegate::OnResponseStarted(net::URLRequest *request) { - if (request->status().status() != URLRequestStatus::SUCCESS) { + if (request->status().status() != net::URLRequestStatus::SUCCESS) { MY_LOGI(StringPrintf( "Request failed with status %d and os_error %d", request->status().status(), @@ -325,7 +325,7 @@ void SfDelegate::readMore(net::URLRequest *request) { } else { MY_LOGV("readMore pending read"); - if (request->status().status() != URLRequestStatus::IO_PENDING) { + if (request->status().status() != net::URLRequestStatus::IO_PENDING) { MY_LOGI(StringPrintf( "Direct read failed w/ status %d\n", request->status().status()).c_str()); diff --git a/media/libstagefright/chromium_http/support.h b/media/libstagefright/chromium_http/support.h index 4d034937a952..8fe8db1da9e1 100644 --- a/media/libstagefright/chromium_http/support.h +++ b/media/libstagefright/chromium_http/support.h @@ -50,7 +50,7 @@ private: DISALLOW_EVIL_CONSTRUCTORS(SfNetLog); }; -struct SfRequestContext : public URLRequestContext { +struct SfRequestContext : public net::URLRequestContext { SfRequestContext(); virtual const std::string &GetUserAgent(const GURL &url) const; diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp index 4b1c3a7ed12f..14968e880549 100644 --- a/media/libstagefright/omx/OMX.cpp +++ b/media/libstagefright/omx/OMX.cpp @@ -116,7 +116,7 @@ void *OMX::CallbackDispatcher::ThreadWrapper(void *me) { } void OMX::CallbackDispatcher::threadEntry() { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); prctl(PR_SET_NAME, (unsigned long)"OMXCallbackDisp", 0, 0, 0); for (;;) { diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png Binary files differdeleted file mode 100644 index eb6eb0c82391..000000000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_default.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_pressed.png b/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_pressed.png Binary files differdeleted file mode 100644 index 11f66f547cf0..000000000000 --- a/packages/SystemUI/res/drawable-hdpi/ic_sysbar_ime_pressed.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png Binary files differdeleted file mode 100644 index f907de7e73aa..000000000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_default.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_pressed.png b/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_pressed.png Binary files differdeleted file mode 100644 index 36f8853a21a0..000000000000 --- a/packages/SystemUI/res/drawable-mdpi/ic_sysbar_ime_pressed.png +++ /dev/null diff --git a/packages/SystemUI/res/drawable-nodpi/compat_mode_help_bg.png b/packages/SystemUI/res/drawable-nodpi/compat_mode_help_bg.png Binary files differnew file mode 100644 index 000000000000..d1d32a470f0b --- /dev/null +++ b/packages/SystemUI/res/drawable-nodpi/compat_mode_help_bg.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_ime_default.png Binary files differnew file mode 100644 index 000000000000..1a9d88c0b664 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_ime_default.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_ime_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_ime_pressed.png Binary files differnew file mode 100644 index 000000000000..a6d7507dd30b --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_ime_pressed.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png Binary files differindex e2584e3a2f9a..1e1324a6bb19 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_default.png diff --git a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png Binary files differindex 58b85102d9b3..e4e13c52b302 100644 --- a/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png +++ b/packages/SystemUI/res/drawable-sw600dp-hdpi/ic_sysbar_zoom_pressed.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_ime_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_ime_default.png Binary files differnew file mode 100644 index 000000000000..33edce00137c --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_ime_default.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_ime_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_ime_pressed.png Binary files differnew file mode 100644 index 000000000000..8bab6cf338f7 --- /dev/null +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_ime_pressed.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png Binary files differindex 2795c345f62e..89d486f391de 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_default.png diff --git a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png Binary files differindex bbed6a6467d6..b13443607255 100644 --- a/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png +++ b/packages/SystemUI/res/drawable-sw600dp-mdpi/ic_sysbar_zoom_pressed.png diff --git a/packages/SystemUI/res/drawable/compat_mode_help_diagram.png b/packages/SystemUI/res/drawable/compat_mode_help_diagram.png Binary files differnew file mode 100644 index 000000000000..e2122313bc55 --- /dev/null +++ b/packages/SystemUI/res/drawable/compat_mode_help_diagram.png diff --git a/packages/SystemUI/res/drawable/compat_mode_help_icon.png b/packages/SystemUI/res/drawable/compat_mode_help_icon.png Binary files differnew file mode 100644 index 000000000000..03bbef9c06a2 --- /dev/null +++ b/packages/SystemUI/res/drawable/compat_mode_help_icon.png diff --git a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml index 977e00205e6f..97d0348f0a3b 100644 --- a/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml +++ b/packages/SystemUI/res/drawable/ic_sysbar_zoom.xml @@ -16,6 +16,7 @@ <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_pressed="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" /> + <item android:state_selected="true" android:drawable="@drawable/ic_sysbar_zoom_pressed" /> <item android:drawable="@drawable/ic_sysbar_zoom_default" /> </selector> diff --git a/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml b/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml new file mode 100644 index 000000000000..df3c5a3a402d --- /dev/null +++ b/packages/SystemUI/res/layout-sw600dp/compat_mode_help.xml @@ -0,0 +1,79 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/* +** Copyright 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. +*/ +--> + +<RelativeLayout + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="match_parent" + android:layout_width="match_parent" + android:background="@drawable/compat_mode_help_bg" + > + <TextView + android:id="@+id/header" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginLeft="160dp" + android:layout_marginTop="80dp" + android:textSize="60sp" + android:maxLines="1" + android:shadowRadius="8" + android:shadowColor="#FF000000" + android:text="@string/compat_mode_help_header" + /> + <ImageView + android:id="@+id/diagram" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="160dp" + android:layout_marginTop="80dp" + android:layout_centerInParent="true" + android:src="@drawable/compat_mode_help_diagram" + /> + <TextView + android:id="@+id/explanation" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_marginBottom="80dp" + android:layout_marginRight="240dp" + android:layout_alignLeft="@id/header" + android:layout_alignParentBottom="true" + android:shadowRadius="4" + android:shadowColor="#FF000000" + android:textSize="28sp" + android:text="@string/compat_mode_help_body" + /> + <ImageView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginRight="80dp" + android:layout_alignBottom="@id/explanation" + android:layout_alignParentRight="true" + android:src="@drawable/compat_mode_help_icon" + /> + <Button + android:id="@+id/button" + android:layout_width="208dp" + android:layout_height="48dp" + android:layout_alignLeft="@id/header" + android:layout_alignParentBottom="true" + android:layout_marginBottom="10dp" + android:textSize="28sp" + android:text="@android:string/ok" + /> +</RelativeLayout> + diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar.xml b/packages/SystemUI/res/layout-sw600dp/status_bar.xml index 707a8cb56f73..d9f3f2324499 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar.xml @@ -75,13 +75,6 @@ systemui:keyCode="82" android:visibility="invisible" /> - <com.android.systemui.statusbar.policy.CompatModeButton - android:id="@+id/compat_button" - android:layout_width="80dip" - android:layout_height="match_parent" - android:src="@drawable/ic_sysbar_zoom" - android:visibility="invisible" - /> </LinearLayout> <!-- fake space bar zone --> diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_compat_mode_panel.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_compat_mode_panel.xml new file mode 100644 index 000000000000..c151565fe798 --- /dev/null +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_compat_mode_panel.xml @@ -0,0 +1,43 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +** +** Copyright 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. +*/ +--> + +<com.android.systemui.statusbar.tablet.CompatModePanel + xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:paddingBottom="@dimen/panel_float" + android:paddingRight="20dp" + > + <RadioGroup android:id="@+id/compat_mode_radio_group" + android:background="@*android:drawable/dialog_full_holo_dark" + android:layout_height="wrap_content" + android:layout_width="match_parent" + android:orientation="vertical" + android:padding="10dp" + > + <RadioButton android:id="@+id/compat_mode_on_radio" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/compat_mode_on" /> + <RadioButton android:id="@+id/compat_mode_off_radio" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/compat_mode_off" /> + </RadioGroup> +</com.android.systemui.statusbar.tablet.CompatModePanel> diff --git a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml index f53b29e36beb..fecfe7f1ae1b 100644 --- a/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml +++ b/packages/SystemUI/res/layout-sw600dp/status_bar_notification_area.xml @@ -27,7 +27,7 @@ > <LinearLayout - android:id="@+id/notificationAndImeArea" + android:id="@+id/feedbackIconArea" android:layout_width="wrap_content" android:layout_height="match_parent" android:orientation="horizontal" @@ -41,7 +41,16 @@ android:src="@drawable/ic_sysbar_ime_default" android:visibility="gone" /> - + + <com.android.systemui.statusbar.policy.CompatModeButton + android:id="@+id/compatModeButton" + android:layout_width="wrap_content" + android:layout_height="match_parent" + android:layout_marginLeft="8dip" + android:src="@drawable/ic_sysbar_zoom" + android:visibility="gone" + /> + <com.android.systemui.statusbar.tablet.NotificationIconArea android:id="@+id/notificationIcons" android:layout_width="wrap_content" @@ -144,4 +153,3 @@ </LinearLayout> </LinearLayout> </LinearLayout> - diff --git a/packages/SystemUI/res/values-sw600dp/dimens.xml b/packages/SystemUI/res/values-sw600dp/dimens.xml index dec8b672734b..944e0eeffe6d 100644 --- a/packages/SystemUI/res/values-sw600dp/dimens.xml +++ b/packages/SystemUI/res/values-sw600dp/dimens.xml @@ -26,5 +26,6 @@ <dimen name="notification_panel_width">512dp</dimen> <!-- The minimum height of the notification panel window --> <dimen name="notification_panel_min_height">770dp</dimen> + <!-- Bottom margin (from display edge) for status bar panels --> + <dimen name="panel_float">56dp</dimen> </resources> - diff --git a/packages/SystemUI/res/values/dimens.xml b/packages/SystemUI/res/values/dimens.xml index 5f0fbef7b6c1..83eaaa824ac4 100644 --- a/packages/SystemUI/res/values/dimens.xml +++ b/packages/SystemUI/res/values/dimens.xml @@ -44,6 +44,8 @@ <dimen name="status_bar_recents_fading_edge_length">20dip</dimen> <!-- Margin between recents container and glow on the right --> <dimen name="status_bar_recents_right_glow_margin">100dip</dimen> + <!-- Amount to offset bottom of notification peek window from top of status bar. --> + <dimen name="peek_window_y_offset">-12dp</dimen> <!-- thickness (height) of the navigation bar on phones that require it --> <dimen name="navigation_bar_size">42dp</dimen> diff --git a/packages/SystemUI/res/values/strings.xml b/packages/SystemUI/res/values/strings.xml index 446827b2869e..8945da5828d9 100644 --- a/packages/SystemUI/res/values/strings.xml +++ b/packages/SystemUI/res/values/strings.xml @@ -143,4 +143,17 @@ <!-- Checkbox label for USB accessory dialogs. [CHAR LIMIT=50] --> <string name="always_use_accessory">Use by default for this USB accessory</string> + <!-- Checkbox label for application compatibility mode ON (zooming app to look like it's running + on a phone). [CHAR LIMIT=25] --> + <string name="compat_mode_on">Zoom to fill screen</string> + + <!-- Checkbox label for application compatibility mode OFF (normal mode on tablets). + [CHAR LIMIT=25] --> + <string name="compat_mode_off">Stretch to fill screen</string> + + <!-- Compatibility mode help screen: header text. [CHAR LIMIT=50] --> + <string name="compat_mode_help_header">Compatibility Zoom</string> + + <!-- Compatibility mode help screen: body text. [CHAR LIMIT=150] --> + <string name="compat_mode_help_body">When an app was designed for a smaller screen, a zoom control will appear by the clock.</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java index da1e1c5e72c5..f81820e5a3f4 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/CommandQueue.java @@ -56,13 +56,12 @@ public class CommandQueue extends IStatusBar.Stub { private static final int MSG_SET_LIGHTS_ON = 7 << MSG_SHIFT; - private static final int MSG_SHOW_MENU = 8 << MSG_SHIFT; + private static final int MSG_TOP_APP_WINDOW_CHANGED = 8 << MSG_SHIFT; private static final int MSG_SHOW_IME_BUTTON = 9 << MSG_SHIFT; private static final int MSG_SET_HARD_KEYBOARD_STATUS = 10 << MSG_SHIFT; private static final int MSG_USER_ACTIVITY = 11 << MSG_SHIFT; - private StatusBarIconList mList; private Callbacks mCallbacks; private Handler mHandler = new H(); @@ -87,7 +86,7 @@ public class CommandQueue extends IStatusBar.Stub { public void animateExpand(); public void animateCollapse(); public void setLightsOn(boolean on); - public void setMenuKeyVisible(boolean visible); + public void topAppWindowChanged(boolean visible); public void setImeWindowStatus(IBinder token, int vis, int backDisposition); public void setHardKeyboardStatus(boolean available, boolean enabled); public void userActivity(); @@ -166,10 +165,11 @@ public class CommandQueue extends IStatusBar.Stub { } } - public void setMenuKeyVisible(boolean visible) { + public void topAppWindowChanged(boolean menuVisible) { synchronized (mList) { - mHandler.removeMessages(MSG_SHOW_MENU); - mHandler.obtainMessage(MSG_SHOW_MENU, visible ? 1 : 0, 0, null).sendToTarget(); + mHandler.removeMessages(MSG_TOP_APP_WINDOW_CHANGED); + mHandler.obtainMessage(MSG_TOP_APP_WINDOW_CHANGED, menuVisible ? 1 : 0, 0, + null).sendToTarget(); } } @@ -253,8 +253,8 @@ public class CommandQueue extends IStatusBar.Stub { case MSG_SET_LIGHTS_ON: mCallbacks.setLightsOn(msg.arg1 != 0); break; - case MSG_SHOW_MENU: - mCallbacks.setMenuKeyVisible(msg.arg1 != 0); + case MSG_TOP_APP_WINDOW_CHANGED: + mCallbacks.topAppWindowChanged(msg.arg1 != 0); break; case MSG_SHOW_IME_BUTTON: mCallbacks.setImeWindowStatus((IBinder)msg.obj, msg.arg1, msg.arg2); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java index 23ae8230aef4..e567dc7a71b5 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBar.java @@ -78,7 +78,7 @@ public abstract class StatusBar extends SystemUI implements CommandQueue.Callbac disable(switches[0]); setLightsOn(switches[1] != 0); - setMenuKeyVisible(switches[2] != 0); + topAppWindowChanged(switches[2] != 0); // StatusBarManagerService has a back up of IME token and it's restored here. setImeWindowStatus(binders.get(0), switches[3], switches[4]); setHardKeyboardStatus(switches[5] != 0, switches[6] != 0); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 687de0750efd..cc8358e8b723 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -1144,7 +1144,7 @@ public class PhoneStatusBar extends StatusBar { } // Not supported - public void setMenuKeyVisible(boolean visible) { } + public void topAppWindowChanged(boolean visible) { } public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { } @Override public void setHardKeyboardStatus(boolean available, boolean enabled) { } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java index c3052e8c3d2c..7fbf7344a25b 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/CompatModeButton.java @@ -27,7 +27,8 @@ import android.widget.ImageView; import com.android.systemui.R; -public class CompatModeButton extends ImageView implements View.OnClickListener { +public class CompatModeButton extends ImageView { + private static final boolean DEBUG = false; private static final String TAG = "StatusBar.CompatModeButton"; private ActivityManager mAM; @@ -43,22 +44,14 @@ public class CompatModeButton extends ImageView implements View.OnClickListener mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); - setOnClickListener(this); - refresh(); } - @Override - public void onClick(View v) { - mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_TOGGLE); - } - public void refresh() { int mode = mAM.getFrontActivityScreenCompatMode(); - setVisibility((mode == ActivityManager.COMPAT_MODE_NEVER - || mode == ActivityManager.COMPAT_MODE_ALWAYS) - ? View.GONE - : View.VISIBLE - ); + final boolean vis = (mode != ActivityManager.COMPAT_MODE_NEVER + && mode != ActivityManager.COMPAT_MODE_ALWAYS); + if (DEBUG) Slog.d(TAG, "compat mode is " + mode + "; icon will " + (vis ? "show" : "hide")); + setVisibility(vis ? View.VISIBLE : View.GONE); } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java index 24eee274d8bb..b5ea7b2035cd 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NotificationRowLayout.java @@ -20,6 +20,7 @@ import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; +import android.animation.TimeAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.Resources; @@ -30,6 +31,8 @@ import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.util.Slog; import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.VelocityTracker; import android.view.View; import android.view.ViewGroup; import android.view.animation.AccelerateInterpolator; @@ -45,10 +48,15 @@ import com.android.systemui.R; public class NotificationRowLayout extends ViewGroup { private static final String TAG = "NotificationRowLayout"; private static final boolean DEBUG = false; + private static final boolean SLOW_ANIMATIONS = false; // DEBUG; private static final boolean ANIMATE_LAYOUT = true; - private static final int ANIM_LEN = DEBUG ? 5000 : 250; + private static final int APPEAR_ANIM_LEN = SLOW_ANIMATIONS ? 5000 : 250; + private static final int DISAPPEAR_ANIM_LEN = APPEAR_ANIM_LEN; + private static final int SNAP_ANIM_LEN = SLOW_ANIMATIONS ? 1000 : 250; + + private static final float SWIPE_ESCAPE_VELOCITY = 1500f; Rect mTmpRect = new Rect(); int mNumRows = 0; @@ -58,6 +66,11 @@ public class NotificationRowLayout extends ViewGroup { HashSet<View> mAppearingViews = new HashSet<View>(); HashSet<View> mDisappearingViews = new HashSet<View>(); + VelocityTracker mVT; + float mInitialTouchX, mInitialTouchY; + View mSlidingChild = null; + float mLiftoffVelocity; + public NotificationRowLayout(Context context, AttributeSet attrs) { this(context, attrs, 0); } @@ -65,6 +78,8 @@ public class NotificationRowLayout extends ViewGroup { public NotificationRowLayout(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); + mVT = VelocityTracker.obtain(); + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.NotificationRowLayout, defStyle, 0); mRowHeight = a.getDimensionPixelSize(R.styleable.NotificationRowLayout_rowHeight, 0); @@ -89,6 +104,92 @@ public class NotificationRowLayout extends ViewGroup { } + // Swipey code + @Override + public boolean onInterceptTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); +// if (DEBUG) Slog.d(TAG, "intercepting touch event: " + ev); + switch (action) { + case MotionEvent.ACTION_DOWN: + mVT.clear(); + mVT.addMovement(ev); + mInitialTouchX = ev.getX(); + mInitialTouchY = ev.getY(); + mSlidingChild = null; + break; + case MotionEvent.ACTION_MOVE: + mVT.addMovement(ev); + if (mSlidingChild == null) { + if (Math.abs(ev.getX() - mInitialTouchX) > 4) { // slide slop + + // find the view under the pointer, accounting for GONE views + final int count = getChildCount(); + int y = 0; + int childIdx = 0; + for (; childIdx < count; childIdx++) { + mSlidingChild = getChildAt(childIdx); + if (mSlidingChild.getVisibility() == GONE) { + continue; + } + y += mRowHeight; + if (mInitialTouchY < y) break; + } + + mInitialTouchX -= mSlidingChild.getTranslationX(); + mSlidingChild.animate().cancel(); + + if (DEBUG) { + Slog.d(TAG, String.format( + "now sliding child %d: %s (touchY=%.1f, rowHeight=%d, count=%d)", + childIdx, mSlidingChild, mInitialTouchY, mRowHeight, count)); + } + } + } + break; + } + return mSlidingChild != null; + } + @Override + public boolean onTouchEvent(MotionEvent ev) { + final int action = ev.getAction(); +// if (DEBUG) Slog.d(TAG, "touch event: " + ev + " sliding: " + mSlidingChild); + if (mSlidingChild != null) { + switch (action) { + case MotionEvent.ACTION_OUTSIDE: + case MotionEvent.ACTION_MOVE: + mVT.addMovement(ev); + + mSlidingChild.setTranslationX(ev.getX() - mInitialTouchX); + break; + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + mVT.addMovement(ev); + mVT.computeCurrentVelocity(1000 /* px/sec */); + if (DEBUG) Slog.d(TAG, "exit velocity: " + mVT.getXVelocity()); + boolean restore = true; + mLiftoffVelocity = mVT.getXVelocity(); + if (Math.abs(mLiftoffVelocity) > SWIPE_ESCAPE_VELOCITY) { + // flingadingy + + View veto = mSlidingChild.findViewById(R.id.veto); + if (veto != null && veto.getVisibility() == View.VISIBLE) { + veto.performClick(); + restore = false; + } + } + if (restore) { + // snappity + mSlidingChild.animate().translationX(0) + .setDuration(SNAP_ANIM_LEN) + .start(); + } + break; + } + return true; + } + return false; + } + //** @Override public void addView(View child, int index, LayoutParams params) { @@ -105,7 +206,7 @@ public class NotificationRowLayout extends ViewGroup { ObjectAnimator.ofFloat(child, "alpha", 0f, 1f) // ,ObjectAnimator.ofFloat(child, "scaleY", 0f, 1f) ); - a.setDuration(ANIM_LEN); + a.setDuration(APPEAR_ANIM_LEN); a.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { @@ -127,21 +228,36 @@ public class NotificationRowLayout extends ViewGroup { mDisappearingViews.add(child); child.setPivotY(0); - AnimatorSet a = new AnimatorSet(); - a.playTogether( - ObjectAnimator.ofFloat(child, "alpha", 0f) -// ,ObjectAnimator.ofFloat(child, "scaleY", 0f) - ,ObjectAnimator.ofFloat(child, "translationX", 300f) - ); - a.setDuration(ANIM_LEN); - a.addListener(new AnimatorListenerAdapter() { + + final float velocity = (mSlidingChild == child) + ? mLiftoffVelocity : SWIPE_ESCAPE_VELOCITY; + final TimeAnimator zoom = new TimeAnimator(); + zoom.setTimeListener(new TimeAnimator.TimeListener() { + @Override + public void onTimeUpdate(TimeAnimator animation, long totalTime, long deltaTime) { + childF.setTranslationX(childF.getTranslationX() + deltaTime / 1000f * velocity); + } + }); + + final ObjectAnimator alphaFade = ObjectAnimator.ofFloat(child, "alpha", 0f); + alphaFade.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { + zoom.cancel(); // it won't end on its own + if (DEBUG) Slog.d(TAG, "actually removing child: " + childF); NotificationRowLayout.super.removeView(childF); childF.setAlpha(1f); mDisappearingViews.remove(childF); } }); + + AnimatorSet a = new AnimatorSet(); + a.playTogether(alphaFade, zoom); + +// ,ObjectAnimator.ofFloat(child, "scaleY", 0f) +// ,ObjectAnimator.ofFloat(child, "translationX", child.getTranslationX() + 300f) + + a.setDuration(DISAPPEAR_ANIM_LEN); a.start(); requestLayout(); // start the container animation } else { @@ -160,8 +276,8 @@ public class NotificationRowLayout extends ViewGroup { public void onDraw(android.graphics.Canvas c) { super.onDraw(c); if (DEBUG) { - Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: " - + getMeasuredHeight() + "px"); + //Slog.d(TAG, "onDraw: canvas height: " + c.getHeight() + "px; measured height: " + // + getMeasuredHeight() + "px"); c.save(); c.clipRect(6, 6, c.getWidth() - 6, getMeasuredHeight() - 6, android.graphics.Region.Op.DIFFERENCE); @@ -199,7 +315,7 @@ public class NotificationRowLayout extends ViewGroup { if (ANIMATE_LAYOUT && isShown()) { ObjectAnimator.ofInt(this, "forcedHeight", computedHeight) - .setDuration(ANIM_LEN) + .setDuration(APPEAR_ANIM_LEN) .start(); } else { setForcedHeight(computedHeight); @@ -230,7 +346,7 @@ public class NotificationRowLayout extends ViewGroup { final int width = right - left; final int height = bottom - top; - if (DEBUG) Slog.d(TAG, "onLayout: height=" + height); + //if (DEBUG) Slog.d(TAG, "onLayout: height=" + height); final int count = getChildCount(); int y = 0; @@ -252,7 +368,7 @@ public class NotificationRowLayout extends ViewGroup { } public void setForcedHeight(int h) { - if (DEBUG) Slog.d(TAG, "forcedHeight: " + h); + //if (DEBUG) Slog.d(TAG, "forcedHeight: " + h); if (h != mHeight) { mHeight = h; requestLayout(); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java index 05eafe866955..83e8c96f8064 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/Prefs.java @@ -26,6 +26,8 @@ public class Prefs { public static final String DO_NOT_DISTURB_PREF = "do_not_disturb"; public static final boolean DO_NOT_DISTURB_DEFAULT = false; + public static final String SHOWN_COMPAT_MODE_HELP = "shown_compat_mode_help"; + public static SharedPreferences read(Context context) { return context.getSharedPreferences(Prefs.SHARED_PREFS_NAME, Context.MODE_PRIVATE); } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java new file mode 100644 index 000000000000..5a82d1b47e57 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/CompatModePanel.java @@ -0,0 +1,115 @@ +/* + * 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.systemui.statusbar.tablet; + +import android.app.ActivityManager; +import android.content.Context; +import android.content.res.TypedArray; +import android.os.RemoteException; +import android.util.AttributeSet; +import android.util.Slog; +import android.view.View; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.RadioButton; +import android.widget.RadioGroup; + +import com.android.systemui.R; + +public class CompatModePanel extends FrameLayout implements StatusBarPanel, + View.OnClickListener { + private static final boolean DEBUG = TabletStatusBar.DEBUG; + private static final String TAG = "CompatModePanel"; + + private ActivityManager mAM; + + private boolean mAttached = false; + private Context mContext; + private RadioButton mOnButton, mOffButton; + + private View mTrigger; +// private InputMethodButton mInputMethodSwitchButton; + + public CompatModePanel(Context context, AttributeSet attrs) { + super(context, attrs); + mContext = context; + mAM = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); + } + + @Override + public void onFinishInflate() { + mOnButton = (RadioButton) findViewById(R.id.compat_mode_on_radio); + mOffButton = (RadioButton) findViewById(R.id.compat_mode_off_radio); + mOnButton.setOnClickListener(this); + mOffButton.setOnClickListener(this); + + refresh(); + } + + @Override + protected void onDetachedFromWindow() { + super.onDetachedFromWindow(); + if (mAttached) { + mAttached = false; + } + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + if (!mAttached) { + mAttached = true; + } + } + + @Override + public void onClick(View v) { + if (v == mOnButton) { + mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_ENABLED); + } else if (v == mOffButton) { + mAM.setFrontActivityScreenCompatMode(ActivityManager.COMPAT_MODE_DISABLED); + } + } + + @Override + public boolean isInContentArea(int x, int y) { + return false; + } + + public void setTrigger(View v) { + mTrigger = v; + } + + public void openPanel() { + setVisibility(View.VISIBLE); + if (mTrigger != null) mTrigger.setSelected(true); + refresh(); + } + + public void closePanel() { + setVisibility(View.GONE); + if (mTrigger != null) mTrigger.setSelected(false); + } + + private void refresh() { + int mode = mAM.getFrontActivityScreenCompatMode(); + final boolean on = (mode == ActivityManager.COMPAT_MODE_ENABLED); + mOnButton.setChecked(on); + mOffButton.setChecked(!on); + } + +} diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/HeightReceiver.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/HeightReceiver.java index 9924faa0c8de..7f4c9180dd40 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/HeightReceiver.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/HeightReceiver.java @@ -76,7 +76,7 @@ public class HeightReceiver extends BroadcastReceiver { int height = -1; if (plugged) { final DisplayMetrics metrics = new DisplayMetrics(); - mWindowManager.getDefaultDisplay().getMetrics(metrics); + mWindowManager.getDefaultDisplay().getRealMetrics(metrics); //Slog.i(TAG, "setPlugged: display metrics=" + metrics); final int shortSide = Math.min(metrics.widthPixels, metrics.heightPixels); height = shortSide - 720; diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index ffb45ca5abcf..a7af30d83178 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -23,6 +23,7 @@ import java.util.ArrayList; import android.animation.LayoutTransition; import android.animation.ObjectAnimator; import android.app.ActivityManagerNative; +import android.app.Dialog; import android.app.PendingIntent; import android.app.Notification; import android.app.StatusBarManager; @@ -35,6 +36,7 @@ import android.inputmethodservice.InputMethodService; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.LayerDrawable; +import android.provider.Settings; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -93,6 +95,8 @@ public class TabletStatusBar extends StatusBar implements public static final int MSG_HIDE_CHROME = 1031; public static final int MSG_OPEN_INPUT_METHODS_PANEL = 1040; public static final int MSG_CLOSE_INPUT_METHODS_PANEL = 1041; + public static final int MSG_OPEN_COMPAT_MODE_PANEL = 1050; + public static final int MSG_CLOSE_COMPAT_MODE_PANEL = 1051; public static final int MSG_STOP_TICKER = 2000; // Fitts' Law assistance for LatinIME; see policy.EventHole @@ -126,8 +130,9 @@ public class TabletStatusBar extends StatusBar implements View mMenuButton; View mRecentButton; - ViewGroup mNotificationAndImeArea; + ViewGroup mFeedbackIconArea; // notification icons, IME icon, compat icon InputMethodButton mInputMethodSwitchButton; + CompatModeButton mCompatModeButton; NotificationPanel mNotificationPanel; WindowManager.LayoutParams mNotificationPanelParams; @@ -166,6 +171,7 @@ public class TabletStatusBar extends StatusBar implements private RecentsPanelView mRecentsPanel; private InputMethodsPanel mInputMethodsPanel; + private CompatModePanel mCompatModePanel; public Context getContext() { return mContext; } @@ -246,11 +252,12 @@ public class TabletStatusBar extends StatusBar implements 512, // ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, - WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH, PixelFormat.TRANSLUCENT); lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; + lp.y = res.getDimensionPixelOffset(R.dimen.peek_window_y_offset); lp.setTitle("NotificationPeekWindow"); lp.windowAnimations = com.android.internal.R.style.Animation_Toast; @@ -304,6 +311,29 @@ public class TabletStatusBar extends StatusBar implements lp.windowAnimations = R.style.Animation_RecentPanel; WindowManagerImpl.getDefault().addView(mInputMethodsPanel, lp); + + // Compatibility mode selector panel + mCompatModePanel = (CompatModePanel) View.inflate(context, + R.layout.status_bar_compat_mode_panel, null); + mCompatModePanel.setOnTouchListener(new TouchOutsideListener( + MSG_CLOSE_COMPAT_MODE_PANEL, mCompatModePanel)); + mCompatModePanel.setTrigger(mCompatModeButton); + mCompatModePanel.setVisibility(View.GONE); + mStatusBarView.setIgnoreChildren(4, mCompatModeButton, mCompatModePanel); + lp = new WindowManager.LayoutParams( + 250, + ViewGroup.LayoutParams.WRAP_CONTENT, + WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM + | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH + | WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, + PixelFormat.TRANSLUCENT); + lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; + lp.setTitle("CompatModePanel"); + lp.windowAnimations = android.R.style.Animation_Dialog; + + WindowManagerImpl.getDefault().addView(mCompatModePanel, lp); } private int getNotificationPanelHeight() { @@ -430,11 +460,14 @@ public class TabletStatusBar extends StatusBar implements mNavigationArea.setLayoutTransition(mBarContentsLayoutTransition); // The bar contents buttons - mNotificationAndImeArea = (ViewGroup)sb.findViewById(R.id.notificationAndImeArea); + mFeedbackIconArea = (ViewGroup)sb.findViewById(R.id.feedbackIconArea); mInputMethodSwitchButton = (InputMethodButton) sb.findViewById(R.id.imeSwitchButton); // Overwrite the lister mInputMethodSwitchButton.setOnClickListener(mOnClickListener); + mCompatModeButton = (CompatModeButton) sb.findViewById(R.id.compatModeButton); + mCompatModeButton.setOnClickListener(mOnClickListener); + // for redirecting errant bar taps to the IME mFakeSpaceBar = sb.findViewById(R.id.fake_space_bar); @@ -646,6 +679,14 @@ public class TabletStatusBar extends StatusBar implements if (DEBUG) Slog.d(TAG, "closing input methods panel"); if (mInputMethodsPanel != null) mInputMethodsPanel.closePanel(false); break; + case MSG_OPEN_COMPAT_MODE_PANEL: + if (DEBUG) Slog.d(TAG, "opening compat panel"); + if (mCompatModePanel != null) mCompatModePanel.openPanel(); + break; + case MSG_CLOSE_COMPAT_MODE_PANEL: + if (DEBUG) Slog.d(TAG, "closing compat panel"); + if (mCompatModePanel != null) mCompatModePanel.closePanel(); + break; case MSG_SHOW_CHROME: if (DEBUG) Slog.d(TAG, "hiding shadows (lights on)"); mBarContents.setVisibility(View.VISIBLE); @@ -914,14 +955,14 @@ public class TabletStatusBar extends StatusBar implements if (0 == (mDisabled & (StatusBarManager.DISABLE_NOTIFICATION_ICONS | StatusBarManager.DISABLE_NOTIFICATION_TICKER))) { mTicker.add(key, n); - mNotificationAndImeArea.setVisibility(View.GONE); + mFeedbackIconArea.setVisibility(View.GONE); } } } // called by TabletTicker when it's done with all queued ticks public void doneTicking() { - mNotificationAndImeArea.setVisibility(View.VISIBLE); + mFeedbackIconArea.setVisibility(View.VISIBLE); } public void animateExpand() { @@ -939,6 +980,8 @@ public class TabletStatusBar extends StatusBar implements mHandler.sendEmptyMessage(MSG_CLOSE_RECENTS_PANEL); mHandler.removeMessages(MSG_CLOSE_INPUT_METHODS_PANEL); mHandler.sendEmptyMessage(MSG_CLOSE_INPUT_METHODS_PANEL); + mHandler.removeMessages(MSG_CLOSE_COMPAT_MODE_PANEL); + mHandler.sendEmptyMessage(MSG_CLOSE_COMPAT_MODE_PANEL); mHandler.removeMessages(MSG_CLOSE_NOTIFICATION_PEEK); mHandler.sendEmptyMessage(MSG_CLOSE_NOTIFICATION_PEEK); } @@ -956,18 +999,52 @@ public class TabletStatusBar extends StatusBar implements mHandler.sendEmptyMessage(on ? MSG_SHOW_CHROME : MSG_HIDE_CHROME); } - public void setMenuKeyVisible(boolean visible) { + public void topAppWindowChanged(boolean windowVisible) { if (DEBUG) { - Slog.d(TAG, (visible?"showing":"hiding") + " the MENU button"); + Slog.d(TAG, (windowVisible?"showing":"hiding") + " the MENU button"); } - mMenuButton.setVisibility(visible ? View.VISIBLE : View.GONE); + mMenuButton.setVisibility(windowVisible ? View.VISIBLE : View.GONE); // See above re: lights-out policy for legacy apps. - if (visible) setLightsOn(true); + if (windowVisible) setLightsOn(true); + + mCompatModeButton.refresh(); + if (mCompatModeButton.getVisibility() == View.VISIBLE) { + if (! Prefs.read(mContext).getBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, false)) { + showCompatibilityHelp(); + } + } + } + + private void showCompatibilityHelp() { + final View dlg = View.inflate(mContext, R.layout.compat_mode_help, null); + View button = dlg.findViewById(R.id.button); - // XXX: HACK: not sure if this is the best way to catch a new activity that might require a - // change in compatibility features, but it's a start. - ((CompatModeButton) mBarContents.findViewById(R.id.compat_button)).refresh(); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + WindowManagerImpl.getDefault().removeView(dlg); + } + }); + + WindowManager.LayoutParams lp = mNotificationPanelParams = new WindowManager.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG, + WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS + | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, + PixelFormat.TRANSLUCENT); + lp.setTitle("CompatibilityModeDialog"); + lp.softInputMode = WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED + | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING; + lp.windowAnimations = com.android.internal.R.style.Animation_ZoomButtons; // simple fade + + WindowManagerImpl.getDefault().addView(dlg, lp); + + SharedPreferences.Editor editor = Prefs.edit(mContext); + editor.putBoolean(Prefs.SHOWN_COMPAT_MODE_HELP, true); + editor.apply(); } public void setImeWindowStatus(IBinder token, int vis, int backDisposition) { @@ -1060,6 +1137,8 @@ public class TabletStatusBar extends StatusBar implements onClickRecentButton(); } else if (v == mInputMethodSwitchButton) { onClickInputMethodSwitchButton(); + } else if (v == mCompatModeButton) { + onClickCompatModeButton(); } } }; @@ -1102,6 +1181,13 @@ public class TabletStatusBar extends StatusBar implements mHandler.sendEmptyMessage(msg); } + public void onClickCompatModeButton() { + int msg = (mCompatModePanel.getVisibility() == View.GONE) ? + MSG_OPEN_COMPAT_MODE_PANEL : MSG_CLOSE_COMPAT_MODE_PANEL; + mHandler.removeMessages(msg); + mHandler.sendEmptyMessage(msg); + } + public NotificationClicker makeClicker(PendingIntent intent, String pkg, String tag, int id) { return new NotificationClicker(intent, pkg, tag, id); } @@ -1438,13 +1524,13 @@ public class TabletStatusBar extends StatusBar implements ArrayList<View> toShow = new ArrayList<View>(); - // When IME button is visible, the number of notification icons should be decremented - // to fit the upper limit. - // IME switcher icon is big and occupy width of one icon - final int maxNotificationIconsImeButtonVisible = mMaxNotificationIcons - 1; - final int maxNotificationIconsCount = - (mInputMethodSwitchButton.getVisibility() != View.GONE) ? - maxNotificationIconsImeButtonVisible : mMaxNotificationIcons; + // Extra Special Icons + // The IME switcher and compatibility mode icons take the place of notifications. You didn't + // need to see all those new emails, did you? + int maxNotificationIconsCount = mMaxNotificationIcons; + if (mInputMethodSwitchButton.getVisibility() != View.GONE) maxNotificationIconsCount --; + if (mCompatModeButton.getVisibility() != View.GONE) maxNotificationIconsCount --; + for (int i=0; i< maxNotificationIconsCount; i++) { if (i>=N) break; toShow.add(mNotificationData.get(N-i-1).icon); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java index 4e2faf792771..dff1f6ada0e0 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBarView.java @@ -27,8 +27,9 @@ import android.widget.FrameLayout; public class TabletStatusBarView extends FrameLayout { private Handler mHandler; - private final View[] mIgnoreChildren = new View[4]; - private final View[] mPanels = new View[4]; + private final int MAX_PANELS = 5; + private final View[] mIgnoreChildren = new View[MAX_PANELS]; + private final View[] mPanels = new View[MAX_PANELS]; private final int[] mPos = new int[2]; public TabletStatusBarView(Context context) { diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 9dd2625db20c..572898977c63 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -330,6 +330,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final Rect mTmpVisibleFrame = new Rect(); WindowState mTopFullscreenOpaqueWindowState; + WindowState mTopAppWindowState; + WindowState mLastTopAppWindowState; boolean mTopIsFullscreen; boolean mForceStatusBar; boolean mHideLockScreen; @@ -340,7 +342,6 @@ public class PhoneWindowManager implements WindowManagerPolicy { Intent mDeskDockIntent; int mShortcutKeyPressed = -1; boolean mConsumeShortcutKeyUp; - boolean mShowMenuKey = false; // track FLAG_NEEDS_MENU_KEY on frontmost window // support for activating the lock screen while the screen is on boolean mAllowLockscreenWhenOn; @@ -1957,12 +1958,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { // the status bar. They are protected by the STATUS_BAR_SERVICE // permission, so they have the same privileges as the status // bar itself. - pf.left = df.left = cf.left = vf.left = mUnrestrictedScreenLeft; - pf.top = df.top = cf.top = vf.top = mUnrestrictedScreenTop; - pf.right = df.right = cf.right = vf.right - = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; - pf.bottom = df.bottom = cf.bottom = vf.bottom - = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + pf.left = df.left = cf.left = mRestrictedScreenLeft; + pf.top = df.top = cf.top = mRestrictedScreenTop; + pf.right = df.right = cf.right = mRestrictedScreenLeft+mRestrictedScreenWidth; + pf.bottom = df.bottom = cf.bottom + = mRestrictedScreenTop+mRestrictedScreenHeight; } else { pf.left = mContentLeft; pf.top = mContentTop; @@ -2032,6 +2032,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ public void beginAnimationLw(int displayWidth, int displayHeight) { mTopFullscreenOpaqueWindowState = null; + mTopAppWindowState = null; mForceStatusBar = false; mHideLockScreen = false; @@ -2067,6 +2068,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } } + if (mTopAppWindowState == null && win.isVisibleOrBehindKeyguardLw()) { + if (attrs.type >= FIRST_APPLICATION_WINDOW + && attrs.type <= LAST_APPLICATION_WINDOW) { + mTopAppWindowState = win; + } + } } /** {@inheritDoc} */ @@ -2110,22 +2117,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } - boolean topNeedsMenu = mShowMenuKey; - if (lp != null) { - topNeedsMenu = (lp.flags & WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0; - } - - if (DEBUG_LAYOUT) Log.v(TAG, "Top window " - + (topNeedsMenu ? "needs" : "does not need") - + " the MENU key"); - mTopIsFullscreen = topIsFullscreen; - final boolean changedMenu = (topNeedsMenu != mShowMenuKey); - if (changedMenu) { - final boolean topNeedsMenuF = topNeedsMenu; + if (mTopAppWindowState != null && mTopAppWindowState != mLastTopAppWindowState) { + mLastTopAppWindowState = mTopAppWindowState; - mShowMenuKey = topNeedsMenu; + final boolean topNeedsMenu = (mTopAppWindowState.getAttrs().flags + & WindowManager.LayoutParams.FLAG_NEEDS_MENU_KEY) != 0; mHandler.post(new Runnable() { public void run() { @@ -2140,9 +2138,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { final IStatusBarService sbs = mStatusBarService; if (mStatusBarService != null) { try { - if (changedMenu) { - sbs.setMenuKeyVisible(topNeedsMenuF); - } + sbs.topAppWindowChanged(topNeedsMenu); } catch (RemoteException e) { // This should be impossible because we're in the same process. mStatusBarService = null; diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 6e803a441356..95b8a5723e2f 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -14,26 +14,14 @@ * limitations under the License. */ -// -// Handle events, like key input and vsync. -// -// The goal is to provide an optimized solution for Linux, not an -// implementation that works well across all platforms. We expect -// events to arrive on file descriptors, so that we can use a select() -// select() call to sleep. -// -// We can't select() on anything but network sockets in Windows, so we -// provide an alternative implementation of waitEvent for that platform. -// #define LOG_TAG "EventHub" -//#define LOG_NDEBUG 0 +// #define LOG_NDEBUG 0 #include "EventHub.h" #include <hardware_legacy/power.h> -#include <cutils/atomic.h> #include <cutils/properties.h> #include <utils/Log.h> #include <utils/Timers.h> @@ -55,14 +43,11 @@ #include <string.h> #include <stdint.h> #include <dirent.h> -#ifdef HAVE_INOTIFY -# include <sys/inotify.h> -#endif -#ifdef HAVE_ANDROID_OS -# include <sys/limits.h> /* not part of Linux */ -#endif -#include <sys/poll.h> + +#include <sys/inotify.h> +#include <sys/epoll.h> #include <sys/ioctl.h> +#include <sys/limits.h> /* this macro is used to tell if "bit" is set in "array" * it selects a byte from the array, and does a boolean AND @@ -74,9 +59,6 @@ /* this macro computes the number of bytes needed to represent a bit array of the specified size */ #define sizeof_bit_array(bits) ((bits + 7) / 8) -// Fd at index 0 is always reserved for inotify -#define FIRST_ACTUAL_DEVICE_INDEX 1 - #define INDENT " " #define INDENT2 " " #define INDENT3 " " @@ -102,15 +84,17 @@ EventHub::Device::Device(int fd, int32_t id, const String8& path, const InputDeviceIdentifier& identifier) : next(NULL), fd(fd), id(id), path(path), identifier(identifier), - classes(0), keyBitmask(NULL), relBitmask(NULL), propBitmask(NULL), - configuration(NULL), virtualKeyMap(NULL) { + classes(0), configuration(NULL), virtualKeyMap(NULL) { + memset(keyBitmask, 0, sizeof(keyBitmask)); + memset(absBitmask, 0, sizeof(absBitmask)); + memset(relBitmask, 0, sizeof(relBitmask)); + memset(swBitmask, 0, sizeof(swBitmask)); + memset(ledBitmask, 0, sizeof(ledBitmask)); + memset(propBitmask, 0, sizeof(propBitmask)); } EventHub::Device::~Device() { close(); - delete[] keyBitmask; - delete[] relBitmask; - delete[] propBitmask; delete configuration; delete virtualKeyMap; } @@ -125,25 +109,72 @@ void EventHub::Device::close() { // --- EventHub --- +const uint32_t EventHub::EPOLL_ID_INOTIFY; +const uint32_t EventHub::EPOLL_ID_WAKE; +const int EventHub::EPOLL_SIZE_HINT; +const int EventHub::EPOLL_MAX_EVENTS; + EventHub::EventHub(void) : - mError(NO_INIT), mBuiltInKeyboardId(-1), mNextDeviceId(1), + mBuiltInKeyboardId(-1), mNextDeviceId(1), mOpeningDevices(0), mClosingDevices(0), - mOpened(false), mNeedToSendFinishedDeviceScan(false), - mNeedToReopenDevices(0), mNeedToScanDevices(false), - mInputFdIndex(1) { + mNeedToSendFinishedDeviceScan(false), + mNeedToReopenDevices(false), mNeedToScanDevices(true), + mPendingEventCount(0), mPendingEventIndex(0), mPendingINotify(false) { acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); - memset(mSwitches, 0, sizeof(mSwitches)); mNumCpus = sysconf(_SC_NPROCESSORS_ONLN); + + mEpollFd = epoll_create(EPOLL_SIZE_HINT); + LOG_ALWAYS_FATAL_IF(mEpollFd < 0, "Could not create epoll instance. errno=%d", errno); + + mINotifyFd = inotify_init(); + int result = inotify_add_watch(mINotifyFd, DEVICE_PATH, IN_DELETE | IN_CREATE); + LOG_ALWAYS_FATAL_IF(result < 0, "Could not register INotify for %s. errno=%d", + DEVICE_PATH, errno); + + struct epoll_event eventItem; + memset(&eventItem, 0, sizeof(eventItem)); + eventItem.events = EPOLLIN; + eventItem.data.u32 = EPOLL_ID_INOTIFY; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mINotifyFd, &eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add INotify to epoll instance. errno=%d", errno); + + int wakeFds[2]; + result = pipe(wakeFds); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not create wake pipe. errno=%d", errno); + + mWakeReadPipeFd = wakeFds[0]; + mWakeWritePipeFd = wakeFds[1]; + + result = fcntl(mWakeReadPipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake read pipe non-blocking. errno=%d", + errno); + + result = fcntl(mWakeWritePipeFd, F_SETFL, O_NONBLOCK); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not make wake write pipe non-blocking. errno=%d", + errno); + + eventItem.data.u32 = EPOLL_ID_WAKE; + result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeReadPipeFd, &eventItem); + LOG_ALWAYS_FATAL_IF(result != 0, "Could not add wake read pipe to epoll instance. errno=%d", + errno); } EventHub::~EventHub(void) { - release_wake_lock(WAKE_LOCK_ID); - // we should free stuff here... -} + closeAllDevicesLocked(); + + while (mClosingDevices) { + Device* device = mClosingDevices; + mClosingDevices = device->next; + delete device; + } -status_t EventHub::errorCheck() const { - return mError; + ::close(mEpollFd); + ::close(mINotifyFd); + ::close(mWakeReadPipeFd); + ::close(mWakeWritePipeFd); + + release_wake_lock(WAKE_LOCK_ID); } String8 EventHub::getDeviceName(int32_t deviceId) const { @@ -317,7 +348,7 @@ bool EventHub::markSupportedKeyCodes(int32_t deviceId, size_t numCodes, bool EventHub::markSupportedKeyCodesLocked(Device* device, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const { - if (!device->keyMap.haveKeyLayout() || !device->keyBitmask) { + if (!device->keyMap.haveKeyLayout()) { return false; } @@ -405,13 +436,9 @@ void EventHub::setExcludedDevices(const Vector<String8>& devices) { bool EventHub::hasLed(int32_t deviceId, int32_t led) const { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device) { - uint8_t bitmask[sizeof_bit_array(LED_MAX + 1)]; - memset(bitmask, 0, sizeof(bitmask)); - if (ioctl(device->fd, EVIOCGBIT(EV_LED, sizeof(bitmask)), bitmask) >= 0) { - if (test_bit(led, bitmask)) { - return true; - } + if (device && led >= 0 && led <= LED_MAX) { + if (test_bit(led, device->ledBitmask)) { + return true; } } return false; @@ -420,7 +447,7 @@ bool EventHub::hasLed(int32_t deviceId, int32_t led) const { void EventHub::setLedState(int32_t deviceId, int32_t led, bool on) { AutoMutex _l(mLock); Device* device = getDeviceLocked(deviceId); - if (device) { + if (device && led >= 0 && led <= LED_MAX) { struct input_event ev; ev.time.tv_sec = 0; ev.time.tv_usec = 0; @@ -450,11 +477,14 @@ EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { if (deviceId == 0) { deviceId = mBuiltInKeyboardId; } + ssize_t index = mDevices.indexOfKey(deviceId); + return index >= 0 ? mDevices.valueAt(index) : NULL; +} - size_t numDevices = mDevices.size(); - for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < numDevices; i++) { - Device* device = mDevices[i]; - if (device->id == deviceId) { +EventHub::Device* EventHub::getDeviceByPathLocked(const char* devicePath) const { + for (size_t i = 0; i < mDevices.size(); i++) { + Device* device = mDevices.valueAt(i); + if (device->path == devicePath) { return device; } } @@ -462,35 +492,25 @@ EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { } size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSize) { - // Note that we only allow one caller to getEvents(), so don't need - // to do locking here... only when adding/removing devices. LOG_ASSERT(bufferSize >= 1); - if (!mOpened) { - android_atomic_acquire_store(0, &mNeedToReopenDevices); - - mError = openPlatformInput() ? NO_ERROR : UNKNOWN_ERROR; - mOpened = true; - mNeedToScanDevices = true; - } + AutoMutex _l(mLock); struct input_event readBuffer[bufferSize]; RawEvent* event = buffer; size_t capacity = bufferSize; + bool awoken = false; for (;;) { nsecs_t now = systemTime(SYSTEM_TIME_MONOTONIC); // Reopen input devices if needed. - if (android_atomic_acquire_load(&mNeedToReopenDevices)) { - android_atomic_acquire_store(0, &mNeedToReopenDevices); + if (mNeedToReopenDevices) { + mNeedToReopenDevices = false; LOGI("Reopening all input devices due to a configuration change."); - AutoMutex _l(mLock); - while (mDevices.size() > 1) { - closeDeviceAtIndexLocked(mDevices.size() - 1); - } + closeAllDevicesLocked(); mNeedToScanDevices = true; break; // return to the caller before we actually rescan } @@ -514,7 +534,7 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz if (mNeedToScanDevices) { mNeedToScanDevices = false; - scanDevices(); + scanDevicesLocked(); mNeedToSendFinishedDeviceScan = true; } @@ -544,27 +564,56 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } // Grab the next input event. - // mInputFdIndex is initially 1 because index 0 is used for inotify. - bool deviceWasRemoved = false; - while (mInputFdIndex < mFds.size()) { - const struct pollfd& pfd = mFds[mInputFdIndex]; - if (pfd.revents & POLLIN) { - int32_t readSize = read(pfd.fd, readBuffer, sizeof(struct input_event) * capacity); - if (readSize < 0) { - if (errno == ENODEV) { - deviceWasRemoved = true; - break; - } + bool deviceChanged = false; + while (mPendingEventIndex < mPendingEventCount) { + const struct epoll_event& eventItem = mPendingEventItems[mPendingEventIndex++]; + if (eventItem.data.u32 == EPOLL_ID_INOTIFY) { + if (eventItem.events & EPOLLIN) { + mPendingINotify = true; + } else { + LOGW("Received unexpected epoll event 0x%08x for INotify.", eventItem.events); + } + continue; + } + + if (eventItem.data.u32 == EPOLL_ID_WAKE) { + if (eventItem.events & EPOLLIN) { + LOGV("awoken after wake()"); + awoken = true; + char buffer[16]; + ssize_t nRead; + do { + nRead = read(mWakeReadPipeFd, buffer, sizeof(buffer)); + } while ((nRead == -1 && errno == EINTR) || nRead == sizeof(buffer)); + } else { + LOGW("Received unexpected epoll event 0x%08x for wake read pipe.", + eventItem.events); + } + continue; + } + + ssize_t deviceIndex = mDevices.indexOfKey(eventItem.data.u32); + if (deviceIndex < 0) { + LOGW("Received unexpected epoll event 0x%08x for unknown device id %d.", + eventItem.events, eventItem.data.u32); + continue; + } + + Device* device = mDevices.valueAt(deviceIndex); + if (eventItem.events & EPOLLIN) { + int32_t readSize = read(device->fd, readBuffer, + sizeof(struct input_event) * capacity); + if (readSize == 0 || (readSize < 0 && errno == ENODEV)) { + // Device was removed before INotify noticed. + deviceChanged = true; + closeDeviceLocked(device); + } else if (readSize < 0) { if (errno != EAGAIN && errno != EINTR) { LOGW("could not get event (errno=%d)", errno); } } else if ((readSize % sizeof(struct input_event)) != 0) { LOGE("could not get event (wrong size: %d)", readSize); - } else if (readSize == 0) { // eof - deviceWasRemoved = true; - break; } else { - const Device* device = mDevices[mInputFdIndex]; int32_t deviceId = device->id == mBuiltInKeyboardId ? 0 : device->id; size_t count = size_t(readSize) / sizeof(struct input_event); @@ -609,38 +658,39 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz } capacity -= count; if (capacity == 0) { + // The result buffer is full. Reset the pending event index + // so we will try to read the device again on the next iteration. + mPendingEventIndex -= 1; break; } } + } else { + LOGW("Received unexpected epoll event 0x%08x for device %s.", + eventItem.events, device->identifier.name.string()); } - mInputFdIndex += 1; } - // Handle the case where a device has been removed but INotify has not yet noticed. - if (deviceWasRemoved) { - AutoMutex _l(mLock); - closeDeviceAtIndexLocked(mInputFdIndex); - continue; // report added or removed devices immediately + // readNotify() will modify the list of devices so this must be done after + // processing all other events to ensure that we read all remaining events + // before closing the devices. + if (mPendingINotify && mPendingEventIndex >= mPendingEventCount) { + mPendingINotify = false; + readNotifyLocked(); + deviceChanged = true; } -#if HAVE_INOTIFY - // readNotify() will modify mFDs and mFDCount, so this must be done after - // processing all other events. - if(mFds[0].revents & POLLIN) { - readNotify(mFds[0].fd); - mFds.editItemAt(0).revents = 0; - mInputFdIndex = mFds.size(); - continue; // report added or removed devices immediately + // Report added or removed devices immediately. + if (deviceChanged) { + continue; } -#endif - // Return now if we have collected any events, otherwise poll. - if (event != buffer) { + // Return now if we have collected any events or if we were explicitly awoken. + if (event != buffer || awoken) { break; } // Poll for events. Mind the wake lock dance! - // We hold a wake lock at all times except during poll(). This works due to some + // We hold a wake lock at all times except during epoll_wait(). This works due to some // subtle choreography. When a device driver has pending (unread) events, it acquires // a kernel wake lock. However, once the last pending event has been read, the device // driver will release the kernel wake lock. To prevent the system from going to sleep @@ -650,16 +700,26 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz // // The timeout is advisory only. If the device is asleep, it will not wake just to // service the timeout. + mPendingEventIndex = 0; + + mLock.unlock(); // release lock before poll, must be before release_wake_lock release_wake_lock(WAKE_LOCK_ID); - int pollResult = poll(mFds.editArray(), mFds.size(), timeoutMillis); + int pollResult = epoll_wait(mEpollFd, mPendingEventItems, EPOLL_MAX_EVENTS, timeoutMillis); acquire_wake_lock(PARTIAL_WAKE_LOCK, WAKE_LOCK_ID); + mLock.lock(); // reacquire lock after poll, must be after acquire_wake_lock if (pollResult == 0) { - break; // timed out + // Timed out. + mPendingEventCount = 0; + break; } + if (pollResult < 0) { + // An error occurred. + mPendingEventCount = 0; + // Sleep after errors to avoid locking up the system. // Hopefully the error is transient. if (errno != EINTR) { @@ -667,6 +727,9 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz usleep(100000); } } else { + // Some events occurred. + mPendingEventCount = size_t(pollResult); + // On an SMP system, it is possible for the framework to read input events // faster than the kernel input device driver can produce a complete packet. // Because poll() wakes up as soon as the first input event becomes available, @@ -680,50 +743,27 @@ size_t EventHub::getEvents(int timeoutMillis, RawEvent* buffer, size_t bufferSiz usleep(250); } } - - // Prepare to process all of the FDs we just polled. - mInputFdIndex = 1; } // All done, return the number of events we read. return event - buffer; } -/* - * Open the platform-specific input device. - */ -bool EventHub::openPlatformInput(void) { - /* - * Open platform-specific input device(s). - */ - int res, fd; - -#ifdef HAVE_INOTIFY - fd = inotify_init(); - res = inotify_add_watch(fd, DEVICE_PATH, IN_DELETE | IN_CREATE); - if(res < 0) { - LOGE("could not add watch for %s, %s\n", DEVICE_PATH, strerror(errno)); - } -#else - /* - * The code in EventHub::getEvent assumes that mFDs[0] is an inotify fd. - * We allocate space for it and set it to something invalid. - */ - fd = -1; -#endif +void EventHub::wake() { + LOGV("wake() called"); - // Reserve fd index 0 for inotify. - struct pollfd pollfd; - pollfd.fd = fd; - pollfd.events = POLLIN; - pollfd.revents = 0; - mFds.push(pollfd); - mDevices.push(NULL); - return true; + ssize_t nWrite; + do { + nWrite = write(mWakeWritePipeFd, "W", 1); + } while (nWrite == -1 && errno == EINTR); + + if (nWrite != 1 && errno != EAGAIN) { + LOGW("Could not write wake signal, errno=%d", errno); + } } -void EventHub::scanDevices() { - int res = scanDir(DEVICE_PATH); +void EventHub::scanDevicesLocked() { + status_t res = scanDirLocked(DEVICE_PATH); if(res < 0) { LOGE("scan dir failed for %s\n", DEVICE_PATH); } @@ -755,13 +795,11 @@ static const int32_t GAMEPAD_KEYCODES[] = { AKEYCODE_BUTTON_13, AKEYCODE_BUTTON_14, AKEYCODE_BUTTON_15, AKEYCODE_BUTTON_16, }; -int EventHub::openDevice(const char *devicePath) { +status_t EventHub::openDeviceLocked(const char *devicePath) { char buffer[80]; LOGV("Opening device: %s", devicePath); - AutoMutex _l(mLock); - int fd = open(devicePath, O_RDWR); if(fd < 0) { LOGE("could not open %s, %s\n", devicePath, strerror(errno)); @@ -850,77 +888,50 @@ int EventHub::openDevice(const char *devicePath) { #endif // Load the configuration file for the device. - loadConfiguration(device); + loadConfigurationLocked(device); // Figure out the kinds of events the device reports. - uint8_t key_bitmask[sizeof_bit_array(KEY_MAX + 1)]; - memset(key_bitmask, 0, sizeof(key_bitmask)); - ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(key_bitmask)), key_bitmask); - - uint8_t abs_bitmask[sizeof_bit_array(ABS_MAX + 1)]; - memset(abs_bitmask, 0, sizeof(abs_bitmask)); - ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(abs_bitmask)), abs_bitmask); - - uint8_t rel_bitmask[sizeof_bit_array(REL_MAX + 1)]; - memset(rel_bitmask, 0, sizeof(rel_bitmask)); - ioctl(fd, EVIOCGBIT(EV_REL, sizeof(rel_bitmask)), rel_bitmask); - - uint8_t sw_bitmask[sizeof_bit_array(SW_MAX + 1)]; - memset(sw_bitmask, 0, sizeof(sw_bitmask)); - ioctl(fd, EVIOCGBIT(EV_SW, sizeof(sw_bitmask)), sw_bitmask); - - uint8_t prop_bitmask[sizeof_bit_array(INPUT_PROP_MAX + 1)]; - memset(prop_bitmask, 0, sizeof(prop_bitmask)); - ioctl(fd, EVIOCGPROP(sizeof(prop_bitmask)), prop_bitmask); - - device->keyBitmask = new uint8_t[sizeof(key_bitmask)]; - device->relBitmask = new uint8_t[sizeof(rel_bitmask)]; - device->propBitmask = new uint8_t[sizeof(prop_bitmask)]; - - if (!device->keyBitmask || !device->relBitmask || !device->propBitmask) { - delete device; - LOGE("out of memory allocating bitmasks"); - return -1; - } - - memcpy(device->keyBitmask, key_bitmask, sizeof(key_bitmask)); - memcpy(device->relBitmask, rel_bitmask, sizeof(rel_bitmask)); - memcpy(device->propBitmask, prop_bitmask, sizeof(prop_bitmask)); + ioctl(fd, EVIOCGBIT(EV_KEY, sizeof(device->keyBitmask)), device->keyBitmask); + ioctl(fd, EVIOCGBIT(EV_ABS, sizeof(device->absBitmask)), device->absBitmask); + ioctl(fd, EVIOCGBIT(EV_REL, sizeof(device->relBitmask)), device->relBitmask); + ioctl(fd, EVIOCGBIT(EV_SW, sizeof(device->swBitmask)), device->swBitmask); + ioctl(fd, EVIOCGBIT(EV_LED, sizeof(device->ledBitmask)), device->ledBitmask); + ioctl(fd, EVIOCGPROP(sizeof(device->propBitmask)), device->propBitmask); // See if this is a keyboard. Ignore everything in the button range except for // joystick and gamepad buttons which are handled like keyboards for the most part. - bool haveKeyboardKeys = containsNonZeroByte(key_bitmask, 0, sizeof_bit_array(BTN_MISC)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(KEY_OK), + bool haveKeyboardKeys = containsNonZeroByte(device->keyBitmask, 0, sizeof_bit_array(BTN_MISC)) + || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(KEY_OK), sizeof_bit_array(KEY_MAX + 1)); - bool haveGamepadButtons = containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_MISC), + bool haveGamepadButtons = containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_MISC), sizeof_bit_array(BTN_MOUSE)) - || containsNonZeroByte(key_bitmask, sizeof_bit_array(BTN_JOYSTICK), + || containsNonZeroByte(device->keyBitmask, sizeof_bit_array(BTN_JOYSTICK), sizeof_bit_array(BTN_DIGI)); if (haveKeyboardKeys || haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; } // See if this is a cursor device such as a trackball or mouse. - if (test_bit(BTN_MOUSE, key_bitmask) - && test_bit(REL_X, rel_bitmask) - && test_bit(REL_Y, rel_bitmask)) { + if (test_bit(BTN_MOUSE, device->keyBitmask) + && test_bit(REL_X, device->relBitmask) + && test_bit(REL_Y, device->relBitmask)) { device->classes |= INPUT_DEVICE_CLASS_CURSOR; } // See if this is a touch pad. // Is this a new modern multi-touch driver? - if (test_bit(ABS_MT_POSITION_X, abs_bitmask) - && test_bit(ABS_MT_POSITION_Y, abs_bitmask)) { + if (test_bit(ABS_MT_POSITION_X, device->absBitmask) + && test_bit(ABS_MT_POSITION_Y, device->absBitmask)) { // Some joysticks such as the PS3 controller report axes that conflict // with the ABS_MT range. Try to confirm that the device really is // a touch screen. - if (test_bit(BTN_TOUCH, key_bitmask) || !haveGamepadButtons) { + if (test_bit(BTN_TOUCH, device->keyBitmask) || !haveGamepadButtons) { device->classes |= INPUT_DEVICE_CLASS_TOUCH | INPUT_DEVICE_CLASS_TOUCH_MT; } // Is this an old style single-touch driver? - } else if (test_bit(BTN_TOUCH, key_bitmask) - && test_bit(ABS_X, abs_bitmask) - && test_bit(ABS_Y, abs_bitmask)) { + } else if (test_bit(BTN_TOUCH, device->keyBitmask) + && test_bit(ABS_X, device->absBitmask) + && test_bit(ABS_Y, device->absBitmask)) { device->classes |= INPUT_DEVICE_CLASS_TOUCH; } @@ -930,29 +941,23 @@ int EventHub::openDevice(const char *devicePath) { // from other devices such as accelerometers that also have absolute axes. if (haveGamepadButtons && !(device->classes & INPUT_DEVICE_CLASS_TOUCH) - && containsNonZeroByte(abs_bitmask, 0, sizeof_bit_array(ABS_MAX + 1))) { + && containsNonZeroByte(device->absBitmask, 0, sizeof_bit_array(ABS_MAX + 1))) { device->classes |= INPUT_DEVICE_CLASS_JOYSTICK; } - // figure out the switches this device reports - bool haveSwitches = false; - for (int i=0; i<EV_SW; i++) { - //LOGI("Device %d sw %d: has=%d", device->id, i, test_bit(i, sw_bitmask)); - if (test_bit(i, sw_bitmask)) { - haveSwitches = true; - if (mSwitches[i] == 0) { - mSwitches[i] = device->id; - } + // Check whether this device has switches. + for (int i = 0; i <= SW_MAX; i++) { + if (test_bit(i, device->swBitmask)) { + device->classes |= INPUT_DEVICE_CLASS_SWITCH; + break; } } - if (haveSwitches) { - device->classes |= INPUT_DEVICE_CLASS_SWITCH; - } + // Configure virtual keys. if ((device->classes & INPUT_DEVICE_CLASS_TOUCH)) { // Load the virtual keys for the touch screen, if any. // We do this now so that we can make sure to load the keymap if necessary. - status_t status = loadVirtualKeyMap(device); + status_t status = loadVirtualKeyMapLocked(device); if (!status) { device->classes |= INPUT_DEVICE_CLASS_KEYBOARD; } @@ -963,13 +968,13 @@ int EventHub::openDevice(const char *devicePath) { status_t keyMapStatus = NAME_NOT_FOUND; if (device->classes & (INPUT_DEVICE_CLASS_KEYBOARD | INPUT_DEVICE_CLASS_JOYSTICK)) { // Load the keymap for the device. - keyMapStatus = loadKeyMap(device); + keyMapStatus = loadKeyMapLocked(device); } // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { // Set system properties for the keyboard. - setKeyboardProperties(device, false); + setKeyboardPropertiesLocked(device, false); // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus @@ -977,7 +982,7 @@ int EventHub::openDevice(const char *devicePath) { && isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; - setKeyboardProperties(device, true); + setKeyboardPropertiesLocked(device, true); } // 'Q' key support = cheap test of whether this is an alpha-capable kbd @@ -1012,10 +1017,21 @@ int EventHub::openDevice(const char *devicePath) { } // Determine whether the device is external or internal. - if (isExternalDevice(device)) { + if (isExternalDeviceLocked(device)) { device->classes |= INPUT_DEVICE_CLASS_EXTERNAL; } + // Register with epoll. + struct epoll_event eventItem; + memset(&eventItem, 0, sizeof(eventItem)); + eventItem.events = EPOLLIN; + eventItem.data.u32 = deviceId; + if (epoll_ctl(mEpollFd, EPOLL_CTL_ADD, fd, &eventItem)) { + LOGE("Could not add device fd to epoll instance. errno=%d", errno); + delete device; + return -1; + } + LOGI("New device: id=%d, fd=%d, path='%s', name='%s', classes=0x%x, " "configuration='%s', keyLayout='%s', keyCharacterMap='%s', builtinKeyboard=%s", deviceId, fd, devicePath, device->identifier.name.string(), @@ -1025,19 +1041,14 @@ int EventHub::openDevice(const char *devicePath) { device->keyMap.keyCharacterMapFile.string(), toString(mBuiltInKeyboardId == deviceId)); - struct pollfd pollfd; - pollfd.fd = fd; - pollfd.events = POLLIN; - pollfd.revents = 0; - mFds.push(pollfd); - mDevices.push(device); + mDevices.add(deviceId, device); device->next = mOpeningDevices; mOpeningDevices = device; return 0; } -void EventHub::loadConfiguration(Device* device) { +void EventHub::loadConfigurationLocked(Device* device) { device->configurationFile = getInputDeviceConfigurationFilePathByDeviceIdentifier( device->identifier, INPUT_DEVICE_CONFIGURATION_FILE_TYPE_CONFIGURATION); if (device->configurationFile.isEmpty()) { @@ -1054,7 +1065,7 @@ void EventHub::loadConfiguration(Device* device) { } } -status_t EventHub::loadVirtualKeyMap(Device* device) { +status_t EventHub::loadVirtualKeyMapLocked(Device* device) { // The virtual key map is supplied by the kernel as a system board property file. String8 path; path.append("/sys/board_properties/virtualkeys."); @@ -1065,22 +1076,22 @@ status_t EventHub::loadVirtualKeyMap(Device* device) { return VirtualKeyMap::load(path, &device->virtualKeyMap); } -status_t EventHub::loadKeyMap(Device* device) { +status_t EventHub::loadKeyMapLocked(Device* device) { return device->keyMap.load(device->identifier, device->configuration); } -void EventHub::setKeyboardProperties(Device* device, bool builtInKeyboard) { +void EventHub::setKeyboardPropertiesLocked(Device* device, bool builtInKeyboard) { int32_t id = builtInKeyboard ? 0 : device->id; android::setKeyboardProperties(id, device->identifier, device->keyMap.keyLayoutFile, device->keyMap.keyCharacterMapFile); } -void EventHub::clearKeyboardProperties(Device* device, bool builtInKeyboard) { +void EventHub::clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboard) { int32_t id = builtInKeyboard ? 0 : device->id; android::clearKeyboardProperties(id); } -bool EventHub::isExternalDevice(Device* device) { +bool EventHub::isExternalDeviceLocked(Device* device) { if (device->configuration) { bool value; if (device->configuration->tryGetProperty(String8("device.internal"), value) @@ -1109,41 +1120,40 @@ bool EventHub::hasKeycodeLocked(Device* device, int keycode) const { return false; } -int EventHub::closeDevice(const char *devicePath) { - AutoMutex _l(mLock); - - for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) { - Device* device = mDevices[i]; - if (device->path == devicePath) { - return closeDeviceAtIndexLocked(i); - } +status_t EventHub::closeDeviceByPathLocked(const char *devicePath) { + Device* device = getDeviceByPathLocked(devicePath); + if (device) { + closeDeviceLocked(device); + return 0; } LOGV("Remove device: %s not found, device may already have been removed.", devicePath); return -1; } -int EventHub::closeDeviceAtIndexLocked(int index) { - Device* device = mDevices[index]; +void EventHub::closeAllDevicesLocked() { + while (mDevices.size() > 0) { + closeDeviceLocked(mDevices.valueAt(mDevices.size() - 1)); + } +} + +void EventHub::closeDeviceLocked(Device* device) { LOGI("Removed device: path=%s name=%s id=%d fd=%d classes=0x%x\n", device->path.string(), device->identifier.name.string(), device->id, device->fd, device->classes); - for (int j=0; j<EV_SW; j++) { - if (mSwitches[j] == device->id) { - mSwitches[j] = 0; - } - } - if (device->id == mBuiltInKeyboardId) { LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", device->path.string(), mBuiltInKeyboardId); mBuiltInKeyboardId = -1; - clearKeyboardProperties(device, true); + clearKeyboardPropertiesLocked(device, true); } - clearKeyboardProperties(device, false); + clearKeyboardPropertiesLocked(device, false); - mFds.removeAt(index); - mDevices.removeAt(index); + if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { + LOGW("Could not remove device fd from epoll instance. errno=%d", errno); + } + + mDevices.removeItem(device->id); device->close(); // Unlink for opening devices list if it is present. @@ -1174,11 +1184,9 @@ int EventHub::closeDeviceAtIndexLocked(int index) { device->next = mClosingDevices; mClosingDevices = device; } - return 0; } -int EventHub::readNotify(int nfd) { -#ifdef HAVE_INOTIFY +status_t EventHub::readNotifyLocked() { int res; char devname[PATH_MAX]; char *filename; @@ -1187,13 +1195,13 @@ int EventHub::readNotify(int nfd) { int event_pos = 0; struct inotify_event *event; - LOGV("EventHub::readNotify nfd: %d\n", nfd); - res = read(nfd, event_buf, sizeof(event_buf)); + LOGV("EventHub::readNotify nfd: %d\n", mINotifyFd); + res = read(mINotifyFd, event_buf, sizeof(event_buf)); if(res < (int)sizeof(*event)) { if(errno == EINTR) return 0; LOGW("could not get event, %s\n", strerror(errno)); - return 1; + return -1; } //printf("got %d bytes of event information\n", res); @@ -1207,21 +1215,19 @@ int EventHub::readNotify(int nfd) { if(event->len) { strcpy(filename, event->name); if(event->mask & IN_CREATE) { - openDevice(devname); - } - else { - closeDevice(devname); + openDeviceLocked(devname); + } else { + closeDeviceByPathLocked(devname); } } event_size = sizeof(*event) + event->len; res -= event_size; event_pos += event_size; } -#endif return 0; } -int EventHub::scanDir(const char *dirname) +status_t EventHub::scanDirLocked(const char *dirname) { char devname[PATH_MAX]; char *filename; @@ -1239,14 +1245,17 @@ int EventHub::scanDir(const char *dirname) (de->d_name[1] == '.' && de->d_name[2] == '\0'))) continue; strcpy(filename, de->d_name); - openDevice(devname); + openDeviceLocked(devname); } closedir(dir); return 0; } -void EventHub::reopenDevices() { - android_atomic_release_store(1, &mNeedToReopenDevices); +void EventHub::requestReopenDevices() { + LOGV("requestReopenDevices() called"); + + AutoMutex _l(mLock); + mNeedToReopenDevices = true; } void EventHub::dump(String8& dump) { @@ -1259,31 +1268,29 @@ void EventHub::dump(String8& dump) { dump.append(INDENT "Devices:\n"); - for (size_t i = FIRST_ACTUAL_DEVICE_INDEX; i < mDevices.size(); i++) { - const Device* device = mDevices[i]; - if (device) { - if (mBuiltInKeyboardId == device->id) { - dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", - device->id, device->identifier.name.string()); - } else { - dump.appendFormat(INDENT2 "%d: %s\n", device->id, - device->identifier.name.string()); - } - dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); - dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); - dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string()); - dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string()); - dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " - "product=0x%04x, version=0x%04x\n", - device->identifier.bus, device->identifier.vendor, - device->identifier.product, device->identifier.version); - dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", - device->keyMap.keyLayoutFile.string()); - dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n", - device->keyMap.keyCharacterMapFile.string()); - dump.appendFormat(INDENT3 "ConfigurationFile: %s\n", - device->configurationFile.string()); + for (size_t i = 0; i < mDevices.size(); i++) { + const Device* device = mDevices.valueAt(i); + if (mBuiltInKeyboardId == device->id) { + dump.appendFormat(INDENT2 "%d: %s (aka device 0 - built-in keyboard)\n", + device->id, device->identifier.name.string()); + } else { + dump.appendFormat(INDENT2 "%d: %s\n", device->id, + device->identifier.name.string()); } + dump.appendFormat(INDENT3 "Classes: 0x%08x\n", device->classes); + dump.appendFormat(INDENT3 "Path: %s\n", device->path.string()); + dump.appendFormat(INDENT3 "Location: %s\n", device->identifier.location.string()); + dump.appendFormat(INDENT3 "UniqueId: %s\n", device->identifier.uniqueId.string()); + dump.appendFormat(INDENT3 "Identifier: bus=0x%04x, vendor=0x%04x, " + "product=0x%04x, version=0x%04x\n", + device->identifier.bus, device->identifier.vendor, + device->identifier.product, device->identifier.version); + dump.appendFormat(INDENT3 "KeyLayoutFile: %s\n", + device->keyMap.keyLayoutFile.string()); + dump.appendFormat(INDENT3 "KeyCharacterMapFile: %s\n", + device->keyMap.keyCharacterMapFile.string()); + dump.appendFormat(INDENT3 "ConfigurationFile: %s\n", + device->configurationFile.string()); } } // release lock } diff --git a/services/input/EventHub.h b/services/input/EventHub.h index abe1318206ff..0a34e45a565c 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -31,16 +31,16 @@ #include <utils/Errors.h> #include <utils/PropertyMap.h> #include <utils/Vector.h> +#include <utils/KeyedVector.h> #include <linux/input.h> +#include <sys/epoll.h> /* Convenience constants. */ #define BTN_FIRST 0x100 // first button scancode #define BTN_LAST 0x15f // last button scancode -struct pollfd; - namespace android { /* @@ -199,7 +199,11 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; - virtual void reopenDevices() = 0; + /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ + virtual void requestReopenDevices() = 0; + + /* Wakes up getEvents() if it is blocked on a read. */ + virtual void wake() = 0; virtual void dump(String8& dump) = 0; }; @@ -209,8 +213,6 @@ class EventHub : public EventHubInterface public: EventHub(); - status_t errorCheck() const; - virtual uint32_t getDeviceClasses(int32_t deviceId) const; virtual String8 getDeviceName(int32_t deviceId) const; @@ -247,25 +249,16 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector<VirtualKeyDefinition>& outVirtualKeys) const; - virtual void reopenDevices(); + virtual void requestReopenDevices(); + + virtual void wake(); virtual void dump(String8& dump); protected: virtual ~EventHub(); - -private: - bool openPlatformInput(void); - - int openDevice(const char *devicePath); - int closeDevice(const char *devicePath); - int closeDeviceAtIndexLocked(int index); - int scanDir(const char *dirname); - void scanDevices(); - int readNotify(int nfd); - - status_t mError; +private: struct Device { Device* next; @@ -275,9 +268,14 @@ private: const InputDeviceIdentifier identifier; uint32_t classes; - uint8_t* keyBitmask; - uint8_t* relBitmask; - uint8_t* propBitmask; + + uint8_t keyBitmask[(KEY_MAX + 1) / 8]; + uint8_t absBitmask[(ABS_MAX + 1) / 8]; + uint8_t relBitmask[(REL_MAX + 1) / 8]; + uint8_t swBitmask[(SW_MAX + 1) / 8]; + uint8_t ledBitmask[(LED_MAX + 1) / 8]; + uint8_t propBitmask[(INPUT_PROP_MAX + 1) / 8]; + String8 configurationFile; PropertyMap* configuration; VirtualKeyMap* virtualKeyMap; @@ -289,7 +287,19 @@ private: void close(); }; + status_t openDeviceLocked(const char *devicePath); + status_t closeDeviceByPathLocked(const char *devicePath); + + void closeDeviceLocked(Device* device); + void closeAllDevicesLocked(); + + status_t scanDirLocked(const char *dirname); + void scanDevicesLocked(); + status_t readNotifyLocked(); + Device* getDeviceLocked(int32_t deviceId) const; + Device* getDeviceByPathLocked(const char* devicePath) const; + bool hasKeycodeLocked(Device* device, int keycode) const; int32_t getScanCodeStateLocked(Device* device, int32_t scanCode) const; @@ -298,13 +308,13 @@ private: bool markSupportedKeyCodesLocked(Device* device, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) const; - void loadConfiguration(Device* device); - status_t loadVirtualKeyMap(Device* device); - status_t loadKeyMap(Device* device); - void setKeyboardProperties(Device* device, bool builtInKeyboard); - void clearKeyboardProperties(Device* device, bool builtInKeyboard); + void loadConfigurationLocked(Device* device); + status_t loadVirtualKeyMapLocked(Device* device); + status_t loadKeyMapLocked(Device* device); + void setKeyboardPropertiesLocked(Device* device, bool builtInKeyboard); + void clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboard); - bool isExternalDevice(Device* device); + bool isExternalDeviceLocked(Device* device); // Protect all internal state. mutable Mutex mLock; @@ -315,25 +325,36 @@ private: int32_t mNextDeviceId; - // Parallel arrays of fds and devices. - // First index is reserved for inotify. - Vector<struct pollfd> mFds; - Vector<Device*> mDevices; + KeyedVector<int32_t, Device*> mDevices; Device *mOpeningDevices; Device *mClosingDevices; - bool mOpened; bool mNeedToSendFinishedDeviceScan; - volatile int32_t mNeedToReopenDevices; // must be modified atomically + bool mNeedToReopenDevices; bool mNeedToScanDevices; Vector<String8> mExcludedDevices; - // device ids that report particular switches. - int32_t mSwitches[SW_MAX + 1]; + int mEpollFd; + int mINotifyFd; + int mWakeReadPipeFd; + int mWakeWritePipeFd; + + // Ids used for epoll notifications not associated with devices. + static const uint32_t EPOLL_ID_INOTIFY = 0x80000001; + static const uint32_t EPOLL_ID_WAKE = 0x80000002; + + // Epoll FD list size hint. + static const int EPOLL_SIZE_HINT = 8; + + // Maximum number of signalled FDs to handle at a time. + static const int EPOLL_MAX_EVENTS = 16; - // The index of the next file descriptor that needs to be read. - size_t mInputFdIndex; + // The array of pending epoll events and the index of the next event to be handled. + struct epoll_event mPendingEventItems[EPOLL_MAX_EVENTS]; + size_t mPendingEventCount; + size_t mPendingEventIndex; + bool mPendingINotify; // Set to the number of CPUs. int32_t mNumCpus; diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index 28df3fb46594..4a50d8a7eda8 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -3395,6 +3395,7 @@ void InputDispatcher::dumpDispatchStateLocked(String8& dump) { window.frameRight, window.frameBottom, window.scaleFactor); dumpRegion(dump, window.touchableRegion); + dump.appendFormat(", inputFeatures=0x%08x", window.inputFeatures); dump.appendFormat(", ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", window.ownerPid, window.ownerUid, window.dispatchingTimeout / 1000000.0); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 15bb300d3bae..3e4c66691cb0 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -38,7 +38,6 @@ #include "InputReader.h" -#include <cutils/atomic.h> #include <cutils/log.h> #include <ui/Keyboard.h> #include <ui/VirtualKeyMap.h> @@ -245,8 +244,8 @@ InputReader::InputReader(const sp<EventHubInterface>& eventHub, const sp<InputDispatcherInterface>& dispatcher) : mEventHub(eventHub), mPolicy(policy), mDispatcher(dispatcher), mGlobalMetaState(0), mDisableVirtualKeysTimeout(LLONG_MIN), mNextTimeout(LLONG_MAX), - mRefreshConfiguration(0) { - configure(true /*firstTime*/); + mConfigurationChangesToRefresh(0) { + refreshConfiguration(0); updateGlobalMetaState(); updateInputConfiguration(); } @@ -258,9 +257,16 @@ InputReader::~InputReader() { } void InputReader::loopOnce() { - if (android_atomic_acquire_load(&mRefreshConfiguration)) { - android_atomic_release_store(0, &mRefreshConfiguration); - configure(false /*firstTime*/); + uint32_t changes; + { // acquire lock + AutoMutex _l(mStateLock); + + changes = mConfigurationChangesToRefresh; + mConfigurationChangesToRefresh = 0; + } // release lock + + if (changes) { + refreshConfiguration(changes); } int32_t timeoutMillis = -1; @@ -326,7 +332,7 @@ void InputReader::addDevice(int32_t deviceId) { uint32_t classes = mEventHub->getDeviceClasses(deviceId); InputDevice* device = createDevice(deviceId, name, classes); - device->configure(); + device->configure(&mConfig, 0); if (device->isIgnored()) { LOGI("Device added: id=%d, name='%s' (ignored non-input device)", deviceId, name.string()); @@ -483,12 +489,25 @@ void InputReader::handleConfigurationChanged(nsecs_t when) { mDispatcher->notifyConfigurationChanged(when); } -void InputReader::configure(bool firstTime) { +void InputReader::refreshConfiguration(uint32_t changes) { mPolicy->getReaderConfiguration(&mConfig); mEventHub->setExcludedDevices(mConfig.excludedDeviceNames); - if (!firstTime) { - mEventHub->reopenDevices(); + if (changes) { + LOGI("Reconfiguring input devices. changes=0x%08x", changes); + + if (changes & InputReaderConfiguration::CHANGE_MUST_REOPEN) { + mEventHub->requestReopenDevices(); + } else { + { // acquire device registry reader lock + RWLock::AutoRLock _rl(mDeviceRegistryLock); + + for (size_t i = 0; i < mDevices.size(); i++) { + InputDevice* device = mDevices.valueAt(i); + device->configure(&mConfig, changes); + } + } // release device registry reader lock + } } } @@ -709,8 +728,20 @@ bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, s } // release device registy reader lock } -void InputReader::refreshConfiguration() { - android_atomic_release_store(1, &mRefreshConfiguration); +void InputReader::requestRefreshConfiguration(uint32_t changes) { + if (changes) { + bool needWake; + { // acquire lock + AutoMutex _l(mStateLock); + + needWake = !mConfigurationChangesToRefresh; + mConfigurationChangesToRefresh |= changes; + } // release lock + + if (needWake) { + mEventHub->wake(); + } + } } void InputReader::dump(String8& dump) { @@ -758,6 +789,8 @@ void InputReader::dump(String8& dump) { mConfig.wheelVelocityControlParameters.acceleration); dump.appendFormat(INDENT2 "PointerGesture:\n"); + dump.appendFormat(INDENT3 "Enabled: %s\n", + toString(mConfig.pointerGesturesEnabled)); dump.appendFormat(INDENT3 "QuietInterval: %0.1fms\n", mConfig.pointerGestureQuietInterval * 0.000001f); dump.appendFormat(INDENT3 "DragMinSwitchSpeed: %0.1fpx/s\n", @@ -853,18 +886,20 @@ void InputDevice::addMapper(InputMapper* mapper) { mMappers.add(mapper); } -void InputDevice::configure() { - if (! isIgnored()) { - mContext->getEventHub()->getConfiguration(mId, &mConfiguration); - } - +void InputDevice::configure(const InputReaderConfiguration* config, uint32_t changes) { mSources = 0; - size_t numMappers = mMappers.size(); - for (size_t i = 0; i < numMappers; i++) { - InputMapper* mapper = mMappers[i]; - mapper->configure(); - mSources |= mapper->getSources(); + if (!isIgnored()) { + if (!changes) { // first time only + mContext->getEventHub()->getConfiguration(mId, &mConfiguration); + } + + size_t numMappers = mMappers.size(); + for (size_t i = 0; i < numMappers; i++) { + InputMapper* mapper = mMappers[i]; + mapper->configure(config, changes); + mSources |= mapper->getSources(); + } } } @@ -1008,7 +1043,7 @@ void InputMapper::populateDeviceInfo(InputDeviceInfo* info) { void InputMapper::dump(String8& dump) { } -void InputMapper::configure() { +void InputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) { } void InputMapper::reset() { @@ -1122,16 +1157,18 @@ void KeyboardInputMapper::dump(String8& dump) { } -void KeyboardInputMapper::configure() { - InputMapper::configure(); +void KeyboardInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) { + InputMapper::configure(config, changes); - // Configure basic parameters. - configureParameters(); + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); - // Reset LEDs. - { - AutoMutex _l(mLock); - resetLedStateLocked(); + // Reset LEDs. + { + AutoMutex _l(mLock); + resetLedStateLocked(); + } } } @@ -1409,40 +1446,44 @@ void CursorInputMapper::dump(String8& dump) { } // release lock } -void CursorInputMapper::configure() { - InputMapper::configure(); +void CursorInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) { + InputMapper::configure(config, changes); - // Configure basic parameters. - configureParameters(); + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); - // Configure device mode. - switch (mParameters.mode) { - case Parameters::MODE_POINTER: - mSource = AINPUT_SOURCE_MOUSE; - mXPrecision = 1.0f; - mYPrecision = 1.0f; - mXScale = 1.0f; - mYScale = 1.0f; - mPointerController = getPolicy()->obtainPointerController(getDeviceId()); - break; - case Parameters::MODE_NAVIGATION: - mSource = AINPUT_SOURCE_TRACKBALL; - mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; - mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; - break; - } + // Configure device mode. + switch (mParameters.mode) { + case Parameters::MODE_POINTER: + mSource = AINPUT_SOURCE_MOUSE; + mXPrecision = 1.0f; + mYPrecision = 1.0f; + mXScale = 1.0f; + mYScale = 1.0f; + mPointerController = getPolicy()->obtainPointerController(getDeviceId()); + break; + case Parameters::MODE_NAVIGATION: + mSource = AINPUT_SOURCE_TRACKBALL; + mXPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mYPrecision = TRACKBALL_MOVEMENT_THRESHOLD; + mXScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + mYScale = 1.0f / TRACKBALL_MOVEMENT_THRESHOLD; + break; + } - mVWheelScale = 1.0f; - mHWheelScale = 1.0f; + mVWheelScale = 1.0f; + mHWheelScale = 1.0f; - mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL); - mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL); + mHaveVWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_WHEEL); + mHaveHWheel = getEventHub()->hasRelativeAxis(getDeviceId(), REL_HWHEEL); + } - mPointerVelocityControl.setParameters(getConfig()->pointerVelocityControlParameters); - mWheelXVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters); - mWheelYVelocityControl.setParameters(getConfig()->wheelVelocityControlParameters); + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + mPointerVelocityControl.setParameters(config->pointerVelocityControlParameters); + mWheelXVelocityControl.setParameters(config->wheelVelocityControlParameters); + mWheelYVelocityControl.setParameters(config->wheelVelocityControlParameters); + } } void CursorInputMapper::configureParameters() { @@ -1785,8 +1826,6 @@ void CursorInputMapper::fadePointer() { TouchInputMapper::TouchInputMapper(InputDevice* device) : InputMapper(device) { - mConfig = getConfig(); - mLocked.surfaceOrientation = -1; mLocked.surfaceWidth = -1; mLocked.surfaceHeight = -1; @@ -1923,52 +1962,65 @@ void TouchInputMapper::initializeLocked() { mLocked.orientedRanges.haveDistance = false; mPointerGesture.reset(); - mPointerGesture.pointerVelocityControl.setParameters(mConfig->pointerVelocityControlParameters); } -void TouchInputMapper::configure() { - InputMapper::configure(); +void TouchInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) { + InputMapper::configure(config, changes); - // Configure basic parameters. - configureParameters(); + mConfig = *config; - // Configure sources. - switch (mParameters.deviceType) { - case Parameters::DEVICE_TYPE_TOUCH_SCREEN: - mTouchSource = AINPUT_SOURCE_TOUCHSCREEN; - mPointerSource = 0; - break; - case Parameters::DEVICE_TYPE_TOUCH_PAD: - mTouchSource = AINPUT_SOURCE_TOUCHPAD; - mPointerSource = 0; - break; - case Parameters::DEVICE_TYPE_POINTER: - mTouchSource = AINPUT_SOURCE_TOUCHPAD; - mPointerSource = AINPUT_SOURCE_MOUSE; - break; - default: - LOG_ASSERT(false); - } + if (!changes) { // first time only + // Configure basic parameters. + configureParameters(); + + // Configure sources. + switch (mParameters.deviceType) { + case Parameters::DEVICE_TYPE_TOUCH_SCREEN: + mTouchSource = AINPUT_SOURCE_TOUCHSCREEN; + mPointerSource = 0; + break; + case Parameters::DEVICE_TYPE_TOUCH_PAD: + mTouchSource = AINPUT_SOURCE_TOUCHPAD; + mPointerSource = 0; + break; + case Parameters::DEVICE_TYPE_POINTER: + mTouchSource = AINPUT_SOURCE_TOUCHPAD; + mPointerSource = AINPUT_SOURCE_MOUSE; + break; + default: + LOG_ASSERT(false); + } - // Configure absolute axis information. - configureRawAxes(); + // Configure absolute axis information. + configureRawAxes(); - // Prepare input device calibration. - parseCalibration(); - resolveCalibration(); + // Prepare input device calibration. + parseCalibration(); + resolveCalibration(); - { // acquire lock - AutoMutex _l(mLock); + { // acquire lock + AutoMutex _l(mLock); - // Configure surface dimensions and orientation. - configureSurfaceLocked(); - } // release lock + // Configure surface dimensions and orientation. + configureSurfaceLocked(); + } // release lock + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_SPEED)) { + mPointerGesture.pointerVelocityControl.setParameters( + mConfig.pointerVelocityControlParameters); + } + + if (!changes || (changes & InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT)) { + // Reset the touch screen when pointer gesture enablement changes. + reset(); + } } void TouchInputMapper::configureParameters() { - mParameters.useBadTouchFilter = mConfig->filterTouchEvents; - mParameters.useAveragingTouchFilter = mConfig->filterTouchEvents; - mParameters.useJumpyTouchFilter = mConfig->filterJumpyTouchEvents; + mParameters.useBadTouchFilter = mConfig.filterTouchEvents; + mParameters.useAveragingTouchFilter = mConfig.filterTouchEvents; + mParameters.useJumpyTouchFilter = mConfig.filterJumpyTouchEvents; // Use the pointer presentation mode for devices that do not support distinct // multitouch. The spot-based presentation relies on being able to accurately @@ -2399,14 +2451,14 @@ bool TouchInputMapper::configureSurfaceLocked() { // is applied. // Assume that the touch pad has a square aspect ratio such that movements in // X and Y of the same number of raw units cover the same physical distance. - mLocked.pointerGestureXMovementScale = mConfig->pointerGestureMovementSpeedRatio + mLocked.pointerGestureXMovementScale = mConfig.pointerGestureMovementSpeedRatio * displayDiagonal / rawDiagonal; mLocked.pointerGestureYMovementScale = mLocked.pointerGestureXMovementScale; // Scale zooms to cover a smaller range of the display than movements do. // This value determines the area around the pointer that is affected by freeform // pointer gestures. - mLocked.pointerGestureXZoomScale = mConfig->pointerGestureZoomSpeedRatio + mLocked.pointerGestureXZoomScale = mConfig.pointerGestureZoomSpeedRatio * displayDiagonal / rawDiagonal; mLocked.pointerGestureYZoomScale = mLocked.pointerGestureXZoomScale; @@ -2414,7 +2466,7 @@ bool TouchInputMapper::configureSurfaceLocked() { // of the diagonal axis of the touch pad. Touches that are wider than this are // translated into freeform gestures. mLocked.pointerGestureMaxSwipeWidth = - mConfig->pointerGestureSwipeMaxWidthRatio * rawDiagonal; + mConfig.pointerGestureSwipeMaxWidthRatio * rawDiagonal; // Reset the current pointer gesture. mPointerGesture.reset(); @@ -2900,6 +2952,7 @@ void TouchInputMapper::reset() { if (mPointerController != NULL && mParameters.gestureMode == Parameters::GESTURE_MODE_SPOTS) { + mPointerController->fade(PointerControllerInterface::TRANSITION_GRADUAL); mPointerController->clearSpots(); } } // release lock @@ -2980,7 +3033,7 @@ void TouchInputMapper::syncTouch(nsecs_t when, bool havePointerIds) { touchResult = consumeOffScreenTouches(when, policyFlags); if (touchResult == DISPATCH_TOUCH) { suppressSwipeOntoVirtualKeys(when); - if (mPointerController != NULL) { + if (mPointerController != NULL && mConfig.pointerGesturesEnabled) { dispatchPointerGestures(when, policyFlags, false /*isTimeout*/); } dispatchTouches(when, policyFlags); @@ -3137,8 +3190,8 @@ void TouchInputMapper::suppressSwipeOntoVirtualKeys(nsecs_t when) { // area and accidentally triggers a virtual key. This often happens when virtual keys // are layed out below the screen near to where the on screen keyboard's space bar // is displayed. - if (mConfig->virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) { - mContext->disableVirtualKeysUntil(when + mConfig->virtualKeyQuietTime); + if (mConfig.virtualKeyQuietTime > 0 && mCurrentTouch.pointerCount != 0) { + mContext->disableVirtualKeysUntil(when + mConfig.virtualKeyQuietTime); } } @@ -3686,10 +3739,10 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, #endif if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig->pointerGestureTapDragInterval) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { // The tap/drag timeout has not yet expired. getContext()->requestTimeoutAtTime(mPointerGesture.tapUpTime - + mConfig->pointerGestureTapDragInterval); + + mConfig.pointerGestureTapDragInterval); } else { // The tap is finished. #if DEBUG_GESTURES @@ -3754,7 +3807,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, if (activeTouchId < 0) { mPointerGesture.resetQuietTime(); } else { - isQuietTime = when < mPointerGesture.quietTime + mConfig->pointerGestureQuietInterval; + isQuietTime = when < mPointerGesture.quietTime + mConfig.pointerGestureQuietInterval; if (!isQuietTime) { if ((mPointerGesture.lastGestureMode == PointerGesture::PRESS || mPointerGesture.lastGestureMode == PointerGesture::SWIPE @@ -3783,7 +3836,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, // Case 1: Quiet time. (QUIET) #if DEBUG_GESTURES LOGD("Gestures: QUIET for next %0.3fms", (mPointerGesture.quietTime - + mConfig->pointerGestureQuietInterval - when) * 0.000001f); + + mConfig.pointerGestureQuietInterval - when) * 0.000001f); #endif if (mPointerGesture.lastGestureMode != PointerGesture::QUIET) { *outFinishPreviousGesture = true; @@ -3822,7 +3875,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, // Find the fastest pointer and follow it. if (activeTouchId >= 0 && mCurrentTouch.pointerCount > 1) { int32_t bestId = -1; - float bestSpeed = mConfig->pointerGestureDragMinSwitchSpeed; + float bestSpeed = mConfig.pointerGestureDragMinSwitchSpeed; for (uint32_t i = 0; i < mCurrentTouch.pointerCount; i++) { uint32_t id = mCurrentTouch.pointers[i].id; float vx, vy; @@ -3891,18 +3944,18 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, if ((mPointerGesture.lastGestureMode == PointerGesture::HOVER || mPointerGesture.lastGestureMode == PointerGesture::TAP_DRAG) && mLastTouch.pointerCount == 1) { - if (when <= mPointerGesture.tapDownTime + mConfig->pointerGestureTapInterval) { + if (when <= mPointerGesture.tapDownTime + mConfig.pointerGestureTapInterval) { float x, y; mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig->pointerGestureTapSlop - && fabs(y - mPointerGesture.tapY) <= mConfig->pointerGestureTapSlop) { + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop + && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { #if DEBUG_GESTURES LOGD("Gestures: TAP"); #endif mPointerGesture.tapUpTime = when; getContext()->requestTimeoutAtTime(when - + mConfig->pointerGestureTapDragInterval); + + mConfig.pointerGestureTapDragInterval); mPointerGesture.activeGestureId = 0; mPointerGesture.currentGestureMode = PointerGesture::TAP; @@ -3959,11 +4012,11 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, mPointerGesture.currentGestureMode = PointerGesture::HOVER; if (mPointerGesture.lastGestureMode == PointerGesture::TAP) { - if (when <= mPointerGesture.tapUpTime + mConfig->pointerGestureTapDragInterval) { + if (when <= mPointerGesture.tapUpTime + mConfig.pointerGestureTapDragInterval) { float x, y; mPointerController->getPosition(&x, &y); - if (fabs(x - mPointerGesture.tapX) <= mConfig->pointerGestureTapSlop - && fabs(y - mPointerGesture.tapY) <= mConfig->pointerGestureTapSlop) { + if (fabs(x - mPointerGesture.tapX) <= mConfig.pointerGestureTapSlop + && fabs(y - mPointerGesture.tapY) <= mConfig.pointerGestureTapSlop) { mPointerGesture.currentGestureMode = PointerGesture::TAP_DRAG; } else { #if DEBUG_GESTURES @@ -4057,7 +4110,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, LOG_ASSERT(activeTouchId >= 0); bool settled = when >= mPointerGesture.firstTouchTime - + mConfig->pointerGestureMultitouchSettleInterval; + + mConfig.pointerGestureMultitouchSettleInterval; if (mPointerGesture.lastGestureMode != PointerGesture::PRESS && mPointerGesture.lastGestureMode != PointerGesture::SWIPE && mPointerGesture.lastGestureMode != PointerGesture::FREEFORM) { @@ -4068,7 +4121,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, #if DEBUG_GESTURES LOGD("Gestures: Resetting gesture since additional pointers went down for MULTITOUCH, " "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime - + mConfig->pointerGestureMultitouchSettleInterval - when) + + mConfig.pointerGestureMultitouchSettleInterval - when) * 0.000001f); #endif *outCancelPreviousGesture = true; @@ -4087,7 +4140,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, #if DEBUG_GESTURES LOGD("Gestures: Using centroid as reference for MULTITOUCH, " "settle time remaining %0.3fms", (mPointerGesture.firstTouchTime - + mConfig->pointerGestureMultitouchSettleInterval - when) + + mConfig.pointerGestureMultitouchSettleInterval - when) * 0.000001f); #endif mCurrentTouch.getCentroid(&mPointerGesture.referenceTouchX, @@ -4141,7 +4194,7 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, PointerGesture::Delta& delta = mPointerGesture.referenceDeltas[id]; dist[id] = hypotf(delta.dx * mLocked.pointerGestureXZoomScale, delta.dy * mLocked.pointerGestureYZoomScale); - if (dist[id] > mConfig->pointerGestureMultitouchMinDistance) { + if (dist[id] > mConfig.pointerGestureMultitouchMinDistance) { distOverThreshold += 1; } } @@ -4177,8 +4230,8 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, uint32_t id2 = mCurrentTouch.pointers[1].id; float dist1 = dist[id1]; float dist2 = dist[id2]; - if (dist1 >= mConfig->pointerGestureMultitouchMinDistance - && dist2 >= mConfig->pointerGestureMultitouchMinDistance) { + if (dist1 >= mConfig.pointerGestureMultitouchMinDistance + && dist2 >= mConfig.pointerGestureMultitouchMinDistance) { // Calculate the dot product of the displacement vectors. // When the vectors are oriented in approximately the same direction, // the angle betweeen them is near zero and the cosine of the angle @@ -4191,15 +4244,15 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, float dy2 = delta2.dy * mLocked.pointerGestureYZoomScale; float dot = dx1 * dx2 + dy1 * dy2; float cosine = dot / (dist1 * dist2); // denominator always > 0 - if (cosine >= mConfig->pointerGestureSwipeTransitionAngleCosine) { + if (cosine >= mConfig.pointerGestureSwipeTransitionAngleCosine) { // Pointers are moving in the same direction. Switch to SWIPE. #if DEBUG_GESTURES LOGD("Gestures: PRESS transitioned to SWIPE, " "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " "cosine %0.3f >= %0.3f", - dist1, mConfig->pointerGestureMultitouchMinDistance, - dist2, mConfig->pointerGestureMultitouchMinDistance, - cosine, mConfig->pointerGestureSwipeTransitionAngleCosine); + dist1, mConfig.pointerGestureMultitouchMinDistance, + dist2, mConfig.pointerGestureMultitouchMinDistance, + cosine, mConfig.pointerGestureSwipeTransitionAngleCosine); #endif mPointerGesture.currentGestureMode = PointerGesture::SWIPE; } else { @@ -4208,9 +4261,9 @@ bool TouchInputMapper::preparePointerGestures(nsecs_t when, LOGD("Gestures: PRESS transitioned to FREEFORM, " "dist1 %0.3f >= %0.3f, dist2 %0.3f >= %0.3f, " "cosine %0.3f < %0.3f", - dist1, mConfig->pointerGestureMultitouchMinDistance, - dist2, mConfig->pointerGestureMultitouchMinDistance, - cosine, mConfig->pointerGestureSwipeTransitionAngleCosine); + dist1, mConfig.pointerGestureMultitouchMinDistance, + dist2, mConfig.pointerGestureMultitouchMinDistance, + cosine, mConfig.pointerGestureSwipeTransitionAngleCosine); #endif *outCancelPreviousGesture = true; mPointerGesture.currentGestureMode = PointerGesture::FREEFORM; @@ -5663,85 +5716,87 @@ void JoystickInputMapper::dump(String8& dump) { } } -void JoystickInputMapper::configure() { - InputMapper::configure(); +void JoystickInputMapper::configure(const InputReaderConfiguration* config, uint32_t changes) { + InputMapper::configure(config, changes); - // Collect all axes. - for (int32_t abs = 0; abs <= ABS_MAX; abs++) { - RawAbsoluteAxisInfo rawAxisInfo; - getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo); - if (rawAxisInfo.valid) { - // Map axis. - AxisInfo axisInfo; - bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); - if (!explicitlyMapped) { - // Axis is not explicitly mapped, will choose a generic axis later. - axisInfo.mode = AxisInfo::MODE_NORMAL; - axisInfo.axis = -1; - } + if (!changes) { // first time only + // Collect all axes. + for (int32_t abs = 0; abs <= ABS_MAX; abs++) { + RawAbsoluteAxisInfo rawAxisInfo; + getEventHub()->getAbsoluteAxisInfo(getDeviceId(), abs, &rawAxisInfo); + if (rawAxisInfo.valid) { + // Map axis. + AxisInfo axisInfo; + bool explicitlyMapped = !getEventHub()->mapAxis(getDeviceId(), abs, &axisInfo); + if (!explicitlyMapped) { + // Axis is not explicitly mapped, will choose a generic axis later. + axisInfo.mode = AxisInfo::MODE_NORMAL; + axisInfo.axis = -1; + } - // Apply flat override. - int32_t rawFlat = axisInfo.flatOverride < 0 - ? rawAxisInfo.flat : axisInfo.flatOverride; - - // Calculate scaling factors and limits. - Axis axis; - if (axisInfo.mode == AxisInfo::MODE_SPLIT) { - float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); - float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, 0.0f, highScale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); - } else if (isCenteredAxis(axisInfo.axis)) { - float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, offset, scale, offset, - -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); - } else { - float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); - axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, - scale, 0.0f, scale, 0.0f, - 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); - } + // Apply flat override. + int32_t rawFlat = axisInfo.flatOverride < 0 + ? rawAxisInfo.flat : axisInfo.flatOverride; + + // Calculate scaling factors and limits. + Axis axis; + if (axisInfo.mode == AxisInfo::MODE_SPLIT) { + float scale = 1.0f / (axisInfo.splitValue - rawAxisInfo.minValue); + float highScale = 1.0f / (rawAxisInfo.maxValue - axisInfo.splitValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, + scale, 0.0f, highScale, 0.0f, + 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); + } else if (isCenteredAxis(axisInfo.axis)) { + float scale = 2.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + float offset = avg(rawAxisInfo.minValue, rawAxisInfo.maxValue) * -scale; + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, + scale, offset, scale, offset, + -1.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); + } else { + float scale = 1.0f / (rawAxisInfo.maxValue - rawAxisInfo.minValue); + axis.initialize(rawAxisInfo, axisInfo, explicitlyMapped, + scale, 0.0f, scale, 0.0f, + 0.0f, 1.0f, rawFlat * scale, rawAxisInfo.fuzz * scale); + } - // To eliminate noise while the joystick is at rest, filter out small variations - // in axis values up front. - axis.filter = axis.flat * 0.25f; + // To eliminate noise while the joystick is at rest, filter out small variations + // in axis values up front. + axis.filter = axis.flat * 0.25f; - mAxes.add(abs, axis); + mAxes.add(abs, axis); + } } - } - - // If there are too many axes, start dropping them. - // Prefer to keep explicitly mapped axes. - if (mAxes.size() > PointerCoords::MAX_AXES) { - LOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.", - getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES); - pruneAxes(true); - pruneAxes(false); - } - // Assign generic axis ids to remaining axes. - int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; - size_t numAxes = mAxes.size(); - for (size_t i = 0; i < numAxes; i++) { - Axis& axis = mAxes.editValueAt(i); - if (axis.axisInfo.axis < 0) { - while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 - && haveAxis(nextGenericAxisId)) { - nextGenericAxisId += 1; - } + // If there are too many axes, start dropping them. + // Prefer to keep explicitly mapped axes. + if (mAxes.size() > PointerCoords::MAX_AXES) { + LOGI("Joystick '%s' has %d axes but the framework only supports a maximum of %d.", + getDeviceName().string(), mAxes.size(), PointerCoords::MAX_AXES); + pruneAxes(true); + pruneAxes(false); + } + + // Assign generic axis ids to remaining axes. + int32_t nextGenericAxisId = AMOTION_EVENT_AXIS_GENERIC_1; + size_t numAxes = mAxes.size(); + for (size_t i = 0; i < numAxes; i++) { + Axis& axis = mAxes.editValueAt(i); + if (axis.axisInfo.axis < 0) { + while (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16 + && haveAxis(nextGenericAxisId)) { + nextGenericAxisId += 1; + } - if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { - axis.axisInfo.axis = nextGenericAxisId; - nextGenericAxisId += 1; - } else { - LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " - "have already been assigned to other axes.", - getDeviceName().string(), mAxes.keyAt(i)); - mAxes.removeItemsAt(i--); - numAxes -= 1; + if (nextGenericAxisId <= AMOTION_EVENT_AXIS_GENERIC_16) { + axis.axisInfo.axis = nextGenericAxisId; + nextGenericAxisId += 1; + } else { + LOGI("Ignoring joystick '%s' axis %d because all of the generic axis ids " + "have already been assigned to other axes.", + getDeviceName().string(), mAxes.keyAt(i)); + mAxes.removeItemsAt(i--); + numAxes -= 1; + } } } } diff --git a/services/input/InputReader.h b/services/input/InputReader.h index 55315f70fb25..288ff4e4d386 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -45,6 +45,18 @@ class InputMapper; * Specifies various options that modify the behavior of the input reader. */ struct InputReaderConfiguration { + // Describes changes that have occurred. + enum { + // The pointer speed changed. + CHANGE_POINTER_SPEED = 1 << 0, + + // The pointer gesture control changed. + CHANGE_POINTER_GESTURE_ENABLEMENT = 1 << 1, + + // All devices must be reopened. + CHANGE_MUST_REOPEN = 1 << 31, + }; + // Determines whether to turn on some hacks we have to improve the touch interaction with a // certain device whose screen currently is not all that good. bool filterTouchEvents; @@ -68,6 +80,9 @@ struct InputReaderConfiguration { // Velocity control parameters for mouse wheel movements. VelocityControlParameters wheelVelocityControlParameters; + // True if pointer gestures are enabled. + bool pointerGesturesEnabled; + // Quiet time between certain pointer gesture transitions. // Time to allow for all fingers or buttons to settle into a stable state before // starting a new gesture. @@ -136,6 +151,7 @@ struct InputReaderConfiguration { virtualKeyQuietTime(0), pointerVelocityControlParameters(1.0f, 500.0f, 3000.0f, 3.0f), wheelVelocityControlParameters(1.0f, 15.0f, 50.0f, 4.0f), + pointerGesturesEnabled(true), pointerGestureQuietInterval(100 * 1000000LL), // 100 ms pointerGestureDragMinSwitchSpeed(50), // 50 pixels per second pointerGestureTapInterval(150 * 1000000LL), // 150 ms @@ -235,8 +251,10 @@ public: virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) = 0; - /* Reopens and reconfigures all input devices. */ - virtual void refreshConfiguration() = 0; + /* Requests that a reconfiguration of all input devices. + * The changes flag is a bitfield that indicates what has changed and whether + * the input devices must all be reopened. */ + virtual void requestRefreshConfiguration(uint32_t changes) = 0; }; @@ -260,7 +278,6 @@ public: virtual void requestTimeoutAtTime(nsecs_t when) = 0; virtual InputReaderPolicyInterface* getPolicy() = 0; - virtual const InputReaderConfiguration* getConfig() = 0; virtual InputDispatcherInterface* getDispatcher() = 0; virtual EventHubInterface* getEventHub() = 0; }; @@ -301,7 +318,7 @@ public: virtual bool hasKeys(int32_t deviceId, uint32_t sourceMask, size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags); - virtual void refreshConfiguration(); + virtual void requestRefreshConfiguration(uint32_t changes); protected: // These methods are protected virtual so they can be overridden and instrumented @@ -316,7 +333,6 @@ private: InputReaderConfiguration mConfig; virtual InputReaderPolicyInterface* getPolicy() { return mPolicy.get(); } - virtual const InputReaderConfiguration* getConfig() { return &mConfig; } virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); } virtual EventHubInterface* getEventHub() { return mEventHub.get(); } @@ -365,8 +381,8 @@ private: nsecs_t mNextTimeout; // only accessed by reader thread, not guarded virtual void requestTimeoutAtTime(nsecs_t when); - volatile int32_t mRefreshConfiguration; // atomic - void configure(bool firstTime); + uint32_t mConfigurationChangesToRefresh; // guarded by mStateLock + void refreshConfiguration(uint32_t changes); // state queries typedef int32_t (InputDevice::*GetStateFunc)(uint32_t sourceMask, int32_t code); @@ -408,7 +424,7 @@ public: void dump(String8& dump); void addMapper(InputMapper* mapper); - void configure(); + void configure(const InputReaderConfiguration* config, uint32_t changes); void reset(); void process(const RawEvent* rawEvents, size_t count); void timeoutExpired(nsecs_t when); @@ -460,14 +476,13 @@ public: inline const String8 getDeviceName() { return mDevice->getName(); } inline InputReaderContext* getContext() { return mContext; } inline InputReaderPolicyInterface* getPolicy() { return mContext->getPolicy(); } - inline const InputReaderConfiguration* getConfig() { return mContext->getConfig(); } inline InputDispatcherInterface* getDispatcher() { return mContext->getDispatcher(); } inline EventHubInterface* getEventHub() { return mContext->getEventHub(); } virtual uint32_t getSources() = 0; virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); - virtual void configure(); + virtual void configure(const InputReaderConfiguration* config, uint32_t changes); virtual void reset(); virtual void process(const RawEvent* rawEvent) = 0; virtual void timeoutExpired(nsecs_t when); @@ -514,7 +529,7 @@ public: virtual uint32_t getSources(); virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); - virtual void configure(); + virtual void configure(const InputReaderConfiguration* config, uint32_t changes); virtual void reset(); virtual void process(const RawEvent* rawEvent); @@ -584,7 +599,7 @@ public: virtual uint32_t getSources(); virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); - virtual void configure(); + virtual void configure(const InputReaderConfiguration* config, uint32_t changes); virtual void reset(); virtual void process(const RawEvent* rawEvent); @@ -675,7 +690,7 @@ public: virtual uint32_t getSources(); virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); - virtual void configure(); + virtual void configure(const InputReaderConfiguration* config, uint32_t changes); virtual void reset(); virtual int32_t getKeyCodeState(uint32_t sourceMask, int32_t keyCode); @@ -783,7 +798,7 @@ protected: uint32_t mPointerSource; // sources when reporting pointer gestures // The reader's configuration. - const InputReaderConfiguration* mConfig; + InputReaderConfiguration mConfig; // Immutable configuration parameters. struct Parameters { @@ -1400,7 +1415,7 @@ public: virtual uint32_t getSources(); virtual void populateDeviceInfo(InputDeviceInfo* deviceInfo); virtual void dump(String8& dump); - virtual void configure(); + virtual void configure(const InputReaderConfiguration* config, uint32_t changes); virtual void reset(); virtual void process(const RawEvent* rawEvent); diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h index 93c9b5f268b0..d166ad4f679e 100644 --- a/services/input/InputWindow.h +++ b/services/input/InputWindow.h @@ -127,6 +127,10 @@ struct InputWindow { LAST_SYSTEM_WINDOW = 2999, }; + enum { + INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES = 0x00000001, + }; + sp<InputWindowHandle> inputWindowHandle; sp<InputChannel> inputChannel; String8 name; @@ -147,6 +151,7 @@ struct InputWindow { int32_t layer; int32_t ownerPid; int32_t ownerUid; + int32_t inputFeatures; bool touchableRegionContainsPoint(int32_t x, int32_t y) const; bool frameContainsPoint(int32_t x, int32_t y) const; diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index acda86bc74fe..e349248b1c5a 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -730,7 +730,10 @@ private: virtual void dump(String8& dump) { } - virtual void reopenDevices() { + virtual void requestReopenDevices() { + } + + virtual void wake() { } }; @@ -744,8 +747,6 @@ class FakeInputReaderContext : public InputReaderContext { int32_t mGlobalMetaState; bool mUpdateGlobalMetaStateWasCalled; - InputReaderConfiguration mConfig; - public: FakeInputReaderContext(const sp<EventHubInterface>& eventHub, const sp<InputReaderPolicyInterface>& policy, @@ -783,11 +784,6 @@ private: return mPolicy.get(); } - virtual const InputReaderConfiguration* getConfig() { - mPolicy->getReaderConfiguration(&mConfig); - return &mConfig; - } - virtual InputDispatcherInterface* getDispatcher() { return mDispatcher.get(); } @@ -892,7 +888,7 @@ private: } } - virtual void configure() { + virtual void configure(const InputReaderConfiguration* config, uint32_t changes) { mConfigureWasCalled = true; } @@ -1356,7 +1352,8 @@ TEST_F(InputDeviceTest, ImmutableProperties) { TEST_F(InputDeviceTest, WhenNoMappersAreRegistered_DeviceIsIgnored) { // Configuration. - mDevice->configure(); + InputReaderConfiguration config; + mDevice->configure(&config, 0); // Metadata. ASSERT_TRUE(mDevice->isIgnored()); @@ -1410,7 +1407,8 @@ TEST_F(InputDeviceTest, WhenMappersAreRegistered_DeviceIsNotIgnoredAndForwardsRe mapper2->setMetaState(AMETA_SHIFT_ON); mDevice->addMapper(mapper2); - mDevice->configure(); + InputReaderConfiguration config; + mDevice->configure(&config, 0); String8 propertyValue; ASSERT_TRUE(mDevice->getConfiguration().tryGetProperty(String8("key"), propertyValue)) @@ -1516,8 +1514,10 @@ protected: } void addMapperAndConfigure(InputMapper* mapper) { + InputReaderConfiguration config; + mDevice->addMapper(mapper); - mDevice->configure(); + mDevice->configure(&config, 0); } static void process(InputMapper* mapper, nsecs_t when, int32_t deviceId, int32_t type, diff --git a/services/java/com/android/server/StatusBarManagerService.java b/services/java/com/android/server/StatusBarManagerService.java index 1d2072c2cc27..b1bce50bac97 100644 --- a/services/java/com/android/server/StatusBarManagerService.java +++ b/services/java/com/android/server/StatusBarManagerService.java @@ -16,21 +16,16 @@ package com.android.server; -import android.app.PendingIntent; import android.app.StatusBarManager; import android.content.BroadcastReceiver; -import android.content.ComponentName; import android.content.Context; import android.content.Intent; -import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.net.Uri; import android.os.IBinder; import android.os.RemoteException; import android.os.Binder; import android.os.Handler; -import android.os.SystemClock; import android.util.Slog; import android.view.View; @@ -253,25 +248,23 @@ public class StatusBarManagerService extends IStatusBarService.Stub * Hide or show the on-screen Menu key. Only call this from the window manager, typically in * response to a window with FLAG_NEEDS_MENU_KEY set. */ - public void setMenuKeyVisible(final boolean visible) { + public void topAppWindowChanged(final boolean menuVisible) { enforceStatusBar(); - if (SPEW) Slog.d(TAG, (visible?"showing":"hiding") + " MENU key"); + if (SPEW) Slog.d(TAG, (menuVisible?"showing":"hiding") + " MENU key"); synchronized(mLock) { - if (mMenuVisible != visible) { - mMenuVisible = visible; - mHandler.post(new Runnable() { - public void run() { - if (mBar != null) { - try { - mBar.setMenuKeyVisible(visible); - } catch (RemoteException ex) { - } + mMenuVisible = menuVisible; + mHandler.post(new Runnable() { + public void run() { + if (mBar != null) { + try { + mBar.topAppWindowChanged(menuVisible); + } catch (RemoteException ex) { } } - }); - } + } + }); } } diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index a8201399def0..276900409c07 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -329,6 +329,7 @@ class ServerThread extends Thread { Slog.i(TAG, "Notification Manager"); notification = new NotificationManagerService(context, statusBar, lights); ServiceManager.addService(Context.NOTIFICATION_SERVICE, notification); + networkPolicy.bindNotificationManager(notification); } catch (Throwable e) { Slog.e(TAG, "Failure starting Notification Manager", e); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 4f0265c6b432..f1098a8b04e7 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -7667,7 +7667,8 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" COMP_SPEC may also be a component name (com.foo/.myApp),"); pw.println(" a partial substring in a component name, an"); pw.println(" ActivityRecord hex object identifier, or"); - pw.println(" \"all\" for all objects"); + pw.println(" \"all\" for all objects, or"); + pw.println(" \"top\" for the top activity."); pw.println(" -a: include all available server state."); pw.println(" -c: include client state."); return; @@ -8221,6 +8222,13 @@ public final class ActivityManagerService extends ActivityManagerNative activities.add(r1); } } + } else if ("top".equals(name)) { + synchronized (this) { + final int N = mMainStack.mHistory.size(); + if (N > 0) { + activities.add((ActivityRecord)mMainStack.mHistory.get(N-1)); + } + } } else { ComponentName componentName = ComponentName.unflattenFromString(name); int objectId = 0; diff --git a/services/java/com/android/server/am/CompatModePackages.java b/services/java/com/android/server/am/CompatModePackages.java index 1334bcd7147a..f65648624229 100644 --- a/services/java/com/android/server/am/CompatModePackages.java +++ b/services/java/com/android/server/am/CompatModePackages.java @@ -131,13 +131,7 @@ public class CompatModePackages { final boolean mayCompat = !ci.alwaysSupportsScreen() && !ci.neverSupportsScreen(); - if (!updated) { - // First time -- if the app may run in compat mode, enable that - // by default. - if (mayCompat) { - setPackageScreenCompatModeLocked(ai, ActivityManager.COMPAT_MODE_ENABLED); - } - } else { + if (updated) { // Update -- if the app no longer can run in compat mode, clear // any current settings for it. if (!mayCompat && mPackages.containsKey(packageName)) { diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 99569a8449dd..216433472473 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -20,9 +20,11 @@ import static android.Manifest.permission.CONNECTIVITY_INTERNAL; import static android.Manifest.permission.DUMP; import static android.Manifest.permission.MANAGE_APP_TOKENS; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static android.Manifest.permission.READ_NETWORK_USAGE_HISTORY; import static android.Manifest.permission.READ_PHONE_STATE; import static android.net.ConnectivityManager.CONNECTIVITY_ACTION; import static android.net.NetworkPolicy.LIMIT_DISABLED; +import static android.net.NetworkPolicy.WARNING_DISABLED; import static android.net.NetworkPolicyManager.POLICY_NONE; import static android.net.NetworkPolicyManager.POLICY_REJECT_PAID_BACKGROUND; import static android.net.NetworkPolicyManager.RULE_ALLOW_ALL; @@ -30,19 +32,27 @@ import static android.net.NetworkPolicyManager.RULE_REJECT_PAID; import static android.net.NetworkPolicyManager.computeLastCycleBoundary; import static android.net.NetworkPolicyManager.dumpPolicy; import static android.net.NetworkPolicyManager.dumpRules; +import static android.net.NetworkPolicyManager.isUidValidForPolicy; +import static android.net.TrafficStats.TEMPLATE_MOBILE_3G_LOWER; +import static android.net.TrafficStats.TEMPLATE_MOBILE_4G; import static android.net.TrafficStats.TEMPLATE_MOBILE_ALL; import static android.net.TrafficStats.isNetworkTemplateMobile; import static android.text.format.DateUtils.DAY_IN_MILLIS; import static com.android.internal.util.Preconditions.checkNotNull; +import static com.android.server.net.NetworkStatsService.ACTION_NETWORK_STATS_UPDATED; import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; import static org.xmlpull.v1.XmlPullParser.START_TAG; import android.app.IActivityManager; +import android.app.INotificationManager; import android.app.IProcessObserver; +import android.app.Notification; +import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.net.ConnectivityManager; import android.net.IConnectivityManager; import android.net.INetworkPolicyListener; @@ -58,6 +68,7 @@ import android.os.IPowerManager; import android.os.RemoteCallbackList; import android.os.RemoteException; import android.telephony.TelephonyManager; +import android.text.format.Formatter; import android.text.format.Time; import android.util.NtpTrustedTime; import android.util.Slog; @@ -67,6 +78,7 @@ import android.util.SparseIntArray; import android.util.TrustedTime; import android.util.Xml; +import com.android.internal.R; import com.android.internal.os.AtomicFile; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.Objects; @@ -110,6 +122,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final long MB_IN_BYTES = KB_IN_BYTES * 1024; private static final long GB_IN_BYTES = MB_IN_BYTES * 1024; + private static final int TYPE_WARNING = 0x1; + private static final int TYPE_LIMIT = 0x2; + private static final String TAG_POLICY_LIST = "policy-list"; private static final String TAG_NETWORK_POLICY = "network-policy"; private static final String TAG_UID_POLICY = "uid-policy"; @@ -123,6 +138,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private static final String ATTR_UID = "uid"; private static final String ATTR_POLICY = "policy"; + public static final String ACTION_DATA_USAGE_WARNING = + "android.intent.action.DATA_USAGE_WARNING"; + public static final String ACTION_DATA_USAGE_LIMIT = + "android.intent.action.DATA_USAGE_LIMIT"; + private static final long TIME_CACHE_MAX_AGE = DAY_IN_MILLIS; private final Context mContext; @@ -132,6 +152,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { private final TrustedTime mTime; private IConnectivityManager mConnManager; + private INotificationManager mNotifManager; private final Object mRulesLock = new Object(); @@ -192,10 +213,15 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { mConnManager = checkNotNull(connManager, "missing IConnectivityManager"); } + public void bindNotificationManager(INotificationManager notifManager) { + mNotifManager = checkNotNull(notifManager, "missing INotificationManager"); + } + public void systemReady() { synchronized (mRulesLock) { // read policy from disk readPolicyLocked(); + updateNotificationsLocked(); } updateScreenOn(); @@ -221,6 +247,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { ifaceFilter.addAction(CONNECTIVITY_ACTION); mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler); + // listen for warning polling events; currently dispatched by + final IntentFilter statsFilter = new IntentFilter(ACTION_NETWORK_STATS_UPDATED); + mContext.registerReceiver( + mStatsReceiver, statsFilter, READ_NETWORK_USAGE_HISTORY, mHandler); + } private IProcessObserver mProcessObserver = new IProcessObserver.Stub() { @@ -229,6 +260,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); + // skip when UID couldn't have any policy + if (!isUidValidForPolicy(mContext, uid)) return; + synchronized (mRulesLock) { // because a uid can have multiple pids running inside, we need to // remember all pid states and summarize foreground at uid level. @@ -249,6 +283,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // only someone like AMS should only be calling us mContext.enforceCallingOrSelfPermission(MANAGE_APP_TOKENS, TAG); + // skip when UID couldn't have any policy + if (!isUidValidForPolicy(mContext, uid)) return; + synchronized (mRulesLock) { // clear records and recompute, when they exist final SparseBooleanArray pidForeground = mUidPidForeground.get(uid); @@ -272,6 +309,158 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { }; /** + * Receiver that watches for {@link INetworkStatsService} updates, which we + * use to check against {@link NetworkPolicy#warningBytes}. + */ + private BroadcastReceiver mStatsReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + // on background handler thread, and verified + // READ_NETWORK_USAGE_HISTORY permission above. + + synchronized (mRulesLock) { + updateNotificationsLocked(); + } + } + }; + + /** + * Check {@link NetworkPolicy} against current {@link INetworkStatsService} + * to show visible notifications as needed. + */ + private void updateNotificationsLocked() { + if (LOGV) Slog.v(TAG, "updateNotificationsLocked()"); + + // try refreshing time source when stale + if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) { + mTime.forceRefresh(); + } + + final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() + : System.currentTimeMillis(); + + // TODO: when switching to kernel notifications, compute next future + // cycle boundary to recompute notifications. + + // examine stats for each policy defined + for (NetworkPolicy policy : mNetworkPolicy) { + final long start = computeLastCycleBoundary(currentTime, policy); + final long end = currentTime; + + final long total; + try { + final NetworkStats stats = mNetworkStats.getSummaryForNetwork( + start, end, policy.networkTemplate, policy.subscriberId); + total = stats.rx[0] + stats.tx[0]; + } catch (RemoteException e) { + Slog.w(TAG, "problem reading summary for template " + policy.networkTemplate); + continue; + } + + if (policy.limitBytes != LIMIT_DISABLED && total >= policy.limitBytes) { + cancelNotification(policy, TYPE_WARNING); + enqueueNotification(policy, TYPE_LIMIT); + } else { + cancelNotification(policy, TYPE_LIMIT); + + if (policy.warningBytes != WARNING_DISABLED && total >= policy.warningBytes) { + enqueueNotification(policy, TYPE_WARNING); + } else { + cancelNotification(policy, TYPE_WARNING); + } + } + } + } + + /** + * Build unique tag that identifies an active {@link NetworkPolicy} + * notification of a specific type, like {@link #TYPE_LIMIT}. + */ + private String buildNotificationTag(NetworkPolicy policy, int type) { + // TODO: consider splicing subscriberId hash into mix + return TAG + ":" + policy.networkTemplate + ":" + type; + } + + /** + * Show notification for combined {@link NetworkPolicy} and specific type, + * like {@link #TYPE_LIMIT}. Okay to call multiple times. + */ + private void enqueueNotification(NetworkPolicy policy, int type) { + final String tag = buildNotificationTag(policy, type); + final Notification.Builder builder = new Notification.Builder(mContext); + builder.setOnlyAlertOnce(true); + builder.setOngoing(true); + + final Resources res = mContext.getResources(); + switch (type) { + case TYPE_WARNING: { + final String title = res.getString(R.string.data_usage_warning_title); + final String body = res.getString(R.string.data_usage_warning_body, + Formatter.formatFileSize(mContext, policy.warningBytes)); + + builder.setSmallIcon(R.drawable.ic_menu_info_details); + builder.setTicker(title); + builder.setContentTitle(title); + builder.setContentText(body); + builder.setContentIntent(PendingIntent.getActivity(mContext, 0, + new Intent(ACTION_DATA_USAGE_WARNING), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + case TYPE_LIMIT: { + final String title; + final String body = res.getString(R.string.data_usage_limit_body); + switch (policy.networkTemplate) { + case TEMPLATE_MOBILE_3G_LOWER: + title = res.getString(R.string.data_usage_3g_limit_title); + break; + case TEMPLATE_MOBILE_4G: + title = res.getString(R.string.data_usage_4g_limit_title); + break; + default: + title = res.getString(R.string.data_usage_mobile_limit_title); + break; + } + + builder.setSmallIcon(com.android.internal.R.drawable.ic_menu_block); + builder.setTicker(title); + builder.setContentTitle(title); + builder.setContentText(body); + builder.setContentIntent(PendingIntent.getActivity(mContext, 0, + new Intent(ACTION_DATA_USAGE_LIMIT), + PendingIntent.FLAG_UPDATE_CURRENT)); + break; + } + } + + // TODO: move to NotificationManager once we can mock it + try { + final String packageName = mContext.getPackageName(); + final int[] idReceived = new int[1]; + mNotifManager.enqueueNotificationWithTag( + packageName, tag, 0x0, builder.getNotification(), idReceived); + } catch (RemoteException e) { + Slog.w(TAG, "problem during enqueueNotification: " + e); + } + } + + /** + * Cancel any notification for combined {@link NetworkPolicy} and specific + * type, like {@link #TYPE_LIMIT}. + */ + private void cancelNotification(NetworkPolicy policy, int type) { + final String tag = buildNotificationTag(policy, type); + + // TODO: move to NotificationManager once we can mock it + try { + final String packageName = mContext.getPackageName(); + mNotifManager.cancelNotificationWithTag(packageName, tag, 0x0); + } catch (RemoteException e) { + Slog.w(TAG, "problem during enqueueNotification: " + e); + } + } + + /** * Receiver that watches for {@link IConnectivityManager} to claim network * interfaces. Used to apply {@link NetworkPolicy} to matching networks. */ @@ -372,8 +561,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { // remaining "quota" is based on usage in current cycle final long quota = Math.max(0, policy.limitBytes - total); - - // TODO: push quota rule down through NMS + //kernelSetIfacesQuota(ifaces, quota); } } } @@ -447,7 +635,11 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int uid = readIntAttribute(in, ATTR_UID); final int policy = readIntAttribute(in, ATTR_POLICY); - mUidPolicy.put(uid, policy); + if (isUidValidForPolicy(mContext, uid)) { + setUidPolicyUnchecked(uid, policy, false); + } else { + Slog.w(TAG, "unable to apply policy to UID " + uid + "; ignoring"); + } } } } @@ -495,6 +687,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { final int uid = mUidPolicy.keyAt(i); final int policy = mUidPolicy.valueAt(i); + // skip writing empty policies + if (policy == POLICY_NONE) continue; + out.startTag(null, TAG_UID_POLICY); writeIntAttribute(out, ATTR_UID, uid); writeIntAttribute(out, ATTR_POLICY, policy); @@ -516,6 +711,14 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void setUidPolicy(int uid, int policy) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + if (!isUidValidForPolicy(mContext, uid)) { + throw new IllegalArgumentException("cannot apply policy to UID " + uid); + } + + setUidPolicyUnchecked(uid, policy, true); + } + + private void setUidPolicyUnchecked(int uid, int policy, boolean persist) { final int oldPolicy; synchronized (mRulesLock) { oldPolicy = getUidPolicy(uid); @@ -523,7 +726,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // uid policy changed, recompute rules and persist policy. updateRulesForUidLocked(uid); - writePolicyLocked(); + if (persist) { + writePolicyLocked(); + } } } @@ -572,6 +777,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } updateIfacesLocked(); + updateNotificationsLocked(); writePolicyLocked(); } } @@ -640,6 +846,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { @Override public boolean isUidForeground(int uid) { + mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + synchronized (mRulesLock) { // only really in foreground when screen is also on return mUidForeground.get(uid, false) && mScreenOn; @@ -696,6 +904,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } private void updateRulesForUidLocked(int uid) { + if (!isUidValidForPolicy(mContext, uid)) return; + final int uidPolicy = getUidPolicy(uid); final boolean uidForeground = isUidForeground(uid); @@ -711,6 +921,9 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // record rule locally to dispatch to new listeners mUidRules.put(uid, uidRules); + final boolean rejectPaid = (uidRules & RULE_REJECT_PAID) != 0; + //kernelSetUidRejectPaid(uid, rejectPaid); + // dispatch changed rule to existing listeners final int length = mListeners.beginBroadcast(); for (int i = 0; i < length; i++) { diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index de69849b7d0c..f762123a8704 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -106,6 +106,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // @VisibleForTesting public static final String ACTION_NETWORK_STATS_POLL = "com.android.server.action.NETWORK_STATS_POLL"; + public static final String ACTION_NETWORK_STATS_UPDATED = + "com.android.server.action.NETWORK_STATS_UPDATED"; private PendingIntent mPollIntent; @@ -203,7 +205,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mContext.registerReceiver(mIfaceReceiver, ifaceFilter, CONNECTIVITY_INTERNAL, mHandler); // listen for periodic polling events - // TODO: switch to stronger internal permission final IntentFilter pollFilter = new IntentFilter(ACTION_NETWORK_STATS_POLL); mContext.registerReceiver(mPollReceiver, pollFilter, READ_NETWORK_USAGE_HISTORY, mHandler); @@ -298,7 +299,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } final NetworkStats stats = new NetworkStats(end - start, 1); - stats.addEntry(IFACE_ALL, UID_ALL, tx, tx); + stats.addEntry(IFACE_ALL, UID_ALL, rx, tx); return stats; } } @@ -446,6 +447,11 @@ public class NetworkStatsService extends INetworkStatsService.Stub { break; } } + + // finally, dispatch updated event to any listeners + final Intent updatedIntent = new Intent(ACTION_NETWORK_STATS_UPDATED); + updatedIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); + mContext.sendBroadcast(updatedIntent, READ_NETWORK_USAGE_HISTORY); } /** diff --git a/services/java/com/android/server/wm/InputMonitor.java b/services/java/com/android/server/wm/InputMonitor.java index 9dc92d3ed74b..3be8af63a1d8 100644 --- a/services/java/com/android/server/wm/InputMonitor.java +++ b/services/java/com/android/server/wm/InputMonitor.java @@ -128,6 +128,8 @@ final class InputMonitor { inputWindow.layer = mService.mDragState.getDragLayerLw(); inputWindow.ownerPid = Process.myPid(); inputWindow.ownerUid = Process.myUid(); + inputWindow.inputFeatures = 0; + inputWindow.scaleFactor = 1.0f; // The drag window covers the entire display inputWindow.frameLeft = 0; @@ -204,7 +206,8 @@ final class InputMonitor { inputWindow.layer = child.mLayer; inputWindow.ownerPid = child.mSession.mPid; inputWindow.ownerUid = child.mSession.mUid; - + inputWindow.inputFeatures = child.mAttrs.inputFeatures; + final Rect frame = child.mFrame; inputWindow.frameLeft = frame.left; inputWindow.frameTop = frame.top; diff --git a/services/java/com/android/server/wm/InputWindow.java b/services/java/com/android/server/wm/InputWindow.java index 578120eeaac0..655d73400293 100644 --- a/services/java/com/android/server/wm/InputWindow.java +++ b/services/java/com/android/server/wm/InputWindow.java @@ -75,6 +75,9 @@ public final class InputWindow { public int ownerPid; public int ownerUid; + // Window input features. + public int inputFeatures; + public void recycle() { inputWindowHandle = null; inputChannel = null; diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 7c5084fda650..8a46ab0fee05 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -234,6 +234,9 @@ private: // Pointer speed. int32_t pointerSpeed; + // True if pointer gestures are enabled. + bool pointerGesturesEnabled; + // Sprite controller singleton, created on first use. sp<SpriteController> spriteController; @@ -274,6 +277,7 @@ NativeInputManager::NativeInputManager(jobject contextObj, mLocked.systemUiVisibility = ASYSTEM_UI_VISIBILITY_STATUS_BAR_VISIBLE; mLocked.pointerSpeed = 0; + mLocked.pointerGesturesEnabled = true; } sp<EventHub> eventHub = new EventHub(); @@ -443,6 +447,7 @@ void NativeInputManager::getReaderConfiguration(InputReaderConfiguration* outCon outConfig->pointerVelocityControlParameters.scale = exp2f(mLocked.pointerSpeed * POINTER_SPEED_EXPONENT); + outConfig->pointerGesturesEnabled = mLocked.pointerGesturesEnabled; } // release lock } @@ -594,6 +599,7 @@ bool NativeInputManager::isKeyRepeatEnabled() { void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArray) { Vector<InputWindow> windows; + bool newPointerGesturesEnabled = true; jsize length = env->GetArrayLength(windowObjArray); for (jsize i = 0; i < length; i++) { jobject windowObj = env->GetObjectArrayElement(windowObjArray, i); @@ -606,11 +612,29 @@ void NativeInputManager::setInputWindows(JNIEnv* env, jobjectArray windowObjArra android_server_InputWindow_toNative(env, windowObj, &window); if (window.inputChannel == NULL) { windows.pop(); + } else if (window.hasFocus) { + if (window.inputFeatures & InputWindow::INPUT_FEATURE_DISABLE_TOUCH_PAD_GESTURES) { + newPointerGesturesEnabled = false; + } } env->DeleteLocalRef(windowObj); } mInputManager->getDispatcher()->setInputWindows(windows); + + uint32_t changes = 0; + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.pointerGesturesEnabled != newPointerGesturesEnabled) { + mLocked.pointerGesturesEnabled = newPointerGesturesEnabled; + changes |= InputReaderConfiguration::CHANGE_POINTER_GESTURE_ENABLEMENT; + } + } // release lock + + if (changes) { + mInputManager->getReader()->requestRefreshConfiguration(changes); + } } void NativeInputManager::setFocusedApplication(JNIEnv* env, jobject applicationObj) { @@ -650,14 +674,19 @@ void NativeInputManager::updateInactivityTimeoutLocked(const sp<PointerControlle } void NativeInputManager::setPointerSpeed(int32_t speed) { - AutoMutex _l(mLock); + { // acquire lock + AutoMutex _l(mLock); + + if (mLocked.pointerSpeed == speed) { + return; + } - if (mLocked.pointerSpeed != speed) { LOGI("Setting pointer speed to %d.", speed); mLocked.pointerSpeed = speed; + } // release lock - mInputManager->getReader()->refreshConfiguration(); - } + mInputManager->getReader()->requestRefreshConfiguration( + InputReaderConfiguration::CHANGE_POINTER_SPEED); } bool NativeInputManager::isScreenOn() { diff --git a/services/jni/com_android_server_InputWindow.cpp b/services/jni/com_android_server_InputWindow.cpp index 012ce21d4c28..0426f6357248 100644 --- a/services/jni/com_android_server_InputWindow.cpp +++ b/services/jni/com_android_server_InputWindow.cpp @@ -48,6 +48,7 @@ static struct { jfieldID layer; jfieldID ownerPid; jfieldID ownerUid; + jfieldID inputFeatures; } gInputWindowClassInfo; @@ -130,6 +131,8 @@ void android_server_InputWindow_toNative( gInputWindowClassInfo.ownerPid); outInputWindow->ownerUid = env->GetIntField(inputWindowObj, gInputWindowClassInfo.ownerUid); + outInputWindow->inputFeatures = env->GetIntField(inputWindowObj, + gInputWindowClassInfo.inputFeatures); } @@ -206,6 +209,9 @@ int register_android_server_InputWindow(JNIEnv* env) { GET_FIELD_ID(gInputWindowClassInfo.ownerUid, clazz, "ownerUid", "I"); + + GET_FIELD_ID(gInputWindowClassInfo.inputFeatures, clazz, + "inputFeatures", "I"); return 0; } diff --git a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java index 2c26f62b9a16..977b412f15b3 100644 --- a/telephony/java/com/android/internal/telephony/DataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/DataConnectionTracker.java @@ -145,8 +145,10 @@ public abstract class DataConnectionTracker extends Handler { public static final String APN_TYPE_KEY = "apnType"; - /** Delay between APN attempts */ - protected static final int APN_DELAY_MILLIS = 5000; + /** Delay between APN attempts. + Note the property override mechanism is there just for testing purpose only. */ + protected static final int APN_DELAY_MILLIS = + SystemProperties.getInt("persist.radio.apn_delay", 5000); // responds to the setInternalDataEnabled call - used internally to turn off data // for example during emergency calls diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java index abd87b89b5ec..318cf3728f54 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteServiceStateTracker.java @@ -125,6 +125,8 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { } } + // Not sure if this is needed in CDMALTE phone. + // mDataRoaming = regCodeIsRoaming(regState); mLteSS.setRadioTechnology(type); mLteSS.setState(regCodeToServiceState(regState)); } else { @@ -210,12 +212,6 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { if (DBG) log("pollStateDone: oldSS=[" + ss + "] newSS=[" + newSS + "]"); - if (cm.getSimState().isSIMReady()) { - // If CSIM is used, check roaming status according to SID/NID - // on EFcdmahome record. - newSS.setRoaming(!isInHomeSidNid(newSS.getSystemId(), newSS.getNetworkId())); - } - boolean hasRegistered = ss.getState() != ServiceState.STATE_IN_SERVICE && newSS.getState() == ServiceState.STATE_IN_SERVICE; @@ -468,33 +464,6 @@ public class CdmaLteServiceStateTracker extends CdmaServiceStateTracker { return provisioningState; } - /** - * Check whether the specified SID and NID pair appears in the HOME SID/NID list - * read from NV or SIM. - * - * @return true if provided sid/nid pair belongs to operator's home network. - */ - private boolean isInHomeSidNid(int sid, int nid) { - // if SID/NID is not available, do not declare roaming. - if (isSidsAllZeros()) return true; - - // length of SID/NID shold be same - if (mHomeSystemId.length != mHomeNetworkId.length) return true; - - if (sid == 0) return true; - - for (int i = 0; i < mHomeSystemId.length; i++) { - // Use SID only if NID is a reserved value. - // SID 0 and NID 0 and 65535 are reserved. (C.0005 2.6.5.2) - if ((mHomeSystemId[i] == sid) && - ((mHomeNetworkId[i] == 0) || (mHomeNetworkId[i] == 65535) || - (nid == 0) || (nid == 65535) || (mHomeNetworkId[i] == nid))) { - return true; - } - } - return false; - } - @Override protected void log(String s) { Log.d(LOG_TAG, "[CdmaLteSST] " + s); diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java index 58ef747b42d0..73b5d97c8b97 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaLteUiccRecords.java @@ -17,6 +17,7 @@ package com.android.internal.telephony.cdma; import static com.android.internal.telephony.TelephonyProperties.PROPERTY_ICC_OPERATOR_ALPHA; import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.IccCardApplication.AppType; import com.android.internal.telephony.IccFileHandler; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.MccTable; @@ -438,9 +439,14 @@ public final class CdmaLteUiccRecords extends SIMRecords { @Override public boolean isProvisioned() { - // Look for MDN and MIN field to determine if the SIM is provisioned. - if ((mMdn != null) && (mMin != null)) return true; - - return false; + // If UICC card has CSIM app, look for MDN and MIN field + // to determine if the SIM is provisioned. Otherwise, + // consider the SIM is provisioned. (for case of ordinal + // USIM only UICC.) + if (phone.mIccCard.isApplicationOnIcc(AppType.APPTYPE_CSIM) && + ((mMdn == null) || (mMin == null))) { + return false; + } + return true; } } diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java index e41985ea3978..5ebdd22e3c7e 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaServiceStateTracker.java @@ -130,8 +130,8 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { private String curPlmn = null; protected String mMdn; - protected int mHomeSystemId[] = null; - protected int mHomeNetworkId[] = null; + private int mHomeSystemId[] = null; + private int mHomeNetworkId[] = null; protected String mMin; protected String mPrlVersion; protected boolean mIsMinInfoReady = false; @@ -1481,7 +1481,7 @@ public class CdmaServiceStateTracker extends ServiceStateTracker { } } - protected boolean isSidsAllZeros() { + private boolean isSidsAllZeros() { if (mHomeSystemId != null) { for (int i=0; i < mHomeSystemId.length; i++) { if (mHomeSystemId[i] != 0) { diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 01d30ebf5a15..3e7ca08f94ea 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -19,6 +19,7 @@ <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> + <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera.autofocus" /> diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java index 3ba6ba46435a..b568781b014f 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java @@ -44,6 +44,7 @@ import android.util.Log; public class RsBenchRS { private static final String TAG = "RsBenchRS"; + private static final String SAMPLE_TEXT = "Bench Test"; int mWidth; int mHeight; @@ -125,7 +126,8 @@ public class RsBenchRS { Font mFontSerif; private Allocation mTextAlloc; - private ScriptField_TexAllocs_s mTextureAllocs; + private ScriptField_ListAllocs_s mTextureAllocs; + private ScriptField_ListAllocs_s mSampleTextAllocs; private ScriptC_rsbench mScript; @@ -445,7 +447,7 @@ public class RsBenchRS { mScript.set_g100by100Mesh(m100by100Mesh); mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4); mScript.set_gWbyHMesh(mWbyHMesh); - mSingleMesh = getSingleMesh(50, 50); + mSingleMesh = getSingleMesh(1, 1); // a unit size mesh mScript.set_gSingleMesh(mSingleMesh); FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus); @@ -547,15 +549,24 @@ public class RsBenchRS { mScript.set_gRenderBufferDepth(offscreen); - mTextureAllocs = new ScriptField_TexAllocs_s(mRS, 100); + mTextureAllocs = new ScriptField_ListAllocs_s(mRS, 100); for (int i = 0; i < 100; i++) { - ScriptField_TexAllocs_s.Item texElem = new ScriptField_TexAllocs_s.Item(); - texElem.texture = loadTextureRGB(R.drawable.globe); + ScriptField_ListAllocs_s.Item texElem = new ScriptField_ListAllocs_s.Item(); + texElem.item = loadTextureRGB(R.drawable.globe); mTextureAllocs.set(texElem, i, false); } mTextureAllocs.copyAll(); mScript.bind_gTexList100(mTextureAllocs); + mSampleTextAllocs = new ScriptField_ListAllocs_s(mRS, 100); + for (int i = 0; i < 100; i++) { + ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item(); + textElem.item = Allocation.createFromString(mRS, SAMPLE_TEXT, Allocation.USAGE_SCRIPT); + mSampleTextAllocs.set(textElem, i, false); + } + mSampleTextAllocs.copyAll(); + mScript.bind_gSampleTextList100(mSampleTextAllocs); + mRS.bindRootScript(mScript); } } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs index b6572fb019c2..0294b31b03b4 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs @@ -23,7 +23,7 @@ const int RS_MSG_TEST_DONE = 100; const int RS_MSG_RESULTS_READY = 101; -const int gMaxModes = 27; +const int gMaxModes = 29; int gMaxLoops; // Allocation to send test names back to java @@ -46,11 +46,12 @@ rs_allocation gTexTransparent; rs_allocation gTexChecker; rs_allocation gTexGlobe; -typedef struct TexAllocs_s { - rs_allocation texture; -} TexAllocs; +typedef struct ListAllocs_s { + rs_allocation item; +} ListAllocs; -TexAllocs *gTexList100; +ListAllocs *gTexList100; +ListAllocs *gSampleTextList100; rs_mesh g10by10Mesh; rs_mesh g100by100Mesh; @@ -195,6 +196,7 @@ static void displayMeshSamples(int meshNum) { rsgBindProgramStore(gProgStoreBlendNone); rsgBindProgramFragment(gProgFragmentTexture); rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); if (meshNum == 0) { @@ -207,7 +209,7 @@ static void displayMeshSamples(int meshNum) { } // Display sample images in a mesh with different texture -static void displayMeshWithMultiTexture(int meshMode) { +static void displayIcons(int meshMode) { bindProgramVertexOrtho(); // Fragment shader with texture @@ -217,22 +219,90 @@ static void displayMeshWithMultiTexture(int meshMode) { int meshCount = (int)pow(10.0f, (float)(meshMode + 1)); + float size = 50.0; + rs_matrix4x4 matrix; + rsMatrixLoadScale(&matrix, size, size, 1.0); + float yPos = 0; for (int y = 0; y < meshCount; y++) { yPos = (y + 1) * 50; float xPos = 0; for (int x = 0; x < meshCount; x++) { xPos = (x + 1) * 50; - rs_matrix4x4 matrix; - rsMatrixLoadTranslate(&matrix, xPos, yPos, 0); - rsgProgramVertexLoadModelMatrix(&matrix); + rs_matrix4x4 transMatrix; + rsMatrixLoadTranslate(&transMatrix, xPos, yPos, 0); + rsMatrixMultiply(&transMatrix, &matrix); + rsgProgramVertexLoadModelMatrix(&transMatrix); int i = (x + y * meshCount) % 100; - rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].texture); + rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item); + rsgDrawMesh(gSingleMesh); + } + } +} + +// Draw meshes in a single page with top left corner coordinates (xStart, yStart) +static void drawMeshInPage(float xStart, float yStart, int wResolution, int hResolution) { + // Draw wResolution * hResolution meshes in one page + float wMargin = 100.0f; + float hMargin = 100.0f; + float xPad = 50.0f; + float yPad = 20.0f; + float size = 100.0f; // size of images + + // font info + rs_font font = gFontSans; + rsgBindFont(font); + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + + // Measure text size + int left = 0, right = 0, top = 0, bottom = 0; + rsgMeasureText(gSampleTextList100[0].item, &left, &right, &top, &bottom); + float textHeight = (float)(top - bottom); + float textWidth = (float)(right - left); + + rs_matrix4x4 matrix; + rsMatrixLoadScale(&matrix, size, size, 1.0); + + for (int y = 0; y < hResolution; y++) { + float yPos = yStart + hMargin + y * size + y * yPad; + for (int x = 0; x < wResolution; x++) { + float xPos = xStart + wMargin + x * size + x * xPad; + + rs_matrix4x4 transMatrix; + rsMatrixLoadTranslate(&transMatrix, xPos + size/2, yPos + size/2, 0); + rsMatrixMultiply(&transMatrix, &matrix); // scale the mesh + rsgProgramVertexLoadModelMatrix(&transMatrix); + + int i = (y * wResolution + x) % 100; + rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item); rsgDrawMesh(gSingleMesh); + rsgDrawText(gSampleTextList100[i].item, xPos, yPos + size + yPad/2 + textHeight); } } } +// Display both images and text as shown in launcher and homepage +// meshMode will decide how many pages we draw +// meshMode = 0: draw 3 pages of meshes +// meshMode = 1: draw 5 pages of meshes +static void displayImageWithText(int wResolution, int hResolution, int meshMode) { + bindProgramVertexOrtho(); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendAlpha); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + + drawMeshInPage(0, 0, wResolution, hResolution); + drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution); + if (meshMode == 1) { + // draw another two pages of meshes + drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution); + } +} + static float gTorusRotation = 0; static void updateModelMatrix(rs_matrix4x4 *matrix, void *buffer) { if (buffer == 0) { @@ -532,8 +602,14 @@ static const char *testNames[] = { "Geo test 25.6k heavy fragment heavy vertex", "Geo test 51.2k heavy fragment heavy vertex", "Geo test 204.8k small tries heavy fragment heavy vertex", - "Mesh with 10 by 10 texture", - "Mesh with 100 by 100 texture", + "UI test with icon display 10 by 10", // 25 + "UI test with icon display 100 by 100", // 26 + "UI test with image and text display 3 pages", // 27 + "UI test with image and text display 5 pages", // 28 + "UI test with list view", // 29 +// "UI test with live wallpaper", // 30 +// "Mesh with 10 by 10 texture", +// "Mesh with 100 by 100 texture", }; void getTestName(int testIndex) { @@ -627,10 +703,16 @@ static void runTest(int index) { displayPixelLightSamples(8, true); break; case 25: - displayMeshWithMultiTexture(0); + displayIcons(0); break; case 26: - displayMeshWithMultiTexture(1); + displayIcons(1); + break; + case 27: + displayImageWithText(7, 5, 0); + break; + case 28: + displayImageWithText(7, 5, 1); break; } } @@ -683,6 +765,7 @@ int root(void) { gRenderSurfaceW = rsgGetWidth(); gRenderSurfaceH = rsgGetHeight(); int size = 8; + // draw each frame at (8, 3/4 gRenderSurfaceH) with size drawOffscreenResult((drawPos+=size)%gRenderSurfaceW, (gRenderSurfaceH * 3) / 4, size, size); } @@ -692,11 +775,9 @@ int root(void) { float fps = (float)(frameCount) / ((float)(end - start)*0.001f); rsDebug(testNames[benchMode], fps); gResultBuffer[benchMode] = fps; - drawOffscreenResult(0, 0, gRenderSurfaceW / 2, gRenderSurfaceH / 2); - const char* text = testNames[benchMode]; int left = 0, right = 0, top = 0, bottom = 0; uint width = rsgGetWidth(); diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 8ac7590152b4..223b1fa4912c 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -343,6 +343,9 @@ enum { REQUIRED_ATTR = 0x0101028e, SCREEN_SIZE_ATTR = 0x010102ca, SCREEN_DENSITY_ATTR = 0x010102cb, + REQUIRES_SMALLEST_WIDTH_DP_ATTR = 0x01010364, + COMPATIBLE_WIDTH_LIMIT_DP_ATTR = 0x01010365, + LARGEST_WIDTH_LIMIT_DP_ATTR = 0x01010366, }; const char *getComponentName(String8 &pkgName, String8 &componentName) { @@ -423,6 +426,24 @@ int doDump(Bundle* bundle) return 1; } + // Make a dummy config for retrieving resources... we need to supply + // non-default values for some configs so that we can retrieve resources + // in the app that don't have a default. The most important of these is + // the API version because key resources like icons will have an implicit + // version if they are using newer config types like density. + ResTable_config config; + config.language[0] = 'e'; + config.language[1] = 'n'; + config.country[0] = 'U'; + config.country[1] = 'S'; + config.orientation = ResTable_config::ORIENTATION_PORT; + config.density = ResTable_config::DENSITY_MEDIUM; + config.sdkVersion = 10000; // Very high. + config.screenWidthDp = 320; + config.screenHeightDp = 480; + config.smallestScreenWidthDp = 320; + assets.setConfiguration(config); + const ResTable& res = assets.getResources(false); if (&res == NULL) { fprintf(stderr, "ERROR: dump failed because no resource table was found\n"); @@ -542,6 +563,19 @@ int doDump(Bundle* bundle) } } } else if (strcmp("badging", option) == 0) { + Vector<String8> locales; + res.getLocales(&locales); + + Vector<ResTable_config> configs; + res.getConfigurations(&configs); + SortedVector<int> densities; + const size_t NC = configs.size(); + for (size_t i=0; i<NC; i++) { + int dens = configs[i].density; + if (dens == 0) dens = 160; + densities.add(dens); + } + size_t len; ResXMLTree::event_code_t code; int depth = 0; @@ -598,6 +632,8 @@ int doDump(Bundle* bundle) bool specTouchscreenFeature = false; // touchscreen-related bool specMultitouchFeature = false; bool reqDistinctMultitouchFeature = false; + bool specScreenPortraitFeature = false; + bool specScreenLandscapeFeature = false; // 2.2 also added some other features that apps can request, but that // have no corresponding permission, so we cannot implement any // back-compatibility heuristic for them. The below are thus unnecessary @@ -614,6 +650,9 @@ int doDump(Bundle* bundle) int largeScreen = 1; int xlargeScreen = 1; int anyDensity = 1; + int requiresSmallestWidthDp = 0; + int compatibleWidthLimitDp = 0; + int largestWidthLimitDp = 0; String8 pkg; String8 activityName; String8 activityLabel; @@ -628,10 +667,11 @@ int doDump(Bundle* bundle) } else if (depth < 3) { if (withinActivity && isMainActivity && isLauncherActivity) { const char *aName = getComponentName(pkg, activityName); + printf("launchable-activity:"); if (aName != NULL) { - printf("launchable activity name='%s'", aName); + printf(" name='%s' ", aName); } - printf("label='%s' icon='%s'\n", + printf(" label='%s' icon='%s'\n", activityLabel.string(), activityIcon.string()); } @@ -696,23 +736,51 @@ int doDump(Bundle* bundle) withinApplication = false; if (tag == "application") { withinApplication = true; - String8 label = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); - if (error != "") { - fprintf(stderr, "ERROR getting 'android:label' attribute: %s\n", error.string()); - goto bail; + + String8 label; + const size_t NL = locales.size(); + for (size_t i=0; i<NL; i++) { + const char* localeStr = locales[i].string(); + assets.setLocale(localeStr != NULL ? localeStr : ""); + String8 llabel = getResolvedAttribute(&res, tree, LABEL_ATTR, &error); + if (llabel != "") { + if (localeStr == NULL || strlen(localeStr) == 0) { + label = llabel; + printf("application-label:'%s'\n", llabel.string()); + } else { + if (label == "") { + label = llabel; + } + printf("application-label-%s:'%s'\n", localeStr, + llabel.string()); + } + } } - printf("application: label='%s' ", label.string()); + + ResTable_config tmpConfig = config; + const size_t ND = densities.size(); + for (size_t i=0; i<ND; i++) { + tmpConfig.density = densities[i]; + assets.setConfiguration(tmpConfig); + String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); + if (icon != "") { + printf("application-icon-%d:'%s'\n", densities[i], icon.string()); + } + } + assets.setConfiguration(config); + String8 icon = getResolvedAttribute(&res, tree, ICON_ATTR, &error); if (error != "") { fprintf(stderr, "ERROR getting 'android:icon' attribute: %s\n", error.string()); goto bail; } - printf("icon='%s'\n", icon.string()); int32_t testOnly = getIntegerAttribute(tree, TEST_ONLY_ATTR, &error, 0); if (error != "") { fprintf(stderr, "ERROR getting 'android:testOnly' attribute: %s\n", error.string()); goto bail; } + printf("application: label='%s' ", label.string()); + printf("icon='%s'\n", icon.string()); if (testOnly != 0) { printf("testOnly='%d'\n", testOnly); } @@ -792,6 +860,12 @@ int doDump(Bundle* bundle) XLARGE_SCREEN_ATTR, NULL, 1); anyDensity = getIntegerAttribute(tree, ANY_DENSITY_ATTR, NULL, 1); + requiresSmallestWidthDp = getIntegerAttribute(tree, + REQUIRES_SMALLEST_WIDTH_DP_ATTR, NULL, 0); + compatibleWidthLimitDp = getIntegerAttribute(tree, + COMPATIBLE_WIDTH_LIMIT_DP_ATTR, NULL, 0); + largestWidthLimitDp = getIntegerAttribute(tree, + LARGEST_WIDTH_LIMIT_DP_ATTR, NULL, 0); } else if (tag == "uses-feature") { String8 name = getAttribute(tree, NAME_ATTR, &error); @@ -837,6 +911,10 @@ int doDump(Bundle* bundle) // these have no corresponding permission to check for, // but should imply the foundational telephony permission reqTelephonySubFeature = true; + } else if (name == "android.hardware.screen.portrait") { + specScreenPortraitFeature = true; + } else if (name == "android.hardware.screen.landscape") { + specScreenLandscapeFeature = true; } printf("uses-feature%s:'%s'\n", req ? "" : "-not-required", name.string()); @@ -1103,6 +1181,15 @@ int doDump(Bundle* bundle) printf("uses-feature:'android.hardware.touchscreen.multitouch'\n"); } + // Landscape/portrait-related compatibility logic + if (!specScreenLandscapeFeature && !specScreenPortraitFeature && (targetSdk < 13)) { + // If app has not specified whether it requires portrait or landscape + // and is targeting an API before Honeycomb MR2, then assume it requires + // both. + printf("uses-feature:'android.hardware.screen.portrait'\n"); + printf("uses-feature:'android.hardware.screen.landscape'\n"); + } + if (hasMainActivity) { printf("main\n"); } @@ -1128,6 +1215,34 @@ int doDump(Bundle* bundle) printf("other-services\n"); } + // For modern apps, if screen size buckets haven't been specified + // but the new width ranges have, then infer the buckets from them. + if (smallScreen > 0 && normalScreen > 0 && largeScreen > 0 && xlargeScreen > 0 + && requiresSmallestWidthDp > 0) { + int compatWidth = compatibleWidthLimitDp; + if (compatWidth <= 0) compatWidth = requiresSmallestWidthDp; + if (requiresSmallestWidthDp <= 240 && compatWidth >= 240) { + smallScreen = -1; + } else { + smallScreen = 0; + } + if (requiresSmallestWidthDp <= 320 && compatWidth >= 320) { + normalScreen = -1; + } else { + normalScreen = 0; + } + if (requiresSmallestWidthDp <= 480 && compatWidth >= 480) { + largeScreen = -1; + } else { + largeScreen = 0; + } + if (requiresSmallestWidthDp <= 720 && compatWidth >= 720) { + xlargeScreen = -1; + } else { + xlargeScreen = 0; + } + } + // Determine default values for any unspecified screen sizes, // based on the target SDK of the package. As of 4 (donut) // the screen size support was introduced, so all default to @@ -1146,7 +1261,8 @@ int doDump(Bundle* bundle) xlargeScreen = targetSdk >= 9 ? -1 : 0; } if (anyDensity > 0) { - anyDensity = targetSdk >= 4 ? -1 : 0; + anyDensity = (targetSdk >= 4 || requiresSmallestWidthDp > 0 + || compatibleWidthLimitDp > 0) ? -1 : 0; } printf("supports-screens:"); if (smallScreen != 0) printf(" 'small'"); @@ -1154,12 +1270,18 @@ int doDump(Bundle* bundle) if (largeScreen != 0) printf(" 'large'"); if (xlargeScreen != 0) printf(" 'xlarge'"); printf("\n"); - printf("supports-any-density: '%s'\n", anyDensity ? "true" : "false"); + if (requiresSmallestWidthDp > 0) { + printf("requires-smallest-width:'%d'\n", requiresSmallestWidthDp); + } + if (compatibleWidthLimitDp > 0) { + printf("compatible-width-limit:'%d'\n", compatibleWidthLimitDp); + } + if (largestWidthLimitDp > 0) { + printf("largest-width-limit:'%d'\n", largestWidthLimitDp); + } printf("locales:"); - Vector<String8> locales; - res.getLocales(&locales); const size_t NL = locales.size(); for (size_t i=0; i<NL; i++) { const char* localeStr = locales[i].string(); @@ -1170,16 +1292,6 @@ int doDump(Bundle* bundle) } printf("\n"); - Vector<ResTable_config> configs; - res.getConfigurations(&configs); - SortedVector<int> densities; - const size_t NC = configs.size(); - for (size_t i=0; i<NC; i++) { - int dens = configs[i].density; - if (dens == 0) dens = 160; - densities.add(dens); - } - printf("densities:"); const size_t ND = densities.size(); for (size_t i=0; i<ND; i++) { diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java index afa696cce1de..5ad5d26aee92 100644 --- a/voip/java/com/android/server/sip/SipService.java +++ b/voip/java/com/android/server/sip/SipService.java @@ -60,6 +60,7 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; import java.util.TreeSet; +import java.util.concurrent.Executor; import javax.sip.SipException; /** @@ -68,8 +69,7 @@ import javax.sip.SipException; public final class SipService extends ISipService.Stub { static final String TAG = "SipService"; static final boolean DEBUGV = false; - private static final boolean DEBUG = false; - private static final boolean DEBUG_TIMER = DEBUG && false; + static final boolean DEBUG = false; private static final int EXPIRY_TIME = 3600; private static final int SHORT_EXPIRY_TIME = 10; private static final int MIN_EXPIRY_TIME = 60; @@ -78,13 +78,13 @@ public final class SipService extends ISipService.Stub { private String mLocalIp; private String mNetworkType; private boolean mConnected; - private WakeupTimer mTimer; + private SipWakeupTimer mTimer; private WifiScanProcess mWifiScanProcess; private WifiManager.WifiLock mWifiLock; private boolean mWifiOnly; private IntervalMeasurementProcess mIntervalMeasurementProcess; - private MyExecutor mExecutor; + private MyExecutor mExecutor = new MyExecutor(); // SipProfile URI --> group private Map<String, SipSessionGroupExt> mSipGroups = @@ -118,7 +118,7 @@ public final class SipService extends ISipService.Stub { mMyWakeLock = new SipWakeLock((PowerManager) context.getSystemService(Context.POWER_SERVICE)); - mTimer = new WakeupTimer(context); + mTimer = new SipWakeupTimer(context, mExecutor); mWifiOnly = SipManager.isSipWifiOnly(context); } @@ -159,12 +159,6 @@ public final class SipService extends ISipService.Stub { if (DEBUG) Log.d(TAG, " --- unregister receivers"); } - private MyExecutor getExecutor() { - // create mExecutor lazily - if (mExecutor == null) mExecutor = new MyExecutor(); - return mExecutor; - } - public synchronized SipProfile[] getListOfProfiles() { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.USE_SIP, null); @@ -716,8 +710,8 @@ public final class SipService extends ISipService.Stub { private int mMaxInterval = MAX_INTERVAL; private int mInterval = MAX_INTERVAL / 2; private int mPassCounter = 0; - private WakeupTimer mTimer = new WakeupTimer(mContext); - + private SipWakeupTimer mTimer = new SipWakeupTimer(mContext, mExecutor); + // TODO: fix SipWakeupTimer so that we only use one instance of the timer public IntervalMeasurementProcess(SipSessionGroup group) { try { @@ -1123,7 +1117,7 @@ public final class SipService extends ISipService.Stub { @Override public void onReceive(final Context context, final Intent intent) { // Run the handler in MyExecutor to be protected by wake lock - getExecutor().execute(new Runnable() { + mExecutor.execute(new Runnable() { public void run() { onReceiveInternal(context, intent); } @@ -1227,7 +1221,7 @@ public final class SipService extends ISipService.Stub { @Override public void run() { // delegate to mExecutor - getExecutor().execute(new Runnable() { + mExecutor.execute(new Runnable() { public void run() { realRun(); } @@ -1252,300 +1246,6 @@ public final class SipService extends ISipService.Stub { } } - /** - * Timer that can schedule events to occur even when the device is in sleep. - * Only used internally in this package. - */ - class WakeupTimer extends BroadcastReceiver { - private static final String TAG = "_SIP.WkTimer_"; - private static final String TRIGGER_TIME = "TriggerTime"; - - private Context mContext; - private AlarmManager mAlarmManager; - - // runnable --> time to execute in SystemClock - private TreeSet<MyEvent> mEventQueue = - new TreeSet<MyEvent>(new MyEventComparator()); - - private PendingIntent mPendingIntent; - - public WakeupTimer(Context context) { - mContext = context; - mAlarmManager = (AlarmManager) - context.getSystemService(Context.ALARM_SERVICE); - - IntentFilter filter = new IntentFilter(getAction()); - context.registerReceiver(this, filter); - } - - /** - * Stops the timer. No event can be scheduled after this method is called. - */ - public synchronized void stop() { - mContext.unregisterReceiver(this); - if (mPendingIntent != null) { - mAlarmManager.cancel(mPendingIntent); - mPendingIntent = null; - } - mEventQueue.clear(); - mEventQueue = null; - } - - private synchronized boolean stopped() { - if (mEventQueue == null) { - Log.w(TAG, "Timer stopped"); - return true; - } else { - return false; - } - } - - private void cancelAlarm() { - mAlarmManager.cancel(mPendingIntent); - mPendingIntent = null; - } - - private void recalculatePeriods() { - if (mEventQueue.isEmpty()) return; - - MyEvent firstEvent = mEventQueue.first(); - int minPeriod = firstEvent.mMaxPeriod; - long minTriggerTime = firstEvent.mTriggerTime; - for (MyEvent e : mEventQueue) { - e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod; - int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod - - minTriggerTime); - interval = interval / minPeriod * minPeriod; - e.mTriggerTime = minTriggerTime + interval; - } - TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>( - mEventQueue.comparator()); - newQueue.addAll((Collection<MyEvent>) mEventQueue); - mEventQueue.clear(); - mEventQueue = newQueue; - if (DEBUG_TIMER) { - Log.d(TAG, "queue re-calculated"); - printQueue(); - } - } - - // Determines the period and the trigger time of the new event and insert it - // to the queue. - private void insertEvent(MyEvent event) { - long now = SystemClock.elapsedRealtime(); - if (mEventQueue.isEmpty()) { - event.mTriggerTime = now + event.mPeriod; - mEventQueue.add(event); - return; - } - MyEvent firstEvent = mEventQueue.first(); - int minPeriod = firstEvent.mPeriod; - if (minPeriod <= event.mMaxPeriod) { - event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod; - int interval = event.mMaxPeriod; - interval -= (int) (firstEvent.mTriggerTime - now); - interval = interval / minPeriod * minPeriod; - event.mTriggerTime = firstEvent.mTriggerTime + interval; - mEventQueue.add(event); - } else { - long triggerTime = now + event.mPeriod; - if (firstEvent.mTriggerTime < triggerTime) { - event.mTriggerTime = firstEvent.mTriggerTime; - event.mLastTriggerTime -= event.mPeriod; - } else { - event.mTriggerTime = triggerTime; - } - mEventQueue.add(event); - recalculatePeriods(); - } - } - - /** - * Sets a periodic timer. - * - * @param period the timer period; in milli-second - * @param callback is called back when the timer goes off; the same callback - * can be specified in multiple timer events - */ - public synchronized void set(int period, Runnable callback) { - if (stopped()) return; - - long now = SystemClock.elapsedRealtime(); - MyEvent event = new MyEvent(period, callback, now); - insertEvent(event); - - if (mEventQueue.first() == event) { - if (mEventQueue.size() > 1) cancelAlarm(); - scheduleNext(); - } - - long triggerTime = event.mTriggerTime; - if (DEBUG_TIMER) { - Log.d(TAG, " add event " + event + " scheduled at " - + showTime(triggerTime) + " at " + showTime(now) - + ", #events=" + mEventQueue.size()); - printQueue(); - } - } - - /** - * Cancels all the timer events with the specified callback. - * - * @param callback the callback - */ - public synchronized void cancel(Runnable callback) { - if (stopped() || mEventQueue.isEmpty()) return; - if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback); - - MyEvent firstEvent = mEventQueue.first(); - for (Iterator<MyEvent> iter = mEventQueue.iterator(); - iter.hasNext();) { - MyEvent event = iter.next(); - if (event.mCallback == callback) { - iter.remove(); - if (DEBUG_TIMER) Log.d(TAG, " cancel found:" + event); - } - } - if (mEventQueue.isEmpty()) { - cancelAlarm(); - } else if (mEventQueue.first() != firstEvent) { - cancelAlarm(); - firstEvent = mEventQueue.first(); - firstEvent.mPeriod = firstEvent.mMaxPeriod; - firstEvent.mTriggerTime = firstEvent.mLastTriggerTime - + firstEvent.mPeriod; - recalculatePeriods(); - scheduleNext(); - } - if (DEBUG_TIMER) { - Log.d(TAG, "after cancel:"); - printQueue(); - } - } - - private void scheduleNext() { - if (stopped() || mEventQueue.isEmpty()) return; - - if (mPendingIntent != null) { - throw new RuntimeException("pendingIntent is not null!"); - } - - MyEvent event = mEventQueue.first(); - Intent intent = new Intent(getAction()); - intent.putExtra(TRIGGER_TIME, event.mTriggerTime); - PendingIntent pendingIntent = mPendingIntent = - PendingIntent.getBroadcast(mContext, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT); - mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, - event.mTriggerTime, pendingIntent); - } - - @Override - public void onReceive(Context context, Intent intent) { - // This callback is already protected by AlarmManager's wake lock. - String action = intent.getAction(); - if (getAction().equals(action) - && intent.getExtras().containsKey(TRIGGER_TIME)) { - mPendingIntent = null; - long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L); - execute(triggerTime); - } else { - Log.d(TAG, "unrecognized intent: " + intent); - } - } - - private void printQueue() { - int count = 0; - for (MyEvent event : mEventQueue) { - Log.d(TAG, " " + event + ": scheduled at " - + showTime(event.mTriggerTime) + ": last at " - + showTime(event.mLastTriggerTime)); - if (++count >= 5) break; - } - if (mEventQueue.size() > count) { - Log.d(TAG, " ....."); - } else if (count == 0) { - Log.d(TAG, " <empty>"); - } - } - - private synchronized void execute(long triggerTime) { - if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = " - + showTime(triggerTime) + ": " + mEventQueue.size()); - if (stopped() || mEventQueue.isEmpty()) return; - - for (MyEvent event : mEventQueue) { - if (event.mTriggerTime != triggerTime) break; - if (DEBUG_TIMER) Log.d(TAG, "execute " + event); - - event.mLastTriggerTime = event.mTriggerTime; - event.mTriggerTime += event.mPeriod; - - // run the callback in the handler thread to prevent deadlock - getExecutor().execute(event.mCallback); - } - if (DEBUG_TIMER) { - Log.d(TAG, "after timeout execution"); - printQueue(); - } - scheduleNext(); - } - - private String getAction() { - return toString(); - } - - private String showTime(long time) { - int ms = (int) (time % 1000); - int s = (int) (time / 1000); - int m = s / 60; - s %= 60; - return String.format("%d.%d.%d", m, s, ms); - } - } - - private static class MyEvent { - int mPeriod; - int mMaxPeriod; - long mTriggerTime; - long mLastTriggerTime; - Runnable mCallback; - - MyEvent(int period, Runnable callback, long now) { - mPeriod = mMaxPeriod = period; - mCallback = callback; - mLastTriggerTime = now; - } - - @Override - public String toString() { - String s = super.toString(); - s = s.substring(s.indexOf("@")); - return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":" - + toString(mCallback); - } - - private String toString(Object o) { - String s = o.toString(); - int index = s.indexOf("$"); - if (index > 0) s = s.substring(index + 1); - return s; - } - } - - private static class MyEventComparator implements Comparator<MyEvent> { - public int compare(MyEvent e1, MyEvent e2) { - if (e1 == e2) return 0; - int diff = e1.mMaxPeriod - e2.mMaxPeriod; - if (diff == 0) diff = -1; - return diff; - } - - public boolean equals(Object that) { - return (this == that); - } - } - private static Looper createLooper() { HandlerThread thread = new HandlerThread("SipService.Executor"); thread.start(); @@ -1554,12 +1254,13 @@ public final class SipService extends ISipService.Stub { // Executes immediate tasks in a single thread. // Hold/release wake lock for running tasks - private class MyExecutor extends Handler { + private class MyExecutor extends Handler implements Executor { MyExecutor() { super(createLooper()); } - void execute(Runnable task) { + @Override + public void execute(Runnable task) { mMyWakeLock.acquire(task); Message.obtain(this, 0/* don't care */, task).sendToTarget(); } diff --git a/voip/java/com/android/server/sip/SipWakeupTimer.java b/voip/java/com/android/server/sip/SipWakeupTimer.java new file mode 100644 index 000000000000..9cc26b01aa72 --- /dev/null +++ b/voip/java/com/android/server/sip/SipWakeupTimer.java @@ -0,0 +1,339 @@ +/* + * 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.server.sip; + +import android.app.AlarmManager; +import android.app.PendingIntent; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.os.SystemClock; +import android.util.Log; + +import java.io.IOException; +import java.net.DatagramSocket; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Comparator; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Timer; +import java.util.TimerTask; +import java.util.TreeSet; +import java.util.concurrent.Executor; +import javax.sip.SipException; + +/** + * Timer that can schedule events to occur even when the device is in sleep. + */ +class SipWakeupTimer extends BroadcastReceiver { + private static final String TAG = "_SIP.WkTimer_"; + private static final String TRIGGER_TIME = "TriggerTime"; + private static final boolean DEBUG_TIMER = SipService.DEBUG && false; + + private Context mContext; + private AlarmManager mAlarmManager; + + // runnable --> time to execute in SystemClock + private TreeSet<MyEvent> mEventQueue = + new TreeSet<MyEvent>(new MyEventComparator()); + + private PendingIntent mPendingIntent; + + private Executor mExecutor; + + public SipWakeupTimer(Context context, Executor executor) { + mContext = context; + mAlarmManager = (AlarmManager) + context.getSystemService(Context.ALARM_SERVICE); + + IntentFilter filter = new IntentFilter(getAction()); + context.registerReceiver(this, filter); + mExecutor = executor; + } + + /** + * Stops the timer. No event can be scheduled after this method is called. + */ + public synchronized void stop() { + mContext.unregisterReceiver(this); + if (mPendingIntent != null) { + mAlarmManager.cancel(mPendingIntent); + mPendingIntent = null; + } + mEventQueue.clear(); + mEventQueue = null; + } + + private synchronized boolean stopped() { + if (mEventQueue == null) { + Log.w(TAG, "Timer stopped"); + return true; + } else { + return false; + } + } + + private void cancelAlarm() { + mAlarmManager.cancel(mPendingIntent); + mPendingIntent = null; + } + + private void recalculatePeriods() { + if (mEventQueue.isEmpty()) return; + + MyEvent firstEvent = mEventQueue.first(); + int minPeriod = firstEvent.mMaxPeriod; + long minTriggerTime = firstEvent.mTriggerTime; + for (MyEvent e : mEventQueue) { + e.mPeriod = e.mMaxPeriod / minPeriod * minPeriod; + int interval = (int) (e.mLastTriggerTime + e.mMaxPeriod + - minTriggerTime); + interval = interval / minPeriod * minPeriod; + e.mTriggerTime = minTriggerTime + interval; + } + TreeSet<MyEvent> newQueue = new TreeSet<MyEvent>( + mEventQueue.comparator()); + newQueue.addAll((Collection<MyEvent>) mEventQueue); + mEventQueue.clear(); + mEventQueue = newQueue; + if (DEBUG_TIMER) { + Log.d(TAG, "queue re-calculated"); + printQueue(); + } + } + + // Determines the period and the trigger time of the new event and insert it + // to the queue. + private void insertEvent(MyEvent event) { + long now = SystemClock.elapsedRealtime(); + if (mEventQueue.isEmpty()) { + event.mTriggerTime = now + event.mPeriod; + mEventQueue.add(event); + return; + } + MyEvent firstEvent = mEventQueue.first(); + int minPeriod = firstEvent.mPeriod; + if (minPeriod <= event.mMaxPeriod) { + event.mPeriod = event.mMaxPeriod / minPeriod * minPeriod; + int interval = event.mMaxPeriod; + interval -= (int) (firstEvent.mTriggerTime - now); + interval = interval / minPeriod * minPeriod; + event.mTriggerTime = firstEvent.mTriggerTime + interval; + mEventQueue.add(event); + } else { + long triggerTime = now + event.mPeriod; + if (firstEvent.mTriggerTime < triggerTime) { + event.mTriggerTime = firstEvent.mTriggerTime; + event.mLastTriggerTime -= event.mPeriod; + } else { + event.mTriggerTime = triggerTime; + } + mEventQueue.add(event); + recalculatePeriods(); + } + } + + /** + * Sets a periodic timer. + * + * @param period the timer period; in milli-second + * @param callback is called back when the timer goes off; the same callback + * can be specified in multiple timer events + */ + public synchronized void set(int period, Runnable callback) { + if (stopped()) return; + + long now = SystemClock.elapsedRealtime(); + MyEvent event = new MyEvent(period, callback, now); + insertEvent(event); + + if (mEventQueue.first() == event) { + if (mEventQueue.size() > 1) cancelAlarm(); + scheduleNext(); + } + + long triggerTime = event.mTriggerTime; + if (DEBUG_TIMER) { + Log.d(TAG, " add event " + event + " scheduled at " + + showTime(triggerTime) + " at " + showTime(now) + + ", #events=" + mEventQueue.size()); + printQueue(); + } + } + + /** + * Cancels all the timer events with the specified callback. + * + * @param callback the callback + */ + public synchronized void cancel(Runnable callback) { + if (stopped() || mEventQueue.isEmpty()) return; + if (DEBUG_TIMER) Log.d(TAG, "cancel:" + callback); + + MyEvent firstEvent = mEventQueue.first(); + for (Iterator<MyEvent> iter = mEventQueue.iterator(); + iter.hasNext();) { + MyEvent event = iter.next(); + if (event.mCallback == callback) { + iter.remove(); + if (DEBUG_TIMER) Log.d(TAG, " cancel found:" + event); + } + } + if (mEventQueue.isEmpty()) { + cancelAlarm(); + } else if (mEventQueue.first() != firstEvent) { + cancelAlarm(); + firstEvent = mEventQueue.first(); + firstEvent.mPeriod = firstEvent.mMaxPeriod; + firstEvent.mTriggerTime = firstEvent.mLastTriggerTime + + firstEvent.mPeriod; + recalculatePeriods(); + scheduleNext(); + } + if (DEBUG_TIMER) { + Log.d(TAG, "after cancel:"); + printQueue(); + } + } + + private void scheduleNext() { + if (stopped() || mEventQueue.isEmpty()) return; + + if (mPendingIntent != null) { + throw new RuntimeException("pendingIntent is not null!"); + } + + MyEvent event = mEventQueue.first(); + Intent intent = new Intent(getAction()); + intent.putExtra(TRIGGER_TIME, event.mTriggerTime); + PendingIntent pendingIntent = mPendingIntent = + PendingIntent.getBroadcast(mContext, 0, intent, + PendingIntent.FLAG_UPDATE_CURRENT); + mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, + event.mTriggerTime, pendingIntent); + } + + @Override + public void onReceive(Context context, Intent intent) { + // This callback is already protected by AlarmManager's wake lock. + String action = intent.getAction(); + if (getAction().equals(action) + && intent.getExtras().containsKey(TRIGGER_TIME)) { + mPendingIntent = null; + long triggerTime = intent.getLongExtra(TRIGGER_TIME, -1L); + execute(triggerTime); + } else { + Log.d(TAG, "unrecognized intent: " + intent); + } + } + + private void printQueue() { + int count = 0; + for (MyEvent event : mEventQueue) { + Log.d(TAG, " " + event + ": scheduled at " + + showTime(event.mTriggerTime) + ": last at " + + showTime(event.mLastTriggerTime)); + if (++count >= 5) break; + } + if (mEventQueue.size() > count) { + Log.d(TAG, " ....."); + } else if (count == 0) { + Log.d(TAG, " <empty>"); + } + } + + private synchronized void execute(long triggerTime) { + if (DEBUG_TIMER) Log.d(TAG, "time's up, triggerTime = " + + showTime(triggerTime) + ": " + mEventQueue.size()); + if (stopped() || mEventQueue.isEmpty()) return; + + for (MyEvent event : mEventQueue) { + if (event.mTriggerTime != triggerTime) break; + if (DEBUG_TIMER) Log.d(TAG, "execute " + event); + + event.mLastTriggerTime = event.mTriggerTime; + event.mTriggerTime += event.mPeriod; + + // run the callback in the handler thread to prevent deadlock + mExecutor.execute(event.mCallback); + } + if (DEBUG_TIMER) { + Log.d(TAG, "after timeout execution"); + printQueue(); + } + scheduleNext(); + } + + private String getAction() { + return toString(); + } + + private String showTime(long time) { + int ms = (int) (time % 1000); + int s = (int) (time / 1000); + int m = s / 60; + s %= 60; + return String.format("%d.%d.%d", m, s, ms); + } + + private static class MyEvent { + int mPeriod; + int mMaxPeriod; + long mTriggerTime; + long mLastTriggerTime; + Runnable mCallback; + + MyEvent(int period, Runnable callback, long now) { + mPeriod = mMaxPeriod = period; + mCallback = callback; + mLastTriggerTime = now; + } + + @Override + public String toString() { + String s = super.toString(); + s = s.substring(s.indexOf("@")); + return s + ":" + (mPeriod / 1000) + ":" + (mMaxPeriod / 1000) + ":" + + toString(mCallback); + } + + private String toString(Object o) { + String s = o.toString(); + int index = s.indexOf("$"); + if (index > 0) s = s.substring(index + 1); + return s; + } + } + + private static class MyEventComparator implements Comparator<MyEvent> { + public int compare(MyEvent e1, MyEvent e2) { + if (e1 == e2) return 0; + int diff = e1.mMaxPeriod - e2.mMaxPeriod; + if (diff == 0) diff = -1; + return diff; + } + + public boolean equals(Object that) { + return (this == that); + } + } +} |