diff options
24 files changed, 832 insertions, 68 deletions
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index b7a3f99..f74240f 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -278,6 +278,62 @@ static void writeSourceToMP4(const sp<MediaSource> &source) { writer->stop(); } +static void performSeekTest(const sp<MediaSource> &source) { + CHECK_EQ(OK, source->start()); + + int64_t durationUs; + CHECK(source->getFormat()->findInt64(kKeyDuration, &durationUs)); + + for (int64_t seekTimeUs = 0; seekTimeUs <= durationUs; + seekTimeUs += 60000ll) { + MediaSource::ReadOptions options; + options.setSeekTo( + seekTimeUs, MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); + + MediaBuffer *buffer; + status_t err; + for (;;) { + err = source->read(&buffer, &options); + + options.clearSeekTo(); + + if (err == INFO_FORMAT_CHANGED) { + CHECK(buffer == NULL); + continue; + } + + if (err != OK) { + CHECK(buffer == NULL); + break; + } + + if (buffer->range_length() > 0) { + break; + } + + CHECK(buffer != NULL); + + buffer->release(); + buffer = NULL; + } + + if (err == OK) { + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + + printf("%lld\t%lld\t%lld\n", seekTimeUs, timeUs, seekTimeUs - timeUs); + + buffer->release(); + buffer = NULL; + } else { + printf("ERROR\n"); + break; + } + } + + CHECK_EQ(OK, source->stop()); +} + static void usage(const char *me) { fprintf(stderr, "usage: %s\n", me); fprintf(stderr, " -h(elp)\n"); @@ -291,6 +347,7 @@ static void usage(const char *me) { fprintf(stderr, " -s(oftware) prefer software codec\n"); fprintf(stderr, " -o playback audio\n"); fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n"); + fprintf(stderr, " -k seek test\n"); } int main(int argc, char **argv) { @@ -300,6 +357,7 @@ int main(int argc, char **argv) { bool listComponents = false; bool dumpProfiles = false; bool extractThumbnail = false; + bool seekTest = false; gNumRepetitions = 1; gMaxNumFrames = 0; gReproduceBug = -1; @@ -308,7 +366,7 @@ int main(int argc, char **argv) { gWriteMP4 = false; int res; - while ((res = getopt(argc, argv, "han:lm:b:ptsow:")) >= 0) { + while ((res = getopt(argc, argv, "han:lm:b:ptsow:k")) >= 0) { switch (res) { case 'a': { @@ -375,6 +433,12 @@ int main(int argc, char **argv) { break; } + case 'k': + { + seekTest = true; + break; + } + case '?': case 'h': default: @@ -585,6 +649,8 @@ int main(int argc, char **argv) { if (gWriteMP4) { writeSourceToMP4(mediaSource); + } else if (seekTest) { + performSeekTest(mediaSource); } else { playSource(&client, mediaSource); } diff --git a/core/java/android/animation/Animator.java b/core/java/android/animation/Animator.java index db8b9eb..24c3c88 100755 --- a/core/java/android/animation/Animator.java +++ b/core/java/android/animation/Animator.java @@ -190,6 +190,11 @@ public class Animator extends Animatable { private Object mAnimatedValue = null; /** + * The set of keyframes (time/value pairs) that define this animation. + */ + private KeyframeSet mKeyframeSet = null; + + /** * The type of the values, as determined by the valueFrom/valueTo properties. */ Class mValueType; @@ -222,6 +227,20 @@ public class Animator extends Animatable { } /** + * This constructor takes a set of {@link Keyframe} objects that define the values + * for the animation, along with the times at which those values will hold true during + * the animation. + * + * @param duration The length of the animation, in milliseconds. + * @param keyframes The set of keyframes that define the time/value pairs for the animation. + */ + public Animator(long duration, Keyframe...keyframes) { + mDuration = duration; + mKeyframeSet = new KeyframeSet(keyframes); + mValueType = keyframes[0].getType(); + } + + /** * This function is called immediately before processing the first animation * frame of an animation. If there is a nonzero <code>startDelay</code>, the * function is called after that delay ends. @@ -421,6 +440,15 @@ public class Animator extends Animatable { } /** + * Gets the set of keyframes that define this animation. + * + * @return KeyframeSet The set of keyframes for this animation. + */ + KeyframeSet getKeyframes() { + return mKeyframeSet; + } + + /** * Gets the value that this animation will start from. * * @return Object The starting value for the animation. @@ -751,7 +779,11 @@ public class Animator extends Animatable { */ void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); - mAnimatedValue = mEvaluator.evaluate(fraction, mValueFrom, mValueTo); + if (mKeyframeSet != null) { + mAnimatedValue = mKeyframeSet.getValue(fraction, mEvaluator); + } else { + mAnimatedValue = mEvaluator.evaluate(fraction, mValueFrom, mValueTo); + } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { diff --git a/core/java/android/animation/Keyframe.java b/core/java/android/animation/Keyframe.java new file mode 100644 index 0000000..b98994a --- /dev/null +++ b/core/java/android/animation/Keyframe.java @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.animation; + +import android.view.animation.Interpolator; + +/** + * This class represents timea time/value pair for an animation. The Keyframe class is used + * by {@link Animator} to define the values that the animation target will have over the course + * of the animation. As the time proceeds from one keyframe to the other, the value of the + * target object will animate between the value at the previous keyframe and the value at the + * next keyframe. Each keyframe also holds an option {@link android.view.animation.Interpolator} + * object, which defines the time interpolation over the intervalue preceding the keyframe. + */ +public class Keyframe { + /** + * The time at which mValue will hold true. + */ + private float mFraction; + + /** + * The value of the animation at the time mFraction. + */ + private Object mValue; + + /** + * The type of the value in this Keyframe. This type is determined at construction time, + * based on the type of the <code>value</code> object passed into the constructor. + */ + private Class mValueType; + + /** + * The optional time interpolator for the interval preceding this keyframe. A null interpolator + * (the default) results in linear interpolation over the interval. + */ + private Interpolator mInterpolator = null; + + /** + * Private constructor, called from the public constructors with the additional + * <code>valueType</code> parameter. + * + * @param fraction The time, expressed as a value between 0 and 1, representing the fraction + * of time elapsed of the overall animation duration. + * @param value The value that the object will animate to as the animation time approaches + * the time in this keyframe, and the the value animated from as the time passes the time in + * this keyframe. + * @param valueType The type of the <code>value</code> object. This is used by the + * {@link #getValue()} functionm, which is queried by {@link Animator} to determine + * the type of {@link TypeEvaluator} to use to interpolate between values. + */ + private Keyframe(float fraction, Object value, Class valueType) { + mFraction = fraction; + mValue = value; + mValueType = valueType; + } + + /** + * Constructs a Keyframe object with the given time and value. The time defines the + * time, as a proportion of an overall animation's duration, at which the value will hold true + * for the animation. The value for the animation between keyframes will be calculated as + * an interpolation between the values at those keyframes. + * + * @param fraction The time, expressed as a value between 0 and 1, representing the fraction + * of time elapsed of the overall animation duration. + * @param value The value that the object will animate to as the animation time approaches + * the time in this keyframe, and the the value animated from as the time passes the time in + * this keyframe. + */ + public Keyframe(float fraction, Object value) { + this(fraction, value, Object.class); + } + + /** + * Constructs a Keyframe object with the given time and integer value. The time defines the + * time, as a proportion of an overall animation's duration, at which the value will hold true + * for the animation. The value for the animation between keyframes will be calculated as + * an interpolation between the values at those keyframes. + * + * @param fraction The time, expressed as a value between 0 and 1, representing the fraction + * of time elapsed of the overall animation duration. + * @param value The value that the object will animate to as the animation time approaches + * the time in this keyframe, and the the value animated from as the time passes the time in + * this keyframe. + */ + public Keyframe(float fraction, int value) { + this(fraction, value, int.class); + } + + /** + * Constructs a Keyframe object with the given time and float value. The time defines the + * time, as a proportion of an overall animation's duration, at which the value will hold true + * for the animation. The value for the animation between keyframes will be calculated as + * an interpolation between the values at those keyframes. + * + * @param fraction The time, expressed as a value between 0 and 1, representing the fraction + * of time elapsed of the overall animation duration. + * @param value The value that the object will animate to as the animation time approaches + * the time in this keyframe, and the the value animated from as the time passes the time in + * this keyframe. + */ + public Keyframe(float fraction, float value) { + this(fraction, value, float.class); + } + + /** + * Constructs a Keyframe object with the given time and double value. The time defines the + * time, as a proportion of an overall animation's duration, at which the value will hold true + * for the animation. The value for the animation between keyframes will be calculated as + * an interpolation between the values at those keyframes. + * + * @param fraction The time, expressed as a value between 0 and 1, representing the fraction + * of time elapsed of the overall animation duration. + * @param value The value that the object will animate to as the animation time approaches + * the time in this keyframe, and the the value animated from as the time passes the time in + * this keyframe. + */ + public Keyframe(float fraction, double value) { + this(fraction, value, double.class); + } + + /** + * Gets the value for this Keyframe. + * + * @return The value for this Keyframe. + */ + public Object getValue() { + return mValue; + } + + /** + * Gets the time for this keyframe, as a fraction of the overall animation duration. + * + * @return The time associated with this keyframe, as a fraction of the overall animation + * duration. This should be a value between 0 and 1. + */ + public float getFraction() { + return mFraction; + } + + /** + * Gets the optional interpolator for this Keyframe. A value of <code>null</code> indicates + * that there is no interpolation, which is the same as linear interpolation. + * + * @return The optional interpolator for this Keyframe. + */ + public Interpolator getInterpolator() { + return mInterpolator; + } + + /** + * Sets the optional interpolator for this Keyframe. A value of <code>null</code> indicates + * that there is no interpolation, which is the same as linear interpolation. + * + * @return The optional interpolator for this Keyframe. + */ + public void setInterpolator(Interpolator interpolator) { + mInterpolator = interpolator; + } + + /** + * Gets the type of keyframe. This information is used by Animator to determine the type of + * {@linke TypeEvaluator} to use when calculating values between keyframes. The type is based + * on the type of Keyframe created. For example, {@link IntKeyframe} returns a value of + * <code>int.class</code>. This superclass returns a value of <code>Object.class</code>. + * + * @return The type of the value stored in the Keyframe. + */ + public Class getType() { + return mValueType; + } +} diff --git a/core/java/android/animation/KeyframeSet.java b/core/java/android/animation/KeyframeSet.java new file mode 100644 index 0000000..59e9708 --- /dev/null +++ b/core/java/android/animation/KeyframeSet.java @@ -0,0 +1,99 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.animation; + +import java.util.ArrayList; + +import android.view.animation.Interpolator; + +/** + * This class holds a collection of Keyframe objects and is called by Animator to calculate + * values between those keyframes for a given animation. The class internal to the animation + * package because it is an implementation detail of how Keyframes are stored and used. + */ +class KeyframeSet { + + private int mNumKeyframes; + + private ArrayList<Keyframe> mKeyframes; + + public KeyframeSet(Keyframe... keyframes) { + mKeyframes = new ArrayList<Keyframe>(); + for (Keyframe keyframe : keyframes) { + mKeyframes.add(keyframe); + } + mNumKeyframes = mKeyframes.size(); + } + + /** + * Gets the animated value, given the elapsed fraction of the animation (interpolated by the + * animation's interpolator) and the evaluator used to calculate in-between values. This + * function maps the input fraction to the appropriate keyframe interval and a fraction + * between them and returns the interpolated value. Note that the input fraction may fall + * outside the [0-1] bounds, if the animation's interpolator made that happen (e.g., a + * spring interpolation that might send the fraction past 1.0). We handle this situation by + * just using the two keyframes at the appropriate end when the value is outside those bounds. + * + * @param fraction The elapsed fraction of the animation + * @param evaluator The type evaluator to use when calculating the interpolated values. + * @return The animated value. + */ + public Object getValue(float fraction, TypeEvaluator evaluator) { + // TODO: special-case 2-keyframe common case + + if (fraction <= 0f) { + final Keyframe prevKeyframe = mKeyframes.get(0); + final Keyframe nextKeyframe = mKeyframes.get(1); + final Interpolator interpolator = nextKeyframe.getInterpolator(); + if (interpolator != null) { + fraction = interpolator.getInterpolation(fraction); + } + float intervalFraction = (fraction - prevKeyframe.getFraction()) / + (nextKeyframe.getFraction() - prevKeyframe.getFraction()); + return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(), + nextKeyframe.getValue()); + } else if (fraction >= 1f) { + final Keyframe prevKeyframe = mKeyframes.get(mNumKeyframes - 2); + final Keyframe nextKeyframe = mKeyframes.get(mNumKeyframes - 1); + final Interpolator interpolator = nextKeyframe.getInterpolator(); + if (interpolator != null) { + fraction = interpolator.getInterpolation(fraction); + } + float intervalFraction = (fraction - prevKeyframe.getFraction()) / + (nextKeyframe.getFraction() - prevKeyframe.getFraction()); + return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(), + nextKeyframe.getValue()); + } + Keyframe prevKeyframe = mKeyframes.get(0); + for (int i = 1; i < mNumKeyframes; ++i) { + Keyframe nextKeyframe = mKeyframes.get(i); + if (fraction < nextKeyframe.getFraction()) { + final Interpolator interpolator = nextKeyframe.getInterpolator(); + if (interpolator != null) { + fraction = interpolator.getInterpolation(fraction); + } + float intervalFraction = (fraction - prevKeyframe.getFraction()) / + (nextKeyframe.getFraction() - prevKeyframe.getFraction()); + return evaluator.evaluate(intervalFraction, prevKeyframe.getValue(), + nextKeyframe.getValue()); + } + prevKeyframe = nextKeyframe; + } + // shouldn't get here + return mKeyframes.get(mNumKeyframes - 1).getValue(); + } +} diff --git a/core/java/android/animation/PropertyAnimator.java b/core/java/android/animation/PropertyAnimator.java index 99799f0..937dd58 100644 --- a/core/java/android/animation/PropertyAnimator.java +++ b/core/java/android/animation/PropertyAnimator.java @@ -55,6 +55,9 @@ public final class PropertyAnimator extends Animator { // at a time. private ReentrantReadWriteLock propertyMapLock = new ReentrantReadWriteLock(); + // Used to pass single value to varargs parameter in setter invocation + private Object[] mTmpValueArray = new Object[1]; + /** * Sets the name of the property that will be animated. This name is used to derive @@ -272,6 +275,33 @@ public final class PropertyAnimator extends Animator { } /** + * A constructor that takes <code>Keyframe</code>s. When this constructor + * is called, the system expects to find a setter for <code>propertyName</code> on + * the target object that takes a value of the same type as that returned from + * {@link Keyframe#getType()}. + * . + * + * @param duration The length of the animation, in milliseconds. + * @param target The object whose property is to be animated. This object should + * have a public function on it called <code>setName()</code>, where <code>name</code> is + * the name of the property passed in as the <code>propertyName</code> parameter. + * @param propertyName The name of the property on the <code>target</code> object + * that will be animated. Given this name, the constructor will search for a + * setter on the target object with the name <code>setPropertyName</code>. For example, + * if the constructor is called with <code>propertyName = "foo"</code>, then the + * target object should have a setter function with the name <code>setFoo()</code>. + * @param keyframes The set of keyframes that define the times and values for the animation. + * These keyframes should be ordered in increasing time value, should have a starting + * keyframe with a fraction of 0 and and ending keyframe with a fraction of 1. + */ + public PropertyAnimator(int duration, Object target, String propertyName, + Keyframe...keyframes) { + super(duration, keyframes); + mTarget = target; + mPropertyName = propertyName; + } + + /** * This function is called immediately before processing the first animation * frame of an animation. If there is a nonzero <code>startDelay</code>, the * function is called after that delay ends. @@ -309,7 +339,7 @@ public final class PropertyAnimator extends Animator { propertyMapLock.writeLock().unlock(); } } - if (getValueFrom() == null || getValueTo() == null) { + if (getKeyframes() == null && (getValueFrom() == null || getValueTo() == null)) { // Need to set up the getter if not set by the user, then call it // to get the initial values if (mGetter == null) { @@ -376,7 +406,8 @@ public final class PropertyAnimator extends Animator { super.animateValue(fraction); if (mSetter != null) { try { - mSetter.invoke(mTarget, getAnimatedValue()); + mTmpValueArray[0] = getAnimatedValue(); + mSetter.invoke(mTarget, mTmpValueArray); } catch (InvocationTargetException e) { Log.e("PropertyAnimator", e.toString()); } catch (IllegalAccessException e) { diff --git a/core/java/android/app/LoaderManager.java b/core/java/android/app/LoaderManager.java index c9fdfba..7fd5dde 100644 --- a/core/java/android/app/LoaderManager.java +++ b/core/java/android/app/LoaderManager.java @@ -172,6 +172,7 @@ class LoaderManagerImpl implements LoaderManager { // Let the loader know we're done with it mListenerRegistered = false; mLoader.unregisterListener(this); + mLoader.stopLoading(); } } diff --git a/core/java/android/bluetooth/BluetoothDeviceProfileState.java b/core/java/android/bluetooth/BluetoothDeviceProfileState.java index 8e655e2..1fd7151 100644 --- a/core/java/android/bluetooth/BluetoothDeviceProfileState.java +++ b/core/java/android/bluetooth/BluetoothDeviceProfileState.java @@ -58,19 +58,24 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private static final String TAG = "BluetoothDeviceProfileState"; private static final boolean DBG = true; //STOPSHIP - Change to false + // TODO(): Restructure the state machine to make it scalable with regard to profiles. public static final int CONNECT_HFP_OUTGOING = 1; public static final int CONNECT_HFP_INCOMING = 2; public static final int CONNECT_A2DP_OUTGOING = 3; public static final int CONNECT_A2DP_INCOMING = 4; + public static final int CONNECT_HID_OUTGOING = 5; + public static final int CONNECT_HID_INCOMING = 6; - public static final int DISCONNECT_HFP_OUTGOING = 5; - private static final int DISCONNECT_HFP_INCOMING = 6; - public static final int DISCONNECT_A2DP_OUTGOING = 7; - public static final int DISCONNECT_A2DP_INCOMING = 8; + public static final int DISCONNECT_HFP_OUTGOING = 50; + private static final int DISCONNECT_HFP_INCOMING = 51; + public static final int DISCONNECT_A2DP_OUTGOING = 52; + public static final int DISCONNECT_A2DP_INCOMING = 53; + public static final int DISCONNECT_HID_OUTGOING = 54; + public static final int DISCONNECT_HID_INCOMING = 55; - public static final int UNPAIR = 9; - public static final int AUTO_CONNECT_PROFILES = 10; - public static final int TRANSITION_TO_STABLE = 11; + public static final int UNPAIR = 100; + public static final int AUTO_CONNECT_PROFILES = 101; + public static final int TRANSITION_TO_STABLE = 102; private static final int AUTO_CONNECT_DELAY = 6000; // 6 secs @@ -79,6 +84,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private IncomingHandsfree mIncomingHandsfree = new IncomingHandsfree(); private IncomingA2dp mIncomingA2dp = new IncomingA2dp(); private OutgoingA2dp mOutgoingA2dp = new OutgoingA2dp(); + private OutgoingHid mOutgoingHid = new OutgoingHid(); + private IncomingHid mIncomingHid = new IncomingHid(); private Context mContext; private BluetoothService mService; @@ -89,6 +96,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine private BluetoothDevice mDevice; private int mHeadsetState; private int mA2dpState; + private int mHidState; private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { @Override @@ -125,6 +133,19 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine newState == BluetoothA2dp.STATE_DISCONNECTED) { sendMessage(TRANSITION_TO_STABLE); } + } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + int oldState = + intent.getIntExtra(BluetoothInputDevice.EXTRA_PREVIOUS_INPUT_DEVICE_STATE, 0); + mHidState = newState; + if (oldState == BluetoothInputDevice.STATE_CONNECTED && + newState == BluetoothInputDevice.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_HID_INCOMING); + } + if (newState == BluetoothInputDevice.STATE_CONNECTED || + newState == BluetoothInputDevice.STATE_DISCONNECTED) { + sendMessage(TRANSITION_TO_STABLE); + } } else if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { if (!getCurrentState().equals(mBondedDevice)) { Log.e(TAG, "State is: " + getCurrentState()); @@ -165,6 +186,8 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine addState(mIncomingHandsfree); addState(mIncomingA2dp); addState(mOutgoingA2dp); + addState(mOutgoingHid); + addState(mIncomingHid); setInitialState(mBondedDevice); IntentFilter filter = new IntentFilter(); @@ -172,6 +195,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); + filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); mContext.registerReceiver(mBroadcastReceiver, filter); @@ -224,6 +248,14 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: transitionTo(mIncomingA2dp); break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + transitionTo(mOutgoingHid); + break; + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_INCOMING: + transitionTo(mIncomingHid); + break; case UNPAIR: if (mHeadsetState != BluetoothHeadset.STATE_DISCONNECTED) { sendMessage(DISCONNECT_HFP_OUTGOING); @@ -233,6 +265,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine sendMessage(DISCONNECT_A2DP_OUTGOING); deferMessage(message); break; + } else if (mHidState != BluetoothInputDevice.STATE_DISCONNECTED) { + sendMessage(DISCONNECT_HID_OUTGOING); + deferMessage(message); + break; } processCommand(UNPAIR); break; @@ -254,6 +290,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine mA2dpService.getConnectedSinks().length == 0) { mA2dpService.connectSink(mDevice); } + if (mService.getInputDevicePriority(mDevice) == + BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { + mService.connectInputDevice(mDevice); + } } break; case TRANSITION_TO_STABLE: @@ -342,6 +382,23 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine deferMessage(deferMsg); } break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + transitionTo(mIncomingHid); + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_HID_INCOMING: + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -409,6 +466,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine // If this causes incoming HFP to fail, it is more of a headset problem // since both connections are incoming ones. break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_INCOMING: + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -496,6 +560,23 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + transitionTo(mIncomingHid); + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_HID_INCOMING: + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -561,6 +642,13 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case DISCONNECT_A2DP_INCOMING: // Ignore, will be handled by Bluez break; + case CONNECT_HID_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_INCOMING: + break; // ignore case UNPAIR: case AUTO_CONNECT_PROFILES: deferMessage(message); @@ -576,6 +664,143 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine } + private class OutgoingHid extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering OutgoingHid state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_HID_OUTGOING && + mCommand != DISCONNECT_HID_OUTGOING) { + Log.e(TAG, "Error: OutgoingHid state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("OutgoingHid State->Processing Message: " + message.what); + Message deferMsg = new Message(); + switch(message.what) { + // defer all outgoing messages + case CONNECT_HFP_OUTGOING: + case CONNECT_A2DP_OUTGOING: + case CONNECT_HID_OUTGOING: + case DISCONNECT_HFP_OUTGOING: + case DISCONNECT_A2DP_OUTGOING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + + case CONNECT_HFP_INCOMING: + transitionTo(mIncomingHandsfree); + case CONNECT_A2DP_INCOMING: + transitionTo(mIncomingA2dp); + + // Don't cancel HID outgoing as there is no guarantee it + // will get canceled. + // It might already be connected but we might not have got the + // INPUT_DEVICE_STATE_CHANGE. Hence, no point disconnecting here. + // The worst case, the connection will fail, retry. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case CONNECT_HID_INCOMING: + // Bluez will take care of the conflicts + transitionTo(mIncomingHid); + break; + + case DISCONNECT_HFP_INCOMING: + case DISCONNECT_A2DP_INCOMING: + // At this point, we are already disconnected + // with HFP. Sometimes HID connection can + // fail due to the disconnection of HFP. So add a retry + // for the HID. + if (mStatus) { + deferMsg.what = mCommand; + deferMessage(deferMsg); + } + break; + case DISCONNECT_HID_INCOMING: + // Ignore, will be handled by Bluez + break; + + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + + private class IncomingHid extends HierarchicalState { + private boolean mStatus = false; + private int mCommand; + + @Override + protected void enter() { + log("Entering IncomingHid state with: " + getCurrentMessage().what); + mCommand = getCurrentMessage().what; + if (mCommand != CONNECT_HID_INCOMING && + mCommand != DISCONNECT_HID_INCOMING) { + Log.e(TAG, "Error: IncomingHid state with command:" + mCommand); + } + mStatus = processCommand(mCommand); + if (!mStatus) sendMessage(TRANSITION_TO_STABLE); + } + + @Override + protected boolean processMessage(Message message) { + log("IncomingHid State->Processing Message: " + message.what); + Message deferMsg = new Message(); + switch(message.what) { + case CONNECT_HFP_OUTGOING: + case CONNECT_HFP_INCOMING: + case DISCONNECT_HFP_OUTGOING: + case CONNECT_A2DP_INCOMING: + case CONNECT_A2DP_OUTGOING: + case DISCONNECT_A2DP_OUTGOING: + case CONNECT_HID_OUTGOING: + case CONNECT_HID_INCOMING: + case DISCONNECT_HID_OUTGOING: + deferMessage(message); + break; + case DISCONNECT_HFP_INCOMING: + // Shouldn't happen but if does, we can handle it. + // Depends if the headset can handle it. + // Incoming HID will be handled by Bluez, Disconnect HFP + // the socket would have already been closed. + // ignore + break; + case DISCONNECT_HID_INCOMING: + case DISCONNECT_A2DP_INCOMING: + // Ignore, will be handled by Bluez + break; + case UNPAIR: + case AUTO_CONNECT_PROFILES: + deferMessage(message); + break; + case TRANSITION_TO_STABLE: + transitionTo(mBondedDevice); + break; + default: + return NOT_HANDLED; + } + return HANDLED; + } + } + synchronized void cancelCommand(int command) { if (command == CONNECT_HFP_OUTGOING ) { @@ -619,6 +844,10 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine case CONNECT_A2DP_INCOMING: // ignore, Bluez takes care return true; + case CONNECT_HID_OUTGOING: + return mService.connectInputDeviceInternal(mDevice); + case CONNECT_HID_INCOMING: + return true; case DISCONNECT_HFP_OUTGOING: if (!mHeadsetServiceConnected) { deferHeadsetMessage(command); @@ -645,6 +874,15 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return mA2dpService.disconnectSinkInternal(mDevice); } break; + case DISCONNECT_HID_INCOMING: + // ignore + return true; + case DISCONNECT_HID_OUTGOING: + if (mService.getInputDevicePriority(mDevice) == + BluetoothInputDevice.PRIORITY_AUTO_CONNECT) { + mService.setInputDevicePriority(mDevice, BluetoothInputDevice.PRIORITY_ON); + } + return mService.disconnectInputDeviceInternal(mDevice); case UNPAIR: return mService.removeBondInternal(mDevice.getAddress()); default: @@ -653,6 +891,7 @@ public final class BluetoothDeviceProfileState extends HierarchicalStateMachine return false; } + /*package*/ BluetoothDevice getDevice() { return mDevice; } diff --git a/core/java/android/bluetooth/BluetoothProfileState.java b/core/java/android/bluetooth/BluetoothProfileState.java index 946dcaa..ad70d0d 100644 --- a/core/java/android/bluetooth/BluetoothProfileState.java +++ b/core/java/android/bluetooth/BluetoothProfileState.java @@ -43,8 +43,9 @@ public class BluetoothProfileState extends HierarchicalStateMachine { private static final boolean DBG = true; // STOPSHIP - change to false. private static final String TAG = "BluetoothProfileState"; - public static int HFP = 0; - public static int A2DP = 1; + public static final int HFP = 0; + public static final int A2DP = 1; + public static final int HID = 2; private static int TRANSITION_TO_STABLE = 100; @@ -70,6 +71,12 @@ public class BluetoothProfileState extends HierarchicalStateMachine { newState == BluetoothA2dp.STATE_DISCONNECTED)) { sendMessage(TRANSITION_TO_STABLE); } + } else if (action.equals(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED)) { + int newState = intent.getIntExtra(BluetoothInputDevice.EXTRA_INPUT_DEVICE_STATE, 0); + if (mProfile == HID && (newState == BluetoothInputDevice.STATE_CONNECTED || + newState == BluetoothInputDevice.STATE_DISCONNECTED)) { + sendMessage(TRANSITION_TO_STABLE); + } } } }; @@ -84,6 +91,7 @@ public class BluetoothProfileState extends HierarchicalStateMachine { IntentFilter filter = new IntentFilter(); filter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); filter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); + filter.addAction(BluetoothInputDevice.ACTION_INPUT_DEVICE_STATE_CHANGED); context.registerReceiver(mBroadcastReceiver, filter); } diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index 9b7a73d..eb9b62b 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -693,6 +693,26 @@ class BluetoothEventLoop { } } + private void onInputDeviceConnectionResult(String path, boolean result) { + // Success case gets handled by Property Change signal + if (!result) { + String address = mBluetoothService.getAddressFromObjectPath(path); + if (address == null) return; + + boolean connected = false; + BluetoothDevice device = mAdapter.getRemoteDevice(address); + int state = mBluetoothService.getInputDeviceState(device); + if (state == BluetoothInputDevice.STATE_CONNECTING) { + connected = false; + } else if (state == BluetoothInputDevice.STATE_DISCONNECTING) { + connected = true; + } else { + Log.e(TAG, "Error onInputDeviceConnectionResult. State is:" + state); + } + mBluetoothService.handleInputDevicePropertyChange(address, connected); + } + } + private void onRestartRequired() { if (mBluetoothService.isEnabled()) { Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " + diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index d7cd933..acfc0d7 100644 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -132,6 +132,7 @@ public class BluetoothService extends IBluetooth.Stub { private final HashMap<String, BluetoothDeviceProfileState> mDeviceProfileState; private final BluetoothProfileState mA2dpProfileState; private final BluetoothProfileState mHfpProfileState; + private final BluetoothProfileState mHidProfileState; private BluetoothA2dpService mA2dpService; private final HashMap<BluetoothDevice, Integer> mInputDevices; @@ -196,9 +197,11 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceProfileState = new HashMap<String, BluetoothDeviceProfileState>(); mA2dpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.A2DP); mHfpProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HFP); + mHidProfileState = new BluetoothProfileState(mContext, BluetoothProfileState.HID); mHfpProfileState.start(); mA2dpProfileState.start(); + mHidProfileState.start(); IntentFilter filter = new IntentFilter(); registerForAirplaneMode(filter); @@ -1237,17 +1240,32 @@ public class BluetoothService extends IBluetooth.Stub { "Need BLUETOOTH_ADMIN permission"); String objectPath = getObjectPathFromAddress(device.getAddress()); - if (objectPath == null || getConnectedInputDevices().length != 0 || + if (objectPath == null || + getInputDeviceState(device) != BluetoothInputDevice.STATE_DISCONNECTED || getInputDevicePriority(device) == BluetoothInputDevice.PRIORITY_OFF) { return false; } - if(connectInputDeviceNative(objectPath)) { - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); + BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); + if (state != null) { + Message msg = new Message(); + msg.arg1 = BluetoothDeviceProfileState.CONNECT_HID_OUTGOING; + msg.obj = state; + mHidProfileState.sendMessage(msg); return true; } return false; } + public synchronized boolean connectInputDeviceInternal(BluetoothDevice device) { + String objectPath = getObjectPathFromAddress(device.getAddress()); + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTING); + if (!connectInputDeviceNative(objectPath)) { + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTED); + return false; + } + return true; + } + public synchronized boolean disconnectInputDevice(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, "Need BLUETOOTH_ADMIN permission"); @@ -1256,13 +1274,27 @@ public class BluetoothService extends IBluetooth.Stub { if (objectPath == null || getConnectedInputDevices().length == 0) { return false; } - if(disconnectInputDeviceNative(objectPath)) { - handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); + BluetoothDeviceProfileState state = mDeviceProfileState.get(device.getAddress()); + if (state != null) { + Message msg = new Message(); + msg.arg1 = BluetoothDeviceProfileState.DISCONNECT_HID_OUTGOING; + msg.obj = state; + mHidProfileState.sendMessage(msg); return true; } return false; } + public synchronized boolean disconnectInputDeviceInternal(BluetoothDevice device) { + String objectPath = getObjectPathFromAddress(device.getAddress()); + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_DISCONNECTING); + if (!disconnectInputDeviceNative(objectPath)) { + handleInputDeviceStateChange(device, BluetoothInputDevice.STATE_CONNECTED); + return false; + } + return true; + } + public synchronized int getInputDeviceState(BluetoothDevice device) { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 15b8335..3c161ca 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -896,11 +896,25 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return super.dispatchTouchEvent(ev); } + // Calculate the offset point into the target's local coordinates + float xc; + float yc; + if (target.hasIdentityMatrix() || mAttachInfo == null) { + xc = scrolledXFloat - (float) target.mLeft; + yc = scrolledYFloat - (float) target.mTop; + } else { + // non-identity matrix: transform the point into the view's coordinates + final float[] localXY = mAttachInfo.mTmpTransformLocation; + localXY[0] = scrolledXFloat; + localXY[1] = scrolledYFloat; + target.getInverseMatrix().mapPoints(localXY); + xc = localXY[0] - (float) target.mLeft; + yc = localXY[1] - (float) target.mTop; + } + // if have a target, see if we're allowed to and want to intercept its // events if (!disallowIntercept && onInterceptTouchEvent(ev)) { - final float xc = scrolledXFloat - (float) target.mLeft; - final float yc = scrolledYFloat - (float) target.mTop; mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT; ev.setAction(MotionEvent.ACTION_CANCEL); ev.setLocation(xc, yc); @@ -922,20 +936,6 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // finally offset the event to the target's coordinate system and // dispatch the event. - float xc; - float yc; - if (hasIdentityMatrix() || mAttachInfo == null) { - xc = scrolledXFloat - (float) target.mLeft; - yc = scrolledYFloat - (float) target.mTop; - } else { - // non-identity matrix: transform the point into the view's coordinates - final float[] localXY = mAttachInfo.mTmpTransformLocation; - localXY[0] = scrolledXFloat; - localXY[1] = scrolledYFloat; - getInverseMatrix().mapPoints(localXY); - xc = localXY[0] - (float) target.mLeft; - yc = localXY[1] - (float) target.mTop; - } ev.setLocation(xc, yc); if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) { diff --git a/core/java/com/google/android/mms/pdu/PduParser.java b/core/java/com/google/android/mms/pdu/PduParser.java index 92d5cc4..8edfe52 100644 --- a/core/java/com/google/android/mms/pdu/PduParser.java +++ b/core/java/com/google/android/mms/pdu/PduParser.java @@ -157,9 +157,11 @@ public class PduParser { } String ctTypeStr = new String(contentType); if (ctTypeStr.equals(ContentType.MULTIPART_MIXED) - || ctTypeStr.equals(ContentType.MULTIPART_RELATED)) { + || ctTypeStr.equals(ContentType.MULTIPART_RELATED) + || ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { // The MMS content type must be "application/vnd.wap.multipart.mixed" // or "application/vnd.wap.multipart.related" + // or "application/vnd.wap.multipart.alternative" return retrieveConf; } else if (ctTypeStr.equals(ContentType.MULTIPART_ALTERNATIVE)) { // "application/vnd.wap.multipart.alternative" diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java index d4ac24a..9fdd204 100644 --- a/core/java/com/google/android/mms/pdu/PduPersister.java +++ b/core/java/com/google/android/mms/pdu/PduPersister.java @@ -422,7 +422,8 @@ public class PduPersister { // Store simple string values directly in the database instead of an // external file. This makes the text searchable and retrieval slightly // faster. - if ("text/plain".equals(type) || "application/smil".equals(type)) { + if (ContentType.TEXT_PLAIN.equals(type) || ContentType.APP_SMIL.equals(type) + || ContentType.TEXT_HTML.equals(type)) { String text = c.getString(PART_COLUMN_TEXT); byte [] blob = new EncodedStringValue(text != null ? text : "") .getTextString(); @@ -736,7 +737,9 @@ public class PduPersister { try { byte[] data = part.getData(); - if ("text/plain".equals(contentType) || "application/smil".equals(contentType)) { + if (ContentType.TEXT_PLAIN.equals(contentType) + || ContentType.APP_SMIL.equals(contentType) + || ContentType.TEXT_HTML.equals(contentType)) { ContentValues cv = new ContentValues(); cv.put(Telephony.Mms.Part.TEXT, new EncodedStringValue(data).getString()); if (mContentResolver.update(uri, cv, null, null) != 1) { diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index efaff12..4dc012c 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -65,6 +65,7 @@ static jmethodID method_onAgentAuthorize; static jmethodID method_onAgentCancel; static jmethodID method_onInputDevicePropertyChanged; +static jmethodID method_onInputDeviceConnectionResult; typedef event_loop_native_data_t native_data_t; @@ -120,7 +121,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) { "(Ljava/lang/String;II)V"); method_onInputDevicePropertyChanged = env->GetMethodID(clazz, "onInputDevicePropertyChanged", "(Ljava/lang/String;[Ljava/lang/String;)V"); - + method_onInputDeviceConnectionResult = env->GetMethodID(clazz, "onInputDeviceConnectionResult", + "(Ljava/lang/String;Z)V"); field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I"); #endif @@ -1245,6 +1247,32 @@ done: env->DeleteLocalRef(addr); free(user); } + +void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *n) { + LOGV(__FUNCTION__); + + native_data_t *nat = (native_data_t *)n; + const char *path = (const char *)user; + DBusError err; + dbus_error_init(&err); + JNIEnv *env; + nat->vm->GetEnv((void**)&env, nat->envVer); + + bool result = JNI_TRUE; + if (dbus_set_error_from_message(&err, msg)) { + LOG_AND_FREE_DBUS_ERROR(&err); + result = JNI_FALSE; + } + LOGV("... Device Path = %s, result = %d", path, result); + jstring jPath = env->NewStringUTF(path); + env->CallVoidMethod(nat->me, + method_onInputDeviceConnectionResult, + jPath, + result); + env->DeleteLocalRef(jPath); + free(user); +} + #endif static JNINativeMethod sMethods[] = { diff --git a/core/jni/android_server_BluetoothService.cpp b/core/jni/android_server_BluetoothService.cpp index a52a74c..69a8003 100644 --- a/core/jni/android_server_BluetoothService.cpp +++ b/core/jni/android_server_BluetoothService.cpp @@ -70,6 +70,7 @@ extern DBusHandlerResult agent_event_filter(DBusConnection *conn, void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat); void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat); void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat); +void onInputDeviceConnectionResult(DBusMessage *msg, void *user, void *nat); /** Get native data stored in the opaque (Java code maintained) pointer mNativeData @@ -887,12 +888,21 @@ static jboolean connectInputDeviceNative(JNIEnv *env, jobject object, jstring pa LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); - if (nat) { + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { const char *c_path = env->GetStringUTFChars(path, NULL); - bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, - c_path, DBUS_INPUT_IFACE, "Connect", - DBUS_TYPE_INVALID); + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult, + context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE, + "Connect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); return ret ? JNI_TRUE : JNI_FALSE; @@ -906,12 +916,21 @@ static jboolean disconnectInputDeviceNative(JNIEnv *env, jobject object, LOGV(__FUNCTION__); #ifdef HAVE_BLUETOOTH native_data_t *nat = get_native_data(env, object); - if (nat) { + jobject eventLoop = env->GetObjectField(object, field_mEventLoop); + struct event_loop_native_data_t *eventLoopNat = + get_EventLoop_native_data(env, eventLoop); + + if (nat && eventLoopNat) { const char *c_path = env->GetStringUTFChars(path, NULL); - bool ret = dbus_func_args_async(env, nat->conn, -1, NULL, NULL, nat, - c_path, DBUS_INPUT_IFACE, "Disconnect", - DBUS_TYPE_INVALID); + int len = env->GetStringLength(path) + 1; + char *context_path = (char *)calloc(len, sizeof(char)); + strlcpy(context_path, c_path, len); // for callback + + bool ret = dbus_func_args_async(env, nat->conn, -1, onInputDeviceConnectionResult, + context_path, eventLoopNat, c_path, DBUS_INPUT_IFACE, + "Disconnect", + DBUS_TYPE_INVALID); env->ReleaseStringUTFChars(path, c_path); return ret ? JNI_TRUE : JNI_FALSE; diff --git a/core/res/res/values-xlarge/config.xml b/core/res/res/values-xlarge/config.xml index 2640662..c5a53b2 100644 --- a/core/res/res/values-xlarge/config.xml +++ b/core/res/res/values-xlarge/config.xml @@ -23,5 +23,6 @@ <!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) --> <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.tablet.TabletStatusBarService</string> + <bool name="config_statusBarCanHide">false</bool> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index b37d887..cf2b423 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -23,6 +23,7 @@ <!-- Component to be used as the status bar service. Must implement the IStatusBar interface. This name is in the ComponentName flattened format (package/class) --> <string name="config_statusBarComponent">com.android.systemui/com.android.systemui.statusbar.PhoneStatusBarService</string> + <bool name="config_statusBarCanHide">true</bool> <!-- Do not translate. Defines the slots for the right-hand side icons. That is to say, the icons in the status bar that are not notifications. --> diff --git a/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs b/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs index 4417197..34bea2f 100644 --- a/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs +++ b/libs/rs/java/Fountain/src/com/android/fountain/fountain.rs @@ -3,8 +3,6 @@ #pragma rs java_package_name(com.android.fountain) -#include "rs_types.rsh" -#include "rs_math.rsh" #include "rs_graphics.rsh" static int newPart = 0; diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs index b580393..12180c6 100644 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs @@ -1,8 +1,5 @@ #pragma version(1) -#include "rs_types.rsh" -#include "rs_math.rsh" - #include "ip.rsh" void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs index b0db8a3..86ec56d 100644 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs @@ -1,8 +1,5 @@ #pragma version(1) -#include "rs_types.rsh" -#include "rs_math.rsh" - #include "ip.rsh" diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs index 3cd43c7..5dcd372 100644 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs @@ -1,8 +1,5 @@ #pragma version(1) -#include "rs_types.rsh" -#include "rs_math.rsh" - #include "ip.rsh" int height; diff --git a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs index 23387ef..394d36a 100644 --- a/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs +++ b/libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs @@ -1,8 +1,5 @@ #pragma version(1) -#include "rs_types.rsh" -#include "rs_math.rsh" - #include "ip.rsh" void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { diff --git a/libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs b/libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs index 8da7506..41594eb 100644 --- a/libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs +++ b/libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs @@ -16,8 +16,6 @@ #pragma rs java_package_name(com.android.modelviewer) -#include "rs_types.rsh" -#include "rs_math.rsh" #include "rs_graphics.rsh" rs_program_vertex gPVBackground; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 767f38d..cb7fe06 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -192,6 +192,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean mSafeMode; WindowState mStatusBar = null; + boolean mStatusBarCanHide; final ArrayList<WindowState> mStatusBarPanels = new ArrayList<WindowState>(); WindowState mKeyguard = null; KeyguardViewMediator mKeyguardMediator; @@ -546,6 +547,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.array.config_safeModeDisabledVibePattern); mSafeModeEnabledVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_safeModeEnabledVibePattern); + + // Note: the Configuration is not stable here, so we cannot load mStatusBarCanHide from + // config_statusBarCanHide because the latter depends on the screen size } public void updateSettings() { @@ -954,6 +958,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { return WindowManagerImpl.ADD_MULTIPLE_SINGLETON; } mStatusBar = win; + + // The Configuration will be stable by now, so we can load this + mStatusBarCanHide = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_statusBarCanHide); + break; case TYPE_STATUS_BAR_PANEL: mContext.enforceCallingOrSelfPermission( @@ -1212,7 +1221,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset) { final int fl = attrs.flags; - if ((fl & + if (mStatusBarCanHide && (fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { contentInset.set(mCurLeft, mCurTop, mW - mCurRight, mH - mCurBottom); @@ -1340,7 +1349,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { attrs.gravity = Gravity.BOTTOM; mDockLayer = win.getSurfaceLayer(); } else { - if ((fl & + if (mStatusBarCanHide && (fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { // This is the case for a normal activity window: we want it @@ -1372,7 +1381,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { vf.right = mCurRight; vf.bottom = mCurBottom; } - } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) { + } else if (mStatusBarCanHide && (fl & FLAG_LAYOUT_IN_SCREEN) != 0) { // A window that has requested to fill the entire screen just // gets everything, period. pf.left = df.left = cf.left = 0; @@ -1516,9 +1525,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { boolean hideStatusBar = (lp.flags & WindowManager.LayoutParams.FLAG_FULLSCREEN) != 0; if (hideStatusBar) { - if (DEBUG_LAYOUT) Log.v(TAG, "Hiding status bar"); - if (mStatusBar.hideLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT; - hiding = true; + if (mStatusBarCanHide) { + if (DEBUG_LAYOUT) Log.v(TAG, "Hiding status bar"); + if (mStatusBar.hideLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT; + hiding = true; + } else if (localLOGV) { + Log.v(TAG, "Preventing status bar from hiding by policy"); + } } else { if (DEBUG_LAYOUT) Log.v(TAG, "Showing status bar"); if (mStatusBar.showLw(true)) changes |= FINISH_LAYOUT_REDO_LAYOUT; |
