summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--cmds/stagefright/stagefright.cpp68
-rwxr-xr-xcore/java/android/animation/Animator.java34
-rw-r--r--core/java/android/animation/Keyframe.java185
-rw-r--r--core/java/android/animation/KeyframeSet.java99
-rw-r--r--core/java/android/animation/PropertyAnimator.java35
-rw-r--r--core/java/android/app/LoaderManager.java1
-rw-r--r--core/java/android/bluetooth/BluetoothDeviceProfileState.java253
-rw-r--r--core/java/android/bluetooth/BluetoothProfileState.java12
-rw-r--r--core/java/android/server/BluetoothEventLoop.java20
-rw-r--r--core/java/android/server/BluetoothService.java42
-rw-r--r--core/java/android/view/ViewGroup.java32
-rw-r--r--core/java/com/google/android/mms/pdu/PduParser.java4
-rw-r--r--core/java/com/google/android/mms/pdu/PduPersister.java7
-rw-r--r--core/jni/android_server_BluetoothEventLoop.cpp30
-rw-r--r--core/jni/android_server_BluetoothService.cpp35
-rw-r--r--core/res/res/values-xlarge/config.xml1
-rw-r--r--core/res/res/values/config.xml1
-rw-r--r--libs/rs/java/Fountain/src/com/android/fountain/fountain.rs2
-rw-r--r--libs/rs/java/ImageProcessing/src/com/android/rs/image/horizontal_blur.rs3
-rw-r--r--libs/rs/java/ImageProcessing/src/com/android/rs/image/levels.rs3
-rw-r--r--libs/rs/java/ImageProcessing/src/com/android/rs/image/threshold.rs3
-rw-r--r--libs/rs/java/ImageProcessing/src/com/android/rs/image/vertical_blur.rs3
-rw-r--r--libs/rs/java/ModelViewer/src/com/android/modelviewer/modelviewer.rs2
-rwxr-xr-xpolicy/src/com/android/internal/policy/impl/PhoneWindowManager.java25
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;