diff options
76 files changed, 1086 insertions, 325 deletions
diff --git a/CleanSpec.mk b/CleanSpec.mk index 3cec66ff1584..0dba18b4256a 100644 --- a/CleanSpec.mk +++ b/CleanSpec.mk @@ -106,6 +106,7 @@ $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/SystemUI_interme $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/R/com/android/systemui/R.java) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P) $(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/framework_intermediates/src/media/java/android/media/IAudioService.P) +$(call add-clean-step, rm -rf $(PRODUCT_OUT)/system/media/audio/) # ************************************************ # NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST # ************************************************ diff --git a/api/current.txt b/api/current.txt index 9758433e7317..aea2ff315e02 100644 --- a/api/current.txt +++ b/api/current.txt @@ -8721,7 +8721,6 @@ package android.graphics { public class SurfaceTexture { ctor public SurfaceTexture(int); - ctor public SurfaceTexture(int, boolean); method public long getTimestamp(); method public void getTransformMatrix(float[]); method public void release(); diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java index 6fb79656b683..e3075d763ed3 100644 --- a/core/java/android/app/ActivityThread.java +++ b/core/java/android/app/ActivityThread.java @@ -1943,7 +1943,6 @@ public final class ActivityThread { // we are back active so skip it. unscheduleGcIdler(); - Slog.i(TAG, "Launch: profileFd=" + r.profileFile + " stop=" + r.autoStopProfiler); if (r.profileFd != null) { mBoundApplication.setProfiler(r.profileFile, r.profileFd); mBoundApplication.startProfiling(); diff --git a/core/java/android/net/DnsPinger.java b/core/java/android/net/DnsPinger.java index 6115fef926d6..3e27b0defc07 100644 --- a/core/java/android/net/DnsPinger.java +++ b/core/java/android/net/DnsPinger.java @@ -67,7 +67,7 @@ public final class DnsPinger extends Handler { private final Context mContext; private final int mConnectionType; private final Handler mTarget; - private final InetAddress mDefaultDns; + private final ArrayList<InetAddress> mDefaultDns; private String TAG; private static final int BASE = Protocol.BASE_DNS_PINGER; @@ -113,7 +113,8 @@ public final class DnsPinger extends Handler { throw new IllegalArgumentException("Invalid connectionType in constructor: " + connectionType); } - mDefaultDns = getDefaultDns(); + mDefaultDns = new ArrayList<InetAddress>(); + mDefaultDns.add(getDefaultDns()); mEventCounter = 0; } @@ -213,17 +214,16 @@ public final class DnsPinger extends Handler { for (ActivePing activePing : mActivePings) activePing.socket.close(); mActivePings.clear(); - removeMessages(ACTION_PING_DNS); break; } } /** - * @return The first DNS in the link properties of the specified connection - * type or the default system DNS if the link properties has null - * dns set. Should not be null. + * Returns a list of DNS addresses, coming from either the link properties of the + * specified connection or the default system DNS if the link properties has no dnses. + * @return a non-empty non-null list */ - public InetAddress getDns() { + public List<InetAddress> getDnsList() { LinkProperties curLinkProps = getCurrentLinkProperties(); if (curLinkProps == null) { Slog.e(TAG, "getCurLinkProperties:: LP for type" + mConnectionType + " is null!"); @@ -236,7 +236,7 @@ public final class DnsPinger extends Handler { return mDefaultDns; } - return dnses.iterator().next(); + return new ArrayList<InetAddress>(dnses); } /** diff --git a/core/java/android/provider/CallLog.java b/core/java/android/provider/CallLog.java index c5a924b88a3a..886edaf9f46b 100644 --- a/core/java/android/provider/CallLog.java +++ b/core/java/android/provider/CallLog.java @@ -202,6 +202,42 @@ public class CallLog { public static final String GEOCODED_LOCATION = "geocoded_location"; /** + * The cached URI to look up the contact associated with the phone number, if it exists. + * This value is not guaranteed to be current, if the contact information + * associated with this number has changed. + * <P>Type: TEXT</P> + * @hide + */ + public static final String CACHED_LOOKUP_URI = "lookup_uri"; + + /** + * The cached phone number of the contact which matches this entry, if it exists. + * This value is not guaranteed to be current, if the contact information + * associated with this number has changed. + * <P>Type: TEXT</P> + * @hide + */ + public static final String CACHED_MATCHED_NUMBER = "matched_number"; + + /** + * The cached normalized version of the phone number, if it exists. + * This value is not guaranteed to be current, if the contact information + * associated with this number has changed. + * <P>Type: TEXT</P> + * @hide + */ + public static final String CACHED_NORMALIZED_NUMBER = "normalized_number"; + + /** + * The cached photo id of the picture associated with the phone number, if it exists. + * This value is not guaranteed to be current, if the contact information + * associated with this number has changed. + * <P>Type: INTEGER (long)</P> + * @hide + */ + public static final String CACHED_PHOTO_ID = "photo_id"; + + /** * Adds a call to the call log. * * @param ci the CallerInfo object to get the target contact from. Can be null diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index b7dfabcbed0f..f4b92523594a 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -116,7 +116,8 @@ public abstract class Animation implements Cloneable { /** * Indicates whether the animation transformation should be applied before the - * animation starts. + * animation starts. The value of this variable is only relevant if mFillEnabled is true; + * otherwise it is assumed to be true. */ boolean mFillBefore = true; @@ -127,7 +128,7 @@ public abstract class Animation implements Cloneable { boolean mFillAfter = false; /** - * Indicates whether fillAfter should be taken into account. + * Indicates whether fillBefore should be taken into account. */ boolean mFillEnabled = false; @@ -505,9 +506,9 @@ public abstract class Animation implements Cloneable { } /** - * If fillEnabled is true, this animation will apply fillBefore and fillAfter. + * If fillEnabled is true, this animation will apply the value of fillBefore. * - * @return true if the animation will take fillBefore and fillAfter into account + * @return true if the animation will take fillBefore into account * @attr ref android.R.styleable#Animation_fillEnabled */ public boolean isFillEnabled() { @@ -515,11 +516,11 @@ public abstract class Animation implements Cloneable { } /** - * If fillEnabled is true, the animation will apply the value of fillBefore and - * fillAfter. Otherwise, fillBefore and fillAfter are ignored and the animation - * transformation is always applied. + * If fillEnabled is true, the animation will apply the value of fillBefore. + * Otherwise, fillBefore is ignored and the animation + * transformation is always applied until the animation ends. * - * @param fillEnabled true if the animation should take fillBefore and fillAfter into account + * @param fillEnabled true if the animation should take the value of fillBefore into account * @attr ref android.R.styleable#Animation_fillEnabled * * @see #setFillBefore(boolean) @@ -531,7 +532,8 @@ public abstract class Animation implements Cloneable { /** * If fillBefore is true, this animation will apply its transformation - * before the start time of the animation. Defaults to true if not set. + * before the start time of the animation. Defaults to true if + * {@link #setFillEnabled(boolean)} is not set to true. * Note that this applies when using an {@link * android.view.animation.AnimationSet AnimationSet} to chain * animations. The transformation is not applied before the AnimationSet @@ -549,10 +551,9 @@ public abstract class Animation implements Cloneable { /** * If fillAfter is true, the transformation that this animation performed * will persist when it is finished. Defaults to false if not set. - * Note that this applies when using an {@link + * Note that this applies to individual animations and when using an {@link * android.view.animation.AnimationSet AnimationSet} to chain - * animations. The transformation is not applied before the AnimationSet - * itself starts. + * animations. * * @param fillAfter true if the animation should apply its transformation after it ends * @attr ref android.R.styleable#Animation_fillAfter @@ -674,7 +675,9 @@ public abstract class Animation implements Cloneable { /** * If fillBefore is true, this animation will apply its transformation - * before the start time of the animation. + * before the start time of the animation. If fillBefore is false and + * {@link #isFillEnabled() fillEnabled} is true, the transformation will not be applied until + * the start time of the animation. * * @return true if the animation applies its transformation before it starts * @attr ref android.R.styleable#Animation_fillBefore diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 8db6b4f346a7..bb61c720e825 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -3934,15 +3934,14 @@ <declare-styleable name="Animation"> <!-- Defines the interpolator used to smooth the animation movement in time. --> <attr name="interpolator" /> - <!-- When set to true, fillAfter is taken into account. --> + <!-- When set to true, the value of fillBefore is taken into account. --> <attr name="fillEnabled" format="boolean" /> - <!-- When set to true, the animation transformation is applied before the animation has - started. The default value is true. If fillEnabled is not set to true, fillBefore - is assumed to be true. --> + <!-- When set to true or when fillEnabled is not set to true, the animation transformation + is applied before the animation has started. The default value is true. --> <attr name="fillBefore" format="boolean" /> <!-- When set to true, the animation transformation is applied after the animation is - over. The default value is false. If fillEnabled is not set to true and the animation - is not set on a View, fillAfter is assumed to be true. --> + over. The default value is false. If fillEnabled is not set to true and the + animation is not set on a View, fillAfter is assumed to be true.--> <attr name="fillAfter" format="boolean" /> <!-- Amount of time (in milliseconds) for the animation to run. --> <attr name="duration" /> @@ -4100,7 +4099,6 @@ <declare-styleable name="Animator"> <!-- Defines the interpolator used to smooth the animation movement in time. --> <attr name="interpolator" /> - <!-- When set to true, fillAfter is taken into account. --> <!-- Amount of time (in milliseconds) for the animation to run. --> <attr name="duration" /> <!-- Delay in milliseconds before the animation runs, once start time is reached. --> diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk new file mode 100755 index 000000000000..44dd899e2f6b --- /dev/null +++ b/data/sounds/AudioPackage7.mk @@ -0,0 +1,64 @@ +# +# Audio Package 7 - Tuna +# +# Include this file in a product makefile to include these audio files +# +# + +LOCAL_PATH:= frameworks/base/data/sounds + +PRODUCT_COPY_FILES += \ + $(LOCAL_PATH)/alarms/ogg/Cesium.ogg:system/media/audio/alarms/Cesium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Plutonium.ogg:system/media/audio/alarms/Plutonium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Scandium.ogg:system/media/audio/alarms/Scandium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Curium.ogg:system/media/audio/alarms/Curium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Fermium.ogg:system/media/audio/alarms/Fermium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Nobelium.ogg:system/media/audio/alarms/Nobelium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Copernicium.ogg:system/media/audio/alarms/Copernicium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Hassium.ogg:system/media/audio/alarms/Hassium.ogg \ + $(LOCAL_PATH)/alarms/ogg/Neptunium.ogg:system/media/audio/alarms/Neptunium.ogg \ + $(LOCAL_PATH)/effects/ogg/Effect_Tick.ogg:system/media/audio/ui/Effect_Tick.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressStandard_24.ogg:system/media/audio/ui/KeypressStandard.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressSpacebar_24.ogg:system/media/audio/ui/KeypressSpacebar.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressDelete_24.ogg:system/media/audio/ui/KeypressDelete.ogg \ + $(LOCAL_PATH)/effects/ogg/KeypressReturn_24.ogg:system/media/audio/ui/KeypressReturn.ogg \ + $(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \ + $(LOCAL_PATH)/effects/ogg/CameraShutter.ogg:system/media/audio/ui/camera_click.ogg \ + $(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \ + $(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \ + $(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \ + $(LOCAL_PATH)/effects/ogg/Lock.ogg:system/media/audio/ui/Lock.ogg \ + $(LOCAL_PATH)/effects/ogg/Unlock.ogg:system/media/audio/ui/Unlock.ogg \ + $(LOCAL_PATH)/notifications/ogg/Altair.ogg:system/media/audio/notifications/Altair.ogg \ + $(LOCAL_PATH)/notifications/ogg/Antares.ogg:system/media/audio/notifications/Antares.ogg \ + $(LOCAL_PATH)/notifications/ogg/Deneb.ogg:system/media/audio/notifications/Deneb.ogg \ + $(LOCAL_PATH)/notifications/ogg/Lalande.ogg:system/media/audio/notifications/Lalande.ogg \ + $(LOCAL_PATH)/notifications/ogg/Hojus.ogg:system/media/audio/notifications/Hojus.ogg \ + $(LOCAL_PATH)/notifications/ogg/Proxima.ogg:system/media/audio/notifications/Proxima.ogg \ + $(LOCAL_PATH)/notifications/ogg/Betelgeuse.ogg:system/media/audio/notifications/Betelgeuse.ogg \ + $(LOCAL_PATH)/notifications/ogg/Mira.ogg:system/media/audio/notifications/Mira.ogg \ + $(LOCAL_PATH)/notifications/ogg/Upsilon.ogg:system/media/audio/notifications/Upsilon.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Andromeda.ogg:system/media/audio/ringtones/Andromeda.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Aquila.ogg:system/media/audio/ringtones/Aquila.ogg \ + $(LOCAL_PATH)/ringtones/ogg/ArgoNavis.ogg:system/media/audio/ringtones/ArgoNavis.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Bootes.ogg:system/media/audio/ringtones/Bootes.ogg \ + $(LOCAL_PATH)/ringtones/ogg/CanisMajor.ogg:system/media/audio/ringtones/CanisMajor.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Carina.ogg:system/media/audio/ringtones/Carina.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Cassiopeia.ogg:system/media/audio/ringtones/Cassiopeia.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Centaurus.ogg:system/media/audio/ringtones/Centaurus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Cygnus.ogg:system/media/audio/ringtones/Cygnus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Draco.ogg:system/media/audio/ringtones/Draco.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Hydra.ogg:system/media/audio/ringtones/Hydra.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Lyra.ogg:system/media/audio/ringtones/Lyra.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Machina.ogg:system/media/audio/ringtones/Machina.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Orion.ogg:system/media/audio/ringtones/Orion.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Pegasus.ogg:system/media/audio/ringtones/Pegasus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Perseus.ogg:system/media/audio/ringtones/Perseus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Pyxis.ogg:system/media/audio/ringtones/Pyxis.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Rigel.ogg:system/media/audio/ringtones/Rigel.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Scarabaeus.ogg:system/media/audio/ringtones/Scarabaeus.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Sceptrum.ogg:system/media/audio/ringtones/Sceptrum.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Solarium.ogg:system/media/audio/ringtones/Solarium.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Testudo.ogg:system/media/audio/ringtones/Testudo.ogg \ + $(LOCAL_PATH)/ringtones/ogg/UrsaMinor.ogg:system/media/audio/ringtones/UrsaMinor.ogg \ + $(LOCAL_PATH)/ringtones/ogg/Vespa.ogg:system/media/audio/ringtones/Vespa.ogg diff --git a/data/sounds/alarms/ogg/Copernicium.ogg b/data/sounds/alarms/ogg/Copernicium.ogg Binary files differnew file mode 100644 index 000000000000..c619e8bc97f2 --- /dev/null +++ b/data/sounds/alarms/ogg/Copernicium.ogg diff --git a/data/sounds/alarms/ogg/Curium.ogg b/data/sounds/alarms/ogg/Curium.ogg Binary files differnew file mode 100644 index 000000000000..ebce391809f5 --- /dev/null +++ b/data/sounds/alarms/ogg/Curium.ogg diff --git a/data/sounds/alarms/ogg/Fermium.ogg b/data/sounds/alarms/ogg/Fermium.ogg Binary files differnew file mode 100644 index 000000000000..6132565e4365 --- /dev/null +++ b/data/sounds/alarms/ogg/Fermium.ogg diff --git a/data/sounds/alarms/ogg/Hassium.ogg b/data/sounds/alarms/ogg/Hassium.ogg Binary files differnew file mode 100644 index 000000000000..408b7c29703e --- /dev/null +++ b/data/sounds/alarms/ogg/Hassium.ogg diff --git a/data/sounds/alarms/ogg/Neptunium.ogg b/data/sounds/alarms/ogg/Neptunium.ogg Binary files differnew file mode 100644 index 000000000000..058e2db3799f --- /dev/null +++ b/data/sounds/alarms/ogg/Neptunium.ogg diff --git a/data/sounds/alarms/ogg/Nobelium.ogg b/data/sounds/alarms/ogg/Nobelium.ogg Binary files differnew file mode 100644 index 000000000000..33878c9c1807 --- /dev/null +++ b/data/sounds/alarms/ogg/Nobelium.ogg diff --git a/data/sounds/effects/ogg/CameraShutter.ogg b/data/sounds/effects/ogg/CameraShutter.ogg Binary files differnew file mode 100644 index 000000000000..1b67daccdcfe --- /dev/null +++ b/data/sounds/effects/ogg/CameraShutter.ogg diff --git a/data/sounds/effects/ogg/Dock.ogg b/data/sounds/effects/ogg/Dock.ogg Binary files differindex a1c1f2cd1406..caa8eeb0fac9 100755..100644 --- a/data/sounds/effects/ogg/Dock.ogg +++ b/data/sounds/effects/ogg/Dock.ogg diff --git a/data/sounds/effects/ogg/KeypressDelete_24.ogg b/data/sounds/effects/ogg/KeypressDelete_24.ogg Binary files differnew file mode 100644 index 000000000000..2503c3e68142 --- /dev/null +++ b/data/sounds/effects/ogg/KeypressDelete_24.ogg diff --git a/data/sounds/effects/ogg/KeypressReturn_24.ogg b/data/sounds/effects/ogg/KeypressReturn_24.ogg Binary files differnew file mode 100644 index 000000000000..342eb123f76f --- /dev/null +++ b/data/sounds/effects/ogg/KeypressReturn_24.ogg diff --git a/data/sounds/effects/ogg/KeypressSpacebar_24.ogg b/data/sounds/effects/ogg/KeypressSpacebar_24.ogg Binary files differnew file mode 100644 index 000000000000..9f17dd263fda --- /dev/null +++ b/data/sounds/effects/ogg/KeypressSpacebar_24.ogg diff --git a/data/sounds/effects/ogg/KeypressStandard_24.ogg b/data/sounds/effects/ogg/KeypressStandard_24.ogg Binary files differnew file mode 100644 index 000000000000..80d7d6d14dcd --- /dev/null +++ b/data/sounds/effects/ogg/KeypressStandard_24.ogg diff --git a/data/sounds/effects/ogg/Lock.ogg b/data/sounds/effects/ogg/Lock.ogg Binary files differindex deeba6883b8b..471258a39b15 100755..100644 --- a/data/sounds/effects/ogg/Lock.ogg +++ b/data/sounds/effects/ogg/Lock.ogg diff --git a/data/sounds/effects/ogg/Media_Volume.ogg b/data/sounds/effects/ogg/Media_Volume.ogg Binary files differindex 88db9d9737fa..b06656fd308b 100644 --- a/data/sounds/effects/ogg/Media_Volume.ogg +++ b/data/sounds/effects/ogg/Media_Volume.ogg diff --git a/data/sounds/effects/ogg/Undock.ogg b/data/sounds/effects/ogg/Undock.ogg Binary files differindex 91e410ef132d..28918f70cbe5 100755..100644 --- a/data/sounds/effects/ogg/Undock.ogg +++ b/data/sounds/effects/ogg/Undock.ogg diff --git a/data/sounds/effects/ogg/Unlock.ogg b/data/sounds/effects/ogg/Unlock.ogg Binary files differindex ac50288cecfc..1cd537b1a254 100755..100644 --- a/data/sounds/effects/ogg/Unlock.ogg +++ b/data/sounds/effects/ogg/Unlock.ogg diff --git a/data/sounds/effects/ogg/VideoRecord.ogg b/data/sounds/effects/ogg/VideoRecord.ogg Binary files differindex 7afe9e612b2b..28455c907768 100644 --- a/data/sounds/effects/ogg/VideoRecord.ogg +++ b/data/sounds/effects/ogg/VideoRecord.ogg diff --git a/data/sounds/notifications/ogg/Altair.ogg b/data/sounds/notifications/ogg/Altair.ogg Binary files differnew file mode 100755 index 000000000000..d84b59e418fe --- /dev/null +++ b/data/sounds/notifications/ogg/Altair.ogg diff --git a/data/sounds/notifications/ogg/Antares.ogg b/data/sounds/notifications/ogg/Antares.ogg Binary files differnew file mode 100755 index 000000000000..9d609177099a --- /dev/null +++ b/data/sounds/notifications/ogg/Antares.ogg diff --git a/data/sounds/notifications/ogg/Betelgeuse.ogg b/data/sounds/notifications/ogg/Betelgeuse.ogg Binary files differnew file mode 100644 index 000000000000..83c7722a5efd --- /dev/null +++ b/data/sounds/notifications/ogg/Betelgeuse.ogg diff --git a/data/sounds/notifications/ogg/Deneb.ogg b/data/sounds/notifications/ogg/Deneb.ogg Binary files differnew file mode 100755 index 000000000000..e58b3b642743 --- /dev/null +++ b/data/sounds/notifications/ogg/Deneb.ogg diff --git a/data/sounds/notifications/ogg/Hojus.ogg b/data/sounds/notifications/ogg/Hojus.ogg Binary files differnew file mode 100644 index 000000000000..fc8f73f7548d --- /dev/null +++ b/data/sounds/notifications/ogg/Hojus.ogg diff --git a/data/sounds/notifications/ogg/Lalande.ogg b/data/sounds/notifications/ogg/Lalande.ogg Binary files differnew file mode 100755 index 000000000000..b6e253a3f416 --- /dev/null +++ b/data/sounds/notifications/ogg/Lalande.ogg diff --git a/data/sounds/notifications/ogg/Mira.ogg b/data/sounds/notifications/ogg/Mira.ogg Binary files differnew file mode 100644 index 000000000000..f21e3c476a55 --- /dev/null +++ b/data/sounds/notifications/ogg/Mira.ogg diff --git a/data/sounds/notifications/ogg/Proxima.ogg b/data/sounds/notifications/ogg/Proxima.ogg Binary files differnew file mode 100644 index 000000000000..235b5ca96874 --- /dev/null +++ b/data/sounds/notifications/ogg/Proxima.ogg diff --git a/data/sounds/notifications/ogg/Upsilon.ogg b/data/sounds/notifications/ogg/Upsilon.ogg Binary files differnew file mode 100644 index 000000000000..036dcad0f613 --- /dev/null +++ b/data/sounds/notifications/ogg/Upsilon.ogg diff --git a/data/sounds/ringtones/ogg/Cassiopeia.ogg b/data/sounds/ringtones/ogg/Cassiopeia.ogg Binary files differindex 942d4e4ca3b9..61c4d274e0ef 100644 --- a/data/sounds/ringtones/ogg/Cassiopeia.ogg +++ b/data/sounds/ringtones/ogg/Cassiopeia.ogg diff --git a/data/sounds/ringtones/ogg/Lyra.ogg b/data/sounds/ringtones/ogg/Lyra.ogg Binary files differindex e4bf37a5f77f..b7f740df386a 100644 --- a/data/sounds/ringtones/ogg/Lyra.ogg +++ b/data/sounds/ringtones/ogg/Lyra.ogg diff --git a/data/sounds/ringtones/ogg/Sceptrum.ogg b/data/sounds/ringtones/ogg/Sceptrum.ogg Binary files differindex a006afe31c7a..89d64d70a91f 100644 --- a/data/sounds/ringtones/ogg/Sceptrum.ogg +++ b/data/sounds/ringtones/ogg/Sceptrum.ogg diff --git a/data/sounds/ringtones/ogg/Solarium.ogg b/data/sounds/ringtones/ogg/Solarium.ogg Binary files differindex 108ba11ab183..361367ad2070 100644 --- a/data/sounds/ringtones/ogg/Solarium.ogg +++ b/data/sounds/ringtones/ogg/Solarium.ogg diff --git a/data/sounds/ringtones/ogg/UrsaMinor.ogg b/data/sounds/ringtones/ogg/UrsaMinor.ogg Binary files differindex 5591d73573fe..a80801d234f8 100644 --- a/data/sounds/ringtones/ogg/UrsaMinor.ogg +++ b/data/sounds/ringtones/ogg/UrsaMinor.ogg diff --git a/data/sounds/ringtones/ogg/Vespa.ogg b/data/sounds/ringtones/ogg/Vespa.ogg Binary files differindex 6fb8ebdd35d2..1f75ec86080e 100644 --- a/data/sounds/ringtones/ogg/Vespa.ogg +++ b/data/sounds/ringtones/ogg/Vespa.ogg diff --git a/graphics/java/android/graphics/SurfaceTexture.java b/graphics/java/android/graphics/SurfaceTexture.java index d62fd679d485..f3b62ec10e51 100644 --- a/graphics/java/android/graphics/SurfaceTexture.java +++ b/graphics/java/android/graphics/SurfaceTexture.java @@ -93,7 +93,7 @@ public class SurfaceTexture { * @param texName the OpenGL texture object name (e.g. generated via glGenTextures) */ public SurfaceTexture(int texName) { - this(texName, true); + this(texName, false); } /** @@ -104,6 +104,8 @@ public class SurfaceTexture { * When the image stream comes from OpenGL, SurfaceTexture may run in the synchronous * mode where the producer side may be blocked to avoid skipping frames. To avoid the * thread block, set allowSynchronousMode to false. + * + * @hide */ public SurfaceTexture(int texName, boolean allowSynchronousMode) { Looper looper; diff --git a/include/media/stagefright/SurfaceMediaSource.h b/include/media/stagefright/SurfaceMediaSource.h index 1affb8adbd8c..74d54d1169d1 100644 --- a/include/media/stagefright/SurfaceMediaSource.h +++ b/include/media/stagefright/SurfaceMediaSource.h @@ -182,9 +182,9 @@ public: protected: - // freeAllBuffers frees the resources (both GraphicBuffer and EGLImage) for + // freeAllBuffersLocked frees the resources (both GraphicBuffer and EGLImage) for // all slots. - void freeAllBuffers(); + void freeAllBuffersLocked(); static bool isExternalFormat(uint32_t format); private: @@ -337,8 +337,15 @@ private: // Set to a default of 30 fps if not specified by the client side int32_t mFrameRate; - // mStarted is a flag to check if the recording has started - bool mStarted; + // mStopped is a flag to check if the recording is going on + bool mStopped; + + // mNumFramesReceived indicates the number of frames recieved from + // the client side + int mNumFramesReceived; + // mNumFramesEncoded indicates the number of frames passed on to the + // encoder + int mNumFramesEncoded; // mFrameAvailableCondition condition used to indicate whether there // is a frame available for dequeuing diff --git a/include/media/stagefright/openmax/OMX_IVCommon.h b/include/media/stagefright/openmax/OMX_IVCommon.h index 97170d7e7dbe..65b63396fe10 100644 --- a/include/media/stagefright/openmax/OMX_IVCommon.h +++ b/include/media/stagefright/openmax/OMX_IVCommon.h @@ -154,7 +154,8 @@ typedef enum OMX_COLOR_FORMATTYPE { * Gralloc Buffers. * FIXME: In the process of reserving some enum values for * Android-specific OMX IL colorformats. Change this enum to - * an acceptable range once that is done.*/ + * an acceptable range once that is done. + * */ OMX_COLOR_FormatAndroidOpaque = 0x7F000001, OMX_TI_COLOR_FormatYUV420PackedSemiPlanar = 0x7F000100, OMX_QCOM_COLOR_FormatYVU420SemiPlanar = 0x7FA30C00, diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index 370253a9c1d1..b9deafcd3baf 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -62,6 +62,7 @@ public: USAGE_HW_TEXTURE = GRALLOC_USAGE_HW_TEXTURE, USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, USAGE_HW_2D = GRALLOC_USAGE_HW_2D, + USAGE_HW_COMPOSER = GRALLOC_USAGE_HW_COMPOSER, USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK }; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index e89d6ecd469f..04f3c58244c7 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1493,7 +1493,8 @@ void OpenGLRenderer::drawBitmap(SkBitmap* bitmap, const float y = (int) floorf(dstTop + mSnapshot->transform->getTranslateY() + 0.5f); GLenum filter = GL_NEAREST; - if (u1 > 0.0f || u2 < 1.0f || v1 > 0.0f || v2 < 1.0f) { + // Enable linear filtering if the source rectangle is scaled + if (srcRight - srcLeft != dstRight - dstLeft || srcBottom - srcTop != dstBottom - dstTop) { filter = GL_LINEAR; } texture->setFilter(filter, filter, true); diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index 36bbdf05a48c..d6ab0dac064c 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -284,6 +284,15 @@ void Element::decRefs(const void *ptr) const { } } +Element::Builder::Builder() { + const uint32_t initialCapacity = 32; + mBuilderElementRefs.setCapacity(initialCapacity); + mBuilderElements.setCapacity(initialCapacity); + mBuilderNameStrings.setCapacity(initialCapacity); + mBuilderNameLengths.setCapacity(initialCapacity); + mBuilderArrays.setCapacity(initialCapacity); +} + void Element::Builder::add(const Element *e, const char *nameStr, uint32_t arraySize) { mBuilderElementRefs.push(ObjectBaseRef<const Element>(e)); mBuilderElements.push(e); @@ -303,41 +312,12 @@ ObjectBaseRef<const Element> Element::Builder::create(Context *rsc) { ElementState::ElementState() { - const uint32_t initialCapacity = 32; - mBuilderElements.setCapacity(initialCapacity); - mBuilderNameStrings.setCapacity(initialCapacity); - mBuilderNameLengths.setCapacity(initialCapacity); - mBuilderArrays.setCapacity(initialCapacity); } ElementState::~ElementState() { rsAssert(!mElements.size()); } -void ElementState::elementBuilderBegin() { - mBuilderElements.clear(); - mBuilderNameStrings.clear(); - mBuilderNameLengths.clear(); - mBuilderArrays.clear(); -} - -void ElementState::elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize) { - mBuilderElements.push(e); - mBuilderNameStrings.push(nameStr); - mBuilderNameLengths.push(strlen(nameStr)); - mBuilderArrays.push(arraySize); - -} - -const Element *ElementState::elementBuilderCreate(Context *rsc) { - return Element::create(rsc, mBuilderElements.size(), - &(mBuilderElements.editArray()[0]), - &(mBuilderNameStrings.editArray()[0]), - mBuilderNameLengths.editArray(), - mBuilderArrays.editArray()); -} - - ///////////////////////////////////////// // diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h index c3ef25038d33..bfdec53b9982 100644 --- a/libs/rs/rsElement.h +++ b/libs/rs/rsElement.h @@ -30,6 +30,7 @@ class Element : public ObjectBase { public: class Builder { public: + Builder(); void add(const Element *e, const char *nameStr, uint32_t arraySize); ObjectBaseRef<const Element> create(Context *rsc); private: @@ -135,17 +136,8 @@ public: ElementState(); ~ElementState(); - void elementBuilderBegin(); - void elementBuilderAdd(const Element *e, const char *nameStr, uint32_t arraySize); - const Element *elementBuilderCreate(Context *rsc); - // Cache of all existing elements. Vector<Element *> mElements; -private: - Vector<const Element *> mBuilderElements; - Vector<const char*> mBuilderNameStrings; - Vector<size_t> mBuilderNameLengths; - Vector<uint32_t> mBuilderArrays; }; diff --git a/libs/rs/rsLocklessFifo.cpp b/libs/rs/rsLocklessFifo.cpp index 02a76ab027d2..4d022698ce31 100644 --- a/libs/rs/rsLocklessFifo.cpp +++ b/libs/rs/rsLocklessFifo.cpp @@ -21,11 +21,11 @@ using namespace android; using namespace android::renderscript; -LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0) { +LocklessCommandFifo::LocklessCommandFifo() : mBuffer(0), mInitialized(false) { } LocklessCommandFifo::~LocklessCommandFifo() { - if (!mInShutdown) { + if (!mInShutdown && mInitialized) { shutdown(); } if (mBuffer) { @@ -58,6 +58,7 @@ bool LocklessCommandFifo::init(uint32_t sizeInBytes) { mGet = mBuffer; mEnd = mBuffer + (sizeInBytes) - 1; //dumpState("init"); + mInitialized = true; return true; } diff --git a/libs/rs/rsLocklessFifo.h b/libs/rs/rsLocklessFifo.h index 4962ef614cc0..fa53d407f871 100644 --- a/libs/rs/rsLocklessFifo.h +++ b/libs/rs/rsLocklessFifo.h @@ -47,6 +47,7 @@ protected: uint8_t * mEnd; uint8_t mSize; bool mInShutdown; + bool mInitialized; Signal mSignalToWorker; Signal mSignalToControl; diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 27dfeab4cb6f..525ee8b2fd13 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -808,7 +808,7 @@ status_t OMXCodec::setVideoPortFormatType( } if (format.eCompressionFormat == compressionFormat - && format.eColorFormat == colorFormat) { + && format.eColorFormat == colorFormat) { found = true; break; } @@ -838,6 +838,15 @@ static size_t getFrameSize( case OMX_COLOR_FormatYUV420Planar: case OMX_COLOR_FormatYUV420SemiPlanar: case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar: + /* + * FIXME: For the Opaque color format, the frame size does not + * need to be (w*h*3)/2. It just needs to + * be larger than certain minimum buffer size. However, + * currently, this opaque foramt has been tested only on + * YUV420 formats. If that is changed, then we need to revisit + * this part in the future + */ + case OMX_COLOR_FormatAndroidOpaque: return (width * height * 3) / 2; default: @@ -887,7 +896,7 @@ status_t OMXCodec::isColorFormatSupported( // Make sure that omx component does not overwrite // the incremented index (bug 2897413). CHECK_EQ(index, portFormat.nIndex); - if ((portFormat.eColorFormat == colorFormat)) { + if (portFormat.eColorFormat == colorFormat) { LOGV("Found supported color format: %d", portFormat.eColorFormat); return OK; // colorFormat is supported! } @@ -2923,6 +2932,7 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { size_t offset = 0; int32_t n = 0; + for (;;) { MediaBuffer *srcBuffer; if (mSeekTimeUs >= 0) { @@ -3021,6 +3031,7 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) { CHECK(info->mMediaBuffer == NULL); info->mMediaBuffer = srcBuffer; } else { + CHECK(srcBuffer->data() != NULL) ; memcpy((uint8_t *)info->mData + offset, (const uint8_t *)srcBuffer->data() + srcBuffer->range_offset(), diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index ddfd9ff2d915..c2e670779127 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -13,7 +13,6 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - // #define LOG_NDEBUG 0 #define LOG_TAG "SurfaceMediaSource" @@ -47,7 +46,9 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) : mSynchronousMode(true), mConnectedApi(NO_CONNECTED_API), mFrameRate(30), - mStarted(false) { + mNumFramesReceived(0), + mNumFramesEncoded(0), + mStopped(false) { LOGV("SurfaceMediaSource::SurfaceMediaSource"); sp<ISurfaceComposer> composer(ComposerService::getComposerService()); mGraphicBufferAlloc = composer->createGraphicBufferAlloc(); @@ -55,10 +56,9 @@ SurfaceMediaSource::SurfaceMediaSource(uint32_t bufW, uint32_t bufH) : SurfaceMediaSource::~SurfaceMediaSource() { LOGV("SurfaceMediaSource::~SurfaceMediaSource"); - if (mStarted) { + if (!mStopped) { stop(); } - freeAllBuffers(); } size_t SurfaceMediaSource::getQueuedCount() const { @@ -139,12 +139,12 @@ status_t SurfaceMediaSource::setBufferCount(int bufferCount) { // here we're guaranteed that the client doesn't have dequeued buffers // and will release all of its buffer references. - freeAllBuffers(); mBufferCount = bufferCount; mClientBufferCount = bufferCount; mCurrentSlot = INVALID_BUFFER_SLOT; mQueue.clear(); mDequeueCondition.signal(); + freeAllBuffersLocked(); return OK; } @@ -164,7 +164,7 @@ status_t SurfaceMediaSource::requestBuffer(int slot, sp<GraphicBuffer>* buf) { status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, uint32_t format, uint32_t usage) { LOGV("dequeueBuffer"); - + Mutex::Autolock lock(mMutex); // Check for the buffer size- the client should just use the // default width and height, and not try to set those. @@ -184,10 +184,7 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return BAD_VALUE; } - Mutex::Autolock lock(mMutex); - status_t returnFlags(OK); - int found, foundSync; int dequeuedCount = 0; bool tryAgain = true; @@ -218,6 +215,9 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, LOGV("Waiting for the FIFO to drain"); mDequeueCondition.wait(mMutex); } + if (mStopped) { + return NO_INIT; + } // need to check again since the mode could have changed // while we were waiting minBufferCountNeeded = mSynchronousMode ? @@ -228,7 +228,7 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, ((mServerBufferCount != mBufferCount) || (mServerBufferCount < minBufferCountNeeded))) { // here we're guaranteed that mQueue is empty - freeAllBuffers(); + freeAllBuffersLocked(); mBufferCount = mServerBufferCount; if (mBufferCount < minBufferCountNeeded) mBufferCount = minBufferCountNeeded; @@ -290,9 +290,12 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, // for for some buffers to be consumed tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); if (tryAgain) { - LOGW("Waiting..In synchronous mode and no buffer to dQ"); + LOGV("Waiting..In synchronous mode and no buffer to dequeue"); mDequeueCondition.wait(mMutex); } + if (mStopped) { + return NO_INIT; + } } if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { @@ -304,7 +307,7 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return -EBUSY; } - const int buf = found; + const int bufIndex = found; *outBuf = found; const bool useDefaultSize = !w && !h; @@ -322,9 +325,9 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, // buffer is now in DEQUEUED (but can also be current at the same time, // if we're in synchronous mode) - mSlots[buf].mBufferState = BufferSlot::DEQUEUED; + mSlots[bufIndex].mBufferState = BufferSlot::DEQUEUED; - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + const sp<GraphicBuffer>& buffer(mSlots[bufIndex].mGraphicBuffer); if ((buffer == NULL) || (uint32_t(buffer->width) != w) || (uint32_t(buffer->height) != h) || @@ -342,22 +345,25 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, if (updateFormat) { mPixelFormat = format; } - mSlots[buf].mGraphicBuffer = graphicBuffer; - mSlots[buf].mRequestBufferCalled = false; + mSlots[bufIndex].mGraphicBuffer = graphicBuffer; + mSlots[bufIndex].mRequestBufferCalled = false; returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; } return returnFlags; } +// TODO: clean this up status_t SurfaceMediaSource::setSynchronousMode(bool enabled) { Mutex::Autolock lock(mMutex); + if (mStopped) { + LOGE("setSynchronousMode: SurfaceMediaSource has been stopped!"); + return NO_INIT; + } - status_t err = OK; if (!enabled) { - // going to asynchronous mode, drain the queue - while (mSynchronousMode != enabled && !mQueue.isEmpty()) { - mDequeueCondition.wait(mMutex); - } + // Async mode is not allowed + LOGE("SurfaceMediaSource can be used only synchronous mode!"); + return INVALID_OPERATION; } if (mSynchronousMode != enabled) { @@ -368,13 +374,19 @@ status_t SurfaceMediaSource::setSynchronousMode(bool enabled) { mSynchronousMode = enabled; mDequeueCondition.signal(); } - return err; + return OK; } status_t SurfaceMediaSource::connect(int api, uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { LOGV("SurfaceMediaSource::connect"); Mutex::Autolock lock(mMutex); + + if (mStopped) { + LOGE("Connect: SurfaceMediaSource has been stopped!"); + return NO_INIT; + } + status_t err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: @@ -397,9 +409,25 @@ status_t SurfaceMediaSource::connect(int api, return err; } +// This is called by the client side when it is done +// TODO: Currently, this also sets mStopped to true which +// is needed for unblocking the encoder which might be +// waiting to read more frames. So if on the client side, +// the same thread supplies the frames and also calls stop +// on the encoder, the client has to call disconnect before +// it calls stop. +// In the case of the camera, +// that need not be required since the thread supplying the +// frames is separate than the one calling stop. status_t SurfaceMediaSource::disconnect(int api) { LOGV("SurfaceMediaSource::disconnect"); Mutex::Autolock lock(mMutex); + + if (mStopped) { + LOGE("disconnect: SurfaceMediaSoource is already stopped!"); + return NO_INIT; + } + status_t err = NO_ERROR; switch (api) { case NATIVE_WINDOW_API_EGL: @@ -408,6 +436,9 @@ status_t SurfaceMediaSource::disconnect(int api) { case NATIVE_WINDOW_API_CAMERA: if (mConnectedApi == api) { mConnectedApi = NO_CONNECTED_API; + mStopped = true; + mDequeueCondition.signal(); + mFrameAvailableCondition.signal(); } else { err = -EINVAL; } @@ -419,45 +450,47 @@ status_t SurfaceMediaSource::disconnect(int api) { return err; } -status_t SurfaceMediaSource::queueBuffer(int buf, int64_t timestamp, +status_t SurfaceMediaSource::queueBuffer(int bufIndex, int64_t timestamp, uint32_t* outWidth, uint32_t* outHeight, uint32_t* outTransform) { LOGV("queueBuffer"); Mutex::Autolock lock(mMutex); - if (buf < 0 || buf >= mBufferCount) { + if (bufIndex < 0 || bufIndex >= mBufferCount) { LOGE("queueBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); + mBufferCount, bufIndex); return -EINVAL; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) { LOGE("queueBuffer: slot %d is not owned by the client (state=%d)", - buf, mSlots[buf].mBufferState); + bufIndex, mSlots[bufIndex].mBufferState); return -EINVAL; - } else if (!mSlots[buf].mRequestBufferCalled) { + } else if (!mSlots[bufIndex].mRequestBufferCalled) { LOGE("queueBuffer: slot %d was enqueued without requesting a " - "buffer", buf); + "buffer", bufIndex); return -EINVAL; } if (mSynchronousMode) { // in synchronous mode we queue all buffers in a FIFO - mQueue.push_back(buf); - LOGV("Client queued buffer on slot: %d, Q size = %d", - buf, mQueue.size()); + mQueue.push_back(bufIndex); + mNumFramesReceived++; + LOGV("Client queued buf# %d @slot: %d, Q size = %d, handle = %p, timestamp = %lld", + mNumFramesReceived, bufIndex, mQueue.size(), + mSlots[bufIndex].mGraphicBuffer->handle, timestamp); } else { // in asynchronous mode we only keep the most recent buffer if (mQueue.empty()) { - mQueue.push_back(buf); + mQueue.push_back(bufIndex); } else { Fifo::iterator front(mQueue.begin()); // buffer currently queued is freed mSlots[*front].mBufferState = BufferSlot::FREE; // and we record the new buffer index in the queued list - *front = buf; + *front = bufIndex; } } - mSlots[buf].mBufferState = BufferSlot::QUEUED; - mSlots[buf].mTimestamp = timestamp; + mSlots[bufIndex].mBufferState = BufferSlot::QUEUED; + mSlots[bufIndex].mTimestamp = timestamp; // TODO: (Confirm) Don't want to signal dequeue here. // May be just in asynchronous mode? // mDequeueCondition.signal(); @@ -482,7 +515,7 @@ status_t SurfaceMediaSource::queueBuffer(int buf, int64_t timestamp, // wait to hear from StageFrightRecorder to set the buffer FREE // Make sure this is called when the mutex is locked status_t SurfaceMediaSource::onFrameReceivedLocked() { - LOGV("On Frame Received"); + LOGV("On Frame Received locked"); // Signal the encoder that a new frame has arrived mFrameAvailableCondition.signal(); @@ -501,19 +534,19 @@ status_t SurfaceMediaSource::onFrameReceivedLocked() { } -void SurfaceMediaSource::cancelBuffer(int buf) { +void SurfaceMediaSource::cancelBuffer(int bufIndex) { LOGV("SurfaceMediaSource::cancelBuffer"); Mutex::Autolock lock(mMutex); - if (buf < 0 || buf >= mBufferCount) { + if (bufIndex < 0 || bufIndex >= mBufferCount) { LOGE("cancelBuffer: slot index out of range [0, %d]: %d", - mBufferCount, buf); + mBufferCount, bufIndex); return; - } else if (mSlots[buf].mBufferState != BufferSlot::DEQUEUED) { + } else if (mSlots[bufIndex].mBufferState != BufferSlot::DEQUEUED) { LOGE("cancelBuffer: slot %d is not owned by the client (state=%d)", - buf, mSlots[buf].mBufferState); + bufIndex, mSlots[bufIndex].mBufferState); return; } - mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[bufIndex].mBufferState = BufferSlot::FREE; mDequeueCondition.signal(); } @@ -531,8 +564,8 @@ void SurfaceMediaSource::setFrameAvailableListener( mFrameAvailableListener = listener; } -void SurfaceMediaSource::freeAllBuffers() { - LOGV("freeAllBuffers"); +void SurfaceMediaSource::freeAllBuffersLocked() { + LOGV("freeAllBuffersLocked"); for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].mGraphicBuffer = 0; mSlots[i].mBufferState = BufferSlot::FREE; @@ -648,10 +681,7 @@ int32_t SurfaceMediaSource::getFrameRate( ) const { status_t SurfaceMediaSource::start(MetaData *params) { - LOGV("start"); - Mutex::Autolock lock(mMutex); - CHECK(!mStarted); - mStarted = true; + LOGV("started!"); return OK; } @@ -662,8 +692,11 @@ status_t SurfaceMediaSource::stop() Mutex::Autolock lock(mMutex); // TODO: Add waiting on mFrameCompletedCondition here? - mStarted = false; + mStopped = true; mFrameAvailableCondition.signal(); + mDequeueCondition.signal(); + mQueue.clear(); + freeAllBuffersLocked(); return OK; } @@ -688,23 +721,25 @@ sp<MetaData> SurfaceMediaSource::getFormat() } status_t SurfaceMediaSource::read( MediaBuffer **buffer, - const ReadOptions *options) + const ReadOptions *options) { + Mutex::Autolock autoLock(mMutex) ; + LOGV("Read. Size of queued buffer: %d", mQueue.size()); *buffer = NULL; - Mutex::Autolock autoLock(mMutex) ; // If the recording has started and the queue is empty, then just // wait here till the frames come in from the client side - while (mStarted && mQueue.empty()) { + while (!mStopped && mQueue.empty()) { LOGV("NO FRAMES! Recorder waiting for FrameAvailableCondition"); mFrameAvailableCondition.wait(mMutex); } // If the loop was exited as a result of stopping the recording, // it is OK - if (!mStarted) { - return OK; + if (mStopped) { + LOGV("Read: SurfaceMediaSource is stopped. Returning NO_INIT;"); + return NO_INIT; } // Update the current buffer info @@ -712,15 +747,20 @@ status_t SurfaceMediaSource::read( MediaBuffer **buffer, // can be more than one "current" slots. Fifo::iterator front(mQueue.begin()); mCurrentSlot = *front; + mQueue.erase(front); mCurrentBuf = mSlots[mCurrentSlot].mGraphicBuffer; + int64_t prevTimeStamp = mCurrentTimestamp; mCurrentTimestamp = mSlots[mCurrentSlot].mTimestamp; - + mNumFramesEncoded++; // Pass the data to the MediaBuffer. Pass in only the metadata passMetadataBufferLocked(buffer); (*buffer)->setObserver(this); (*buffer)->add_ref(); - (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp); + (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000); + LOGV("Frames encoded = %d, timestamp = %lld, time diff = %lld", + mNumFramesEncoded, mCurrentTimestamp / 1000, + mCurrentTimestamp / 1000 - prevTimeStamp / 1000); return OK; } @@ -743,15 +783,17 @@ void SurfaceMediaSource::passMetadataBufferLocked(MediaBuffer **buffer) { new MediaBuffer(4 + sizeof(buffer_handle_t)); char *data = (char *)tempBuffer->data(); if (data == NULL) { - LOGE("Cannot allocate memory for passing buffer metadata!"); + LOGE("Cannot allocate memory for metadata buffer!"); return; } OMX_U32 type = kMetadataBufferTypeGrallocSource; memcpy(data, &type, 4); memcpy(data + 4, &(mCurrentBuf->handle), sizeof(buffer_handle_t)); *buffer = tempBuffer; -} + LOGV("handle = %p, , offset = %d, length = %d", + mCurrentBuf->handle, (*buffer)->range_length(), (*buffer)->range_offset()); +} void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { LOGV("signalBufferReturned"); @@ -759,16 +801,19 @@ void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { bool foundBuffer = false; Mutex::Autolock autoLock(mMutex); - if (!mStarted) { - LOGW("signalBufferReturned: mStarted = false! Nothing to do!"); + if (mStopped) { + LOGV("signalBufferReturned: mStopped = true! Nothing to do!"); return; } - for (Fifo::iterator it = mQueue.begin(); it != mQueue.end(); ++it) { - CHECK(mSlots[*it].mGraphicBuffer != NULL); - if (checkBufferMatchesSlot(*it, buffer)) { - mSlots[*it].mBufferState = BufferSlot::FREE; - mQueue.erase(it); + for (int id = 0; id < NUM_BUFFER_SLOTS; id++) { + if (mSlots[id].mGraphicBuffer == NULL) { + continue; + } + if (checkBufferMatchesSlot(id, buffer)) { + LOGV("Slot %d returned, matches handle = %p", id, + mSlots[id].mGraphicBuffer->handle); + mSlots[id].mBufferState = BufferSlot::FREE; buffer->setObserver(0); buffer->release(); mDequeueCondition.signal(); @@ -792,5 +837,4 @@ bool SurfaceMediaSource::checkBufferMatchesSlot(int slot, MediaBuffer *buffer) { return mSlots[slot].mGraphicBuffer->handle == bufferHandle; } - } // end of namespace android diff --git a/media/libstagefright/tests/Android.mk b/media/libstagefright/tests/Android.mk index 3ea8f39ba699..357feb1477dd 100644 --- a/media/libstagefright/tests/Android.mk +++ b/media/libstagefright/tests/Android.mk @@ -19,12 +19,13 @@ LOCAL_SHARED_LIBRARIES := \ libbinder \ libcutils \ libgui \ - libstlport \ - libui \ - libutils \ + libmedia \ libstagefright \ libstagefright_omx \ libstagefright_foundation \ + libstlport \ + libui \ + libutils \ LOCAL_STATIC_LIBRARIES := \ libgtest \ diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp index 5b32b687c0c7..d643a0b23e64 100644 --- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp +++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp @@ -14,14 +14,17 @@ * limitations under the License. */ -#define LOG_TAG "SurfaceMediaSource_test" // #define LOG_NDEBUG 0 +#define LOG_TAG "SurfaceMediaSource_test" #include <gtest/gtest.h> #include <utils/String8.h> #include <utils/Errors.h> +#include <fcntl.h> +#include <unistd.h> #include <media/stagefright/SurfaceMediaSource.h> +#include <media/mediarecorder.h> #include <gui/SurfaceTextureClient.h> #include <ui/GraphicBuffer.h> @@ -33,24 +36,322 @@ #include <ui/FramebufferNativeWindow.h> #include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaBufferGroup.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MetaData.h> -#include <media/stagefright/MPEG4Writer.h> #include <media/stagefright/OMXClient.h> #include <media/stagefright/OMXCodec.h> #include <OMX_Component.h> #include "DummyRecorder.h" + namespace android { +class GLTest : public ::testing::Test { +protected: + + GLTest(): + mEglDisplay(EGL_NO_DISPLAY), + mEglSurface(EGL_NO_SURFACE), + mEglContext(EGL_NO_CONTEXT) { + } + + virtual void SetUp() { + LOGV("GLTest::SetUp()"); + mEglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_DISPLAY, mEglDisplay); + + EGLint majorVersion; + EGLint minorVersion; + EXPECT_TRUE(eglInitialize(mEglDisplay, &majorVersion, &minorVersion)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglVersionMajor", majorVersion); + RecordProperty("EglVersionMajor", minorVersion); + + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mGlConfig, + 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + char* displaySecsEnv = getenv("GLTEST_DISPLAY_SECS"); + if (displaySecsEnv != NULL) { + mDisplaySecs = atoi(displaySecsEnv); + if (mDisplaySecs < 0) { + mDisplaySecs = 0; + } + } else { + mDisplaySecs = 0; + } + + if (mDisplaySecs > 0) { + mComposerClient = new SurfaceComposerClient; + ASSERT_EQ(NO_ERROR, mComposerClient->initCheck()); + + mSurfaceControl = mComposerClient->createSurface( + String8("Test Surface"), 0, + getSurfaceWidth(), getSurfaceHeight(), + PIXEL_FORMAT_RGB_888, 0); + + ASSERT_TRUE(mSurfaceControl != NULL); + ASSERT_TRUE(mSurfaceControl->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + ASSERT_EQ(NO_ERROR, mSurfaceControl->setLayer(0x7FFFFFFF)); + ASSERT_EQ(NO_ERROR, mSurfaceControl->show()); + SurfaceComposerClient::closeGlobalTransaction(); + + sp<ANativeWindow> window = mSurfaceControl->getSurface(); + mEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, + window.get(), NULL); + } else { + EGLint pbufferAttribs[] = { + EGL_WIDTH, getSurfaceWidth(), + EGL_HEIGHT, getSurfaceHeight(), + EGL_NONE }; + + mEglSurface = eglCreatePbufferSurface(mEglDisplay, mGlConfig, + pbufferAttribs); + } + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mEglSurface); + + mEglContext = eglCreateContext(mEglDisplay, mGlConfig, EGL_NO_CONTEXT, + getContextAttribs()); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_CONTEXT, mEglContext); + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + EGLint w, h; + EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_WIDTH, &w)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + EXPECT_TRUE(eglQuerySurface(mEglDisplay, mEglSurface, EGL_HEIGHT, &h)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + RecordProperty("EglSurfaceWidth", w); + RecordProperty("EglSurfaceHeight", h); + + glViewport(0, 0, w, h); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + virtual void TearDown() { + // Display the result + if (mDisplaySecs > 0 && mEglSurface != EGL_NO_SURFACE) { + eglSwapBuffers(mEglDisplay, mEglSurface); + sleep(mDisplaySecs); + } + + if (mComposerClient != NULL) { + mComposerClient->dispose(); + } + if (mEglContext != EGL_NO_CONTEXT) { + eglDestroyContext(mEglDisplay, mEglContext); + } + if (mEglSurface != EGL_NO_SURFACE) { + eglDestroySurface(mEglDisplay, mEglSurface); + } + if (mEglDisplay != EGL_NO_DISPLAY) { + eglMakeCurrent(mEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, + EGL_NO_CONTEXT); + eglTerminate(mEglDisplay); + } + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + } + + virtual EGLint const* getConfigAttribs() { + LOGV("GLTest getConfigAttribs"); + static EGLint sDefaultConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_ALPHA_SIZE, 8, + EGL_DEPTH_SIZE, 16, + EGL_STENCIL_SIZE, 8, + EGL_NONE }; + + return sDefaultConfigAttribs; + } + + virtual EGLint const* getContextAttribs() { + static EGLint sDefaultContextAttribs[] = { + EGL_CONTEXT_CLIENT_VERSION, 2, + EGL_NONE }; + + return sDefaultContextAttribs; + } + + virtual EGLint getSurfaceWidth() { + return 512; + } + + virtual EGLint getSurfaceHeight() { + return 512; + } + + void loadShader(GLenum shaderType, const char* pSource, GLuint* outShader) { + GLuint shader = glCreateShader(shaderType); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (shader) { + glShaderSource(shader, 1, &pSource, NULL); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glCompileShader(shader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + GLint compiled = 0; + glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (!compiled) { + GLint infoLen = 0; + glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (infoLen) { + char* buf = (char*) malloc(infoLen); + if (buf) { + glGetShaderInfoLog(shader, infoLen, NULL, buf); + printf("Shader compile log:\n%s\n", buf); + free(buf); + FAIL(); + } + } else { + char* buf = (char*) malloc(0x1000); + if (buf) { + glGetShaderInfoLog(shader, 0x1000, NULL, buf); + printf("Shader compile log:\n%s\n", buf); + free(buf); + FAIL(); + } + } + glDeleteShader(shader); + shader = 0; + } + } + ASSERT_TRUE(shader != 0); + *outShader = shader; + } + + void createProgram(const char* pVertexSource, const char* pFragmentSource, + GLuint* outPgm) { + GLuint vertexShader, fragmentShader; + { + SCOPED_TRACE("compiling vertex shader"); + loadShader(GL_VERTEX_SHADER, pVertexSource, &vertexShader); + if (HasFatalFailure()) { + return; + } + } + { + SCOPED_TRACE("compiling fragment shader"); + loadShader(GL_FRAGMENT_SHADER, pFragmentSource, &fragmentShader); + if (HasFatalFailure()) { + return; + } + } + + GLuint program = glCreateProgram(); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + if (program) { + glAttachShader(program, vertexShader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glAttachShader(program, fragmentShader); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + glLinkProgram(program); + GLint linkStatus = GL_FALSE; + glGetProgramiv(program, GL_LINK_STATUS, &linkStatus); + if (linkStatus != GL_TRUE) { + GLint bufLength = 0; + glGetProgramiv(program, GL_INFO_LOG_LENGTH, &bufLength); + if (bufLength) { + char* buf = (char*) malloc(bufLength); + if (buf) { + glGetProgramInfoLog(program, bufLength, NULL, buf); + printf("Program link log:\n%s\n", buf); + free(buf); + FAIL(); + } + } + glDeleteProgram(program); + program = 0; + } + } + glDeleteShader(vertexShader); + glDeleteShader(fragmentShader); + ASSERT_TRUE(program != 0); + *outPgm = program; + } + static int abs(int value) { + return value > 0 ? value : -value; + } + + ::testing::AssertionResult checkPixel(int x, int y, int r, + int g, int b, int a, int tolerance=2) { + GLubyte pixel[4]; + String8 msg; + glReadPixels(x, y, 1, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixel); + GLenum err = glGetError(); + if (err != GL_NO_ERROR) { + msg += String8::format("error reading pixel: %#x", err); + while ((err = glGetError()) != GL_NO_ERROR) { + msg += String8::format(", %#x", err); + } + fprintf(stderr, "pixel check failure: %s\n", msg.string()); + return ::testing::AssertionFailure( + ::testing::Message(msg.string())); + } + if (r >= 0 && abs(r - int(pixel[0])) > tolerance) { + msg += String8::format("r(%d isn't %d)", pixel[0], r); + } + if (g >= 0 && abs(g - int(pixel[1])) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("g(%d isn't %d)", pixel[1], g); + } + if (b >= 0 && abs(b - int(pixel[2])) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("b(%d isn't %d)", pixel[2], b); + } + if (a >= 0 && abs(a - int(pixel[3])) > tolerance) { + if (!msg.isEmpty()) { + msg += " "; + } + msg += String8::format("a(%d isn't %d)", pixel[3], a); + } + if (!msg.isEmpty()) { + fprintf(stderr, "pixel check failure: %s\n", msg.string()); + return ::testing::AssertionFailure( + ::testing::Message(msg.string())); + } else { + return ::testing::AssertionSuccess(); + } + } + + int mDisplaySecs; + sp<SurfaceComposerClient> mComposerClient; + sp<SurfaceControl> mSurfaceControl; + + EGLDisplay mEglDisplay; + EGLSurface mEglSurface; + EGLContext mEglContext; + EGLConfig mGlConfig; +}; + +/////////////////////////////////////////////////////////////////////// +// Class for the NON-GL tests +/////////////////////////////////////////////////////////////////////// class SurfaceMediaSourceTest : public ::testing::Test { public: - SurfaceMediaSourceTest( ): mYuvTexWidth(64), mYuvTexHeight(66) { } - sp<MPEG4Writer> setUpWriter(OMXClient &client ); + SurfaceMediaSourceTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { } void oneBufferPass(int width, int height ); + void oneBufferPassNoFill(int width, int height ); static void fillYV12Buffer(uint8_t* buf, int w, int h, int stride) ; static void fillYV12BufferRect(uint8_t* buf, int w, int h, int stride, const android_native_rect_t& rect) ; @@ -62,27 +363,156 @@ protected: mSMS->setSynchronousMode(true); mSTC = new SurfaceTextureClient(mSMS); mANW = mSTC; + } + virtual void TearDown() { + mSMS.clear(); + mSTC.clear(); + mANW.clear(); } + const int mYuvTexWidth; + const int mYuvTexHeight; + + sp<SurfaceMediaSource> mSMS; + sp<SurfaceTextureClient> mSTC; + sp<ANativeWindow> mANW; +}; + +/////////////////////////////////////////////////////////////////////// +// Class for the GL tests +/////////////////////////////////////////////////////////////////////// +class SurfaceMediaSourceGLTest : public GLTest { +public: + + SurfaceMediaSourceGLTest( ): mYuvTexWidth(176), mYuvTexHeight(144) { } + virtual EGLint const* getConfigAttribs(); + void oneBufferPassGL(int num = 0); + static sp<MediaRecorder> setUpMediaRecorder(int fileDescriptor, int videoSource, + int outputFormat, int videoEncoder, int width, int height, int fps); +protected: + + virtual void SetUp() { + LOGV("SMS-GLTest::SetUp()"); + android::ProcessState::self()->startThreadPool(); + mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); + mSTC = new SurfaceTextureClient(mSMS); + mANW = mSTC; + + // Doing the setup related to the GL Side + GLTest::SetUp(); + } virtual void TearDown() { mSMS.clear(); mSTC.clear(); mANW.clear(); + GLTest::TearDown(); + eglDestroySurface(mEglDisplay, mSmsEglSurface); } + void setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr); + const int mYuvTexWidth; const int mYuvTexHeight; sp<SurfaceMediaSource> mSMS; sp<SurfaceTextureClient> mSTC; sp<ANativeWindow> mANW; - + EGLConfig mSMSGlConfig; + EGLSurface mSmsEglSurface; }; +///////////////////////////////////////////////////////////////////// +// Methods in SurfaceMediaSourceGLTest +///////////////////////////////////////////////////////////////////// +EGLint const* SurfaceMediaSourceGLTest::getConfigAttribs() { + LOGV("SurfaceMediaSourceGLTest getConfigAttribs"); + static EGLint sDefaultConfigAttribs[] = { + EGL_SURFACE_TYPE, EGL_WINDOW_BIT, + EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, + EGL_RED_SIZE, 8, + EGL_GREEN_SIZE, 8, + EGL_BLUE_SIZE, 8, + EGL_RECORDABLE_ANDROID, EGL_TRUE, + EGL_NONE }; + + return sDefaultConfigAttribs; +} + +// One pass of dequeuing and queuing a GLBuffer +void SurfaceMediaSourceGLTest::oneBufferPassGL(int num) { + int d = num % 50; + float f = 0.2f; // 0.1f * d; + + glClearColor(0, 0.3, 0, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(4 + d, 4 + d, 4, 4); + glClearColor(1.0 - f, f, f, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(24 + d, 48 + d, 4, 4); + glClearColor(f, 1.0 - f, f, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(37 + d, 17 + d, 4, 4); + glClearColor(f, f, 1.0 - f, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + // The following call dequeues and queues the buffer + eglSwapBuffers(mEglDisplay, mSmsEglSurface); + glDisable(GL_SCISSOR_TEST); +} + +// Set up the MediaRecorder which runs in the same process as mediaserver +sp<MediaRecorder> SurfaceMediaSourceGLTest::setUpMediaRecorder(int fd, int videoSource, + int outputFormat, int videoEncoder, int width, int height, int fps) { + sp<MediaRecorder> mr = new MediaRecorder(); + mr->setVideoSource(videoSource); + mr->setOutputFormat(outputFormat); + mr->setVideoEncoder(videoEncoder); + mr->setOutputFile(fd, 0, 0); + mr->setVideoSize(width, height); + mr->setVideoFrameRate(fps); + mr->prepare(); + LOGV("Starting MediaRecorder..."); + CHECK_EQ(OK, mr->start()); + return mr; +} + +// query the mediarecorder for a surfacemeidasource and create an egl surface with that +void SurfaceMediaSourceGLTest::setUpEGLSurfaceFromMediaRecorder(sp<MediaRecorder>& mr) { + sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer(); + mSTC = new SurfaceTextureClient(iST); + mANW = mSTC; + + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig, + 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + LOGV("Native Window = %p, mSTC = %p", mANW.get(), mSTC.get()); + + mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig, + mANW.get(), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ; + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); +} + + +///////////////////////////////////////////////////////////////////// +// Methods in SurfaceMediaSourceTest +///////////////////////////////////////////////////////////////////// + +// One pass of dequeuing and queuing the buffer. Fill it in with +// cpu YV12 buffer void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { - LOGV("One Buffer Pass"); ANativeWindowBuffer* anb; ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); ASSERT_TRUE(anb != NULL); @@ -99,42 +529,16 @@ void SurfaceMediaSourceTest::oneBufferPass(int width, int height ) { ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); } -sp<MPEG4Writer> SurfaceMediaSourceTest::setUpWriter(OMXClient &client ) { - // Writing to a file - const char *fileName = "/sdcard/outputSurfEnc.mp4"; - sp<MetaData> enc_meta = new MetaData; - enc_meta->setInt32(kKeyBitRate, 300000); - enc_meta->setInt32(kKeyFrameRate, 30); - - enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); - - sp<MetaData> meta = mSMS->getFormat(); - - int32_t width, height, stride, sliceHeight, colorFormat; - CHECK(meta->findInt32(kKeyWidth, &width)); - CHECK(meta->findInt32(kKeyHeight, &height)); - CHECK(meta->findInt32(kKeyStride, &stride)); - CHECK(meta->findInt32(kKeySliceHeight, &sliceHeight)); - CHECK(meta->findInt32(kKeyColorFormat, &colorFormat)); - - enc_meta->setInt32(kKeyWidth, width); - enc_meta->setInt32(kKeyHeight, height); - enc_meta->setInt32(kKeyIFramesInterval, 1); - enc_meta->setInt32(kKeyStride, stride); - enc_meta->setInt32(kKeySliceHeight, sliceHeight); - // TODO: overwriting the colorformat since the format set by GRAlloc - // could be wrong or not be read by OMX - enc_meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420Planar); - - - sp<MediaSource> encoder = - OMXCodec::Create( - client.interface(), enc_meta, true /* createEncoder */, mSMS); - - sp<MPEG4Writer> writer = new MPEG4Writer(fileName); - writer->addSource(encoder); +// Dequeuing and queuing the buffer without really filling it in. +void SurfaceMediaSourceTest::oneBufferPassNoFill(int width, int height ) { + ANativeWindowBuffer* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); - return writer; + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + // ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + // We do not fill the buffer in. Just queue it back. + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); } // Fill a YV12 buffer with a multi-colored checkerboard pattern @@ -216,46 +620,53 @@ struct SimpleDummyRecorder { return OK; } }; - /////////////////////////////////////////////////////////////////// // TESTS +// SurfaceMediaSourceTest class contains tests that fill the buffers +// using the cpu calls +// SurfaceMediaSourceGLTest class contains tests that fill the buffers +// using the GL calls. +// TODO: None of the tests actually verify the encoded images.. so at this point, +// these are mostly functionality tests + visual inspection +////////////////////////////////////////////////////////////////////// + // Just pass one buffer from the native_window to the SurfaceMediaSource -TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotOneBufferPass) { +// Dummy Encoder +static int testId = 1; +TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotOneBufferPass) { + LOGV("Test # %d", testId++); LOGV("Testing OneBufferPass ******************************"); - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - 0, 0, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - + ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), + HAL_PIXEL_FORMAT_YV12)); oneBufferPass(mYuvTexWidth, mYuvTexHeight); } // Pass the buffer with the wrong height and weight and should not be accepted -TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { +// Dummy Encoder +TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotWrongSizeBufferPass) { + LOGV("Test # %d", testId++); LOGV("Testing Wrong size BufferPass ******************************"); // setting the client side buffer size different than the server size - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - 10, 10, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_dimensions(mANW.get(), + 10, 10)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), + HAL_PIXEL_FORMAT_YV12)); ANativeWindowBuffer* anb; - // make sure we get an error back when dequeuing! + // Note: make sure we get an ERROR back when dequeuing! ASSERT_NE(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); } - // pass multiple buffers from the native_window the SurfaceMediaSource -// A dummy writer is used to simulate actual MPEG4Writer -TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { +// Dummy Encoder +TEST_F(SurfaceMediaSourceTest, DISABLED_DummyEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { + LOGV("Test # %d", testId++); LOGV("Testing MultiBufferPass, Dummy Recorder *********************"); - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - 0, 0, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), + HAL_PIXEL_FORMAT_YV12)); SimpleDummyRecorder writer(mSMS); writer.start(); @@ -272,14 +683,13 @@ TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPa } // Delayed pass of multiple buffers from the native_window the SurfaceMediaSource -// A dummy writer is used to simulate actual MPEG4Writer -TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassLag) { +// Dummy Encoder +TEST_F(SurfaceMediaSourceTest, DISABLED_DummyLagEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { + LOGV("Test # %d", testId++); LOGV("Testing MultiBufferPass, Dummy Recorder Lagging **************"); - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - 0, 0, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), + HAL_PIXEL_FORMAT_YV12)); SimpleDummyRecorder writer(mSMS); writer.start(); @@ -299,12 +709,11 @@ TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPa // pass multiple buffers from the native_window the SurfaceMediaSource // A dummy writer (MULTITHREADED) is used to simulate actual MPEG4Writer -TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPassThreaded) { +TEST_F(SurfaceMediaSourceTest, DISABLED_DummyThreadedEncodingFromCpuFilledYV12BufferNpotMultiBufferPass) { + LOGV("Test # %d", testId++); LOGV("Testing MultiBufferPass, Dummy Recorder Multi-Threaded **********"); - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - 0, 0, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), + HAL_PIXEL_FORMAT_YV12)); DummyRecorder writer(mSMS); writer.start(); @@ -318,32 +727,210 @@ TEST_F(SurfaceMediaSourceTest, EncodingFromCpuFilledYV12BufferNpotMultiBufferPas writer.stop(); } -// Test to examine the actual encoding. Temporarily disabled till the -// colorformat and encoding from GRAlloc data is resolved -TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuFilledYV12BufferNpotWrite) { - LOGV("Testing the whole pipeline with actual Recorder"); - ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), - 0, 0, HAL_PIXEL_FORMAT_YV12)); - ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); - OMXClient client; - CHECK_EQ(OK, client.connect()); - - sp<MPEG4Writer> writer = setUpWriter(client); - int64_t start = systemTime(); - CHECK_EQ(OK, writer->start()); +// Test to examine actual encoding using mediarecorder +// We use the mediaserver to create a mediarecorder and send +// it back to us. So SurfaceMediaSource lives in the same process +// as the mediaserver. +// Very close to the actual camera, except that the +// buffers are filled and queueud by the CPU instead of GL. +TEST_F(SurfaceMediaSourceTest, DISABLED_EncodingFromCpuYV12BufferNpotWriteMediaServer) { + LOGV("Test # %d", testId++); + LOGV("************** Testing the whole pipeline with actual MediaRecorder ***********"); + LOGV("************** SurfaceMediaSource is same process as mediaserver ***********"); + + const char *fileName = "/sdcard/outputSurfEncMSource.mp4"; + int fd = open(fileName, O_RDWR | O_CREAT, 0744); + if (fd < 0) { + LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd); + } + CHECK(fd >= 0); + + sp<MediaRecorder> mr = SurfaceMediaSourceGLTest::setUpMediaRecorder(fd, + VIDEO_SOURCE_GRALLOC_BUFFER, + OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, + mYuvTexHeight, 30); + // get the reference to the surfacemediasource living in + // mediaserver that is created by stagefrightrecorder + sp<ISurfaceTexture> iST = mr->querySurfaceMediaSourceFromMediaServer(); + mSTC = new SurfaceTextureClient(iST); + mANW = mSTC; + ASSERT_EQ(NO_ERROR, native_window_api_connect(mANW.get(), NATIVE_WINDOW_API_CPU)); + ASSERT_EQ(NO_ERROR, native_window_set_buffers_format(mANW.get(), + HAL_PIXEL_FORMAT_YV12)); int32_t nFramesCount = 0; while (nFramesCount <= 300) { - oneBufferPass(mYuvTexWidth, mYuvTexHeight); + oneBufferPassNoFill(mYuvTexWidth, mYuvTexHeight); nFramesCount++; + LOGV("framesCount = %d", nFramesCount); } - CHECK_EQ(OK, writer->stop()); - writer.clear(); - int64_t end = systemTime(); - client.disconnect(); + ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_CPU)); + LOGV("Stopping MediaRecorder..."); + CHECK_EQ(OK, mr->stop()); + mr.clear(); + close(fd); } +////////////////////////////////////////////////////////////////////// +// GL tests +///////////////////////////////////////////////////////////////////// + +// Test to examine whether we can choose the Recordable Android GLConfig +// DummyRecorder used- no real encoding here +TEST_F(SurfaceMediaSourceGLTest, ChooseAndroidRecordableEGLConfigDummyWrite) { + LOGV("Test # %d", testId++); + LOGV("Test to verify creating a surface w/ right config *********"); + + mSMS = new SurfaceMediaSource(mYuvTexWidth, mYuvTexHeight); + mSTC = new SurfaceTextureClient(mSMS); + mANW = mSTC; + + DummyRecorder writer(mSMS); + writer.start(); + + EGLint numConfigs = 0; + EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &mSMSGlConfig, + 1, &numConfigs)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + + mSmsEglSurface = eglCreateWindowSurface(mEglDisplay, mSMSGlConfig, + mANW.get(), NULL); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + ASSERT_NE(EGL_NO_SURFACE, mSmsEglSurface) ; + + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mSmsEglSurface, mSmsEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + int32_t nFramesCount = 0; + while (nFramesCount <= 300) { + oneBufferPassGL(); + nFramesCount++; + LOGV("framesCount = %d", nFramesCount); + } + + ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL)); + writer.stop(); +} +// Test to examine whether we can render GL buffers in to the surface +// created with the native window handle +TEST_F(SurfaceMediaSourceGLTest, RenderingToRecordableEGLSurfaceWorks) { + LOGV("Test # %d", testId++); + LOGV("RenderingToRecordableEGLSurfaceWorks *********************"); + // Do the producer side of things + glClearColor(0.6, 0.6, 0.6, 0.6); + glClear(GL_COLOR_BUFFER_BIT); + + glEnable(GL_SCISSOR_TEST); + glScissor(4, 4, 4, 4); + glClearColor(1.0, 0.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(24, 48, 4, 4); + glClearColor(0.0, 1.0, 0.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + glScissor(37, 17, 4, 4); + glClearColor(0.0, 0.0, 1.0, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153)); + + EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255)); + EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255)); + EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255)); + EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153)); + EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); +} + +// Test to examine the actual encoding with GL buffers +// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource +// The same pattern is rendered every frame +TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaSameImageEachBufNpotWrite) { + LOGV("Test # %d", testId++); + LOGV("************** Testing the whole pipeline with actual Recorder ***********"); + LOGV("************** GL Filling the buffers ***********"); + // Note: No need to set the colorformat for the buffers. The colorformat is + // in the GRAlloc buffers itself. + + const char *fileName = "/sdcard/outputSurfEncMSourceGL.mp4"; + int fd = open(fileName, O_RDWR | O_CREAT, 0744); + if (fd < 0) { + LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd); + } + CHECK(fd >= 0); + + sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER, + OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30); + + // get the reference to the surfacemediasource living in + // mediaserver that is created by stagefrightrecorder + setUpEGLSurfaceFromMediaRecorder(mr); + + int32_t nFramesCount = 0; + while (nFramesCount <= 300) { + oneBufferPassGL(); + nFramesCount++; + LOGV("framesCount = %d", nFramesCount); + } + + ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL)); + LOGV("Stopping MediaRecorder..."); + CHECK_EQ(OK, mr->stop()); + mr.clear(); + close(fd); +} + +// Test to examine the actual encoding from the GL Buffers +// Actual encoder, Actual GL Buffers Filled SurfaceMediaSource +// A different pattern is rendered every frame +TEST_F(SurfaceMediaSourceGLTest, EncodingFromGLRgbaDiffImageEachBufNpotWrite) { + LOGV("Test # %d", testId++); + LOGV("************** Testing the whole pipeline with actual Recorder ***********"); + LOGV("************** Diff GL Filling the buffers ***********"); + // Note: No need to set the colorformat for the buffers. The colorformat is + // in the GRAlloc buffers itself. + + const char *fileName = "/sdcard/outputSurfEncMSourceGLDiff.mp4"; + int fd = open(fileName, O_RDWR | O_CREAT, 0744); + if (fd < 0) { + LOGE("ERROR: Could not open the the file %s, fd = %d !!", fileName, fd); + } + CHECK(fd >= 0); + + sp<MediaRecorder> mr = setUpMediaRecorder(fd, VIDEO_SOURCE_GRALLOC_BUFFER, + OUTPUT_FORMAT_MPEG_4, VIDEO_ENCODER_H264, mYuvTexWidth, mYuvTexHeight, 30); + + // get the reference to the surfacemediasource living in + // mediaserver that is created by stagefrightrecorder + setUpEGLSurfaceFromMediaRecorder(mr); + + int32_t nFramesCount = 0; + while (nFramesCount <= 300) { + oneBufferPassGL(nFramesCount); + nFramesCount++; + LOGV("framesCount = %d", nFramesCount); + } + + ASSERT_EQ(NO_ERROR, native_window_api_disconnect(mANW.get(), NATIVE_WINDOW_API_EGL)); + LOGV("Stopping MediaRecorder..."); + CHECK_EQ(OK, mr->stop()); + mr.clear(); + close(fd); +} } // namespace android diff --git a/packages/SystemUI/res/anim/recent_appear.xml b/packages/SystemUI/res/anim/recent_appear.xml index 4400d9dcdce7..20fe052d4a25 100644 --- a/packages/SystemUI/res/anim/recent_appear.xml +++ b/packages/SystemUI/res/anim/recent_appear.xml @@ -16,5 +16,5 @@ <alpha xmlns:android="http://schemas.android.com/apk/res/android" android:fromAlpha="0.0" android:toAlpha="1.0" - android:duration="@android:integer/config_shortAnimTime" + android:duration="@android:integer/config_mediumAnimTime" /> diff --git a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java index 2d327c41c6eb..9749a1dcdef8 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java +++ b/packages/SystemUI/src/com/android/systemui/recent/Choreographer.java @@ -29,6 +29,7 @@ import android.view.View; // should group this into a multi-property animation private static final int OPEN_DURATION = 136; private static final int CLOSE_DURATION = 250; + private static final int SCRIM_DURATION = 400; private static final String TAG = RecentsPanelView.TAG; private static final boolean DEBUG = RecentsPanelView.DEBUG; @@ -71,12 +72,14 @@ import android.view.View; posAnim.setInterpolator(appearing ? new android.view.animation.DecelerateInterpolator(2.5f) : new android.view.animation.AccelerateInterpolator(2.5f)); + posAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); Animator glowAnim = ObjectAnimator.ofFloat(mContentView, "alpha", mContentView.getAlpha(), appearing ? 1.0f : 0.0f); glowAnim.setInterpolator(appearing ? new android.view.animation.AccelerateInterpolator(1.0f) : new android.view.animation.DecelerateInterpolator(1.0f)); + glowAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); mContentAnim = new AnimatorSet(); final Builder builder = mContentAnim.play(glowAnim).with(posAnim); @@ -84,9 +87,9 @@ import android.view.View; if (background != null) { Animator bgAnim = ObjectAnimator.ofInt(background, "alpha", appearing ? 0 : 255, appearing ? 255 : 0); + bgAnim.setDuration(appearing ? SCRIM_DURATION : CLOSE_DURATION); builder.with(bgAnim); } - mContentAnim.setDuration(appearing ? OPEN_DURATION : CLOSE_DURATION); mContentAnim.addListener(this); if (mListener != null) { mContentAnim.addListener(mListener); diff --git a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java index 8c03ef8e206f..9cc2c29129b2 100644 --- a/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java +++ b/packages/SystemUI/src/com/android/systemui/recent/RecentsPanelView.java @@ -39,7 +39,6 @@ import android.graphics.RectF; import android.graphics.Shader.TileMode; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; -import android.graphics.drawable.LayerDrawable; import android.net.Uri; import android.os.AsyncTask; import android.os.Handler; @@ -497,7 +496,7 @@ public class RecentsPanelView extends RelativeLayout synchronized (ad) { ad.mLabel = label; ad.mIcon = icon; - ad.setThumbnail(thumbs.mainThumbnail); + ad.setThumbnail(thumbs != null ? thumbs.mainThumbnail : null); } } @@ -591,7 +590,7 @@ public class RecentsPanelView extends RelativeLayout ActivityDescription ad = descriptions.get(i); loadActivityDescription(ad, i); long now = SystemClock.uptimeMillis(); - nextTime += 200; + nextTime += 150; if (nextTime > now) { try { Thread.sleep(nextTime-now); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 643866b4efad..bfcf8e048111 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -2188,6 +2188,7 @@ void CursorInputMapper::sync(nsecs_t when) { } nsecs_t downTime = mDownTime; bool buttonsChanged = currentButtonState != lastButtonState; + bool buttonsPressed = currentButtonState & ~lastButtonState; float deltaX = mCursorMotionAccumulator.getRelativeX() * mXScale; float deltaY = mCursorMotionAccumulator.getRelativeY() * mYScale; @@ -2249,7 +2250,7 @@ void CursorInputMapper::sync(nsecs_t when) { // the device in your pocket. // TODO: Use the input device configuration to control this behavior more finely. uint32_t policyFlags = 0; - if (getDevice()->isExternal()) { + if ((buttonsPressed || moved || scrolled) && getDevice()->isExternal()) { policyFlags |= POLICY_FLAG_WAKE_DROPPED; } @@ -3345,9 +3346,12 @@ void TouchInputMapper::sync(nsecs_t when) { // Handle policy on initial down or hover events. uint32_t policyFlags = 0; - if (mLastRawPointerData.pointerCount == 0 && mCurrentRawPointerData.pointerCount != 0) { + bool initialDown = mLastRawPointerData.pointerCount == 0 + && mCurrentRawPointerData.pointerCount != 0; + bool buttonsPressed = mCurrentButtonState & ~mLastButtonState; + if (initialDown || buttonsPressed) { + // If this is a touch screen, hide the pointer on an initial down. if (mDeviceMode == DEVICE_MODE_DIRECT) { - // If this is a touch screen, hide the pointer on an initial down. getContext()->fadePointer(); } diff --git a/services/java/com/android/server/am/TaskRecord.java b/services/java/com/android/server/am/TaskRecord.java index e61a7f4e10d3..87129eaca73f 100644 --- a/services/java/com/android/server/am/TaskRecord.java +++ b/services/java/com/android/server/am/TaskRecord.java @@ -116,6 +116,8 @@ class TaskRecord extends ThumbnailHolder { if (!askedCompatMode) { pw.print(prefix); pw.print("askedCompatMode="); pw.println(askedCompatMode); } + pw.print(prefix); pw.print("lastThumbnail="); pw.print(lastThumbnail); + pw.print(" lastDescription="); pw.println(lastDescription); pw.print(prefix); pw.print("lastActiveTime="); pw.print(lastActiveTime); pw.print(" (inactive for "); pw.print((getInactiveDuration()/1000)); pw.println("s)"); diff --git a/services/java/com/android/server/connectivity/Vpn.java b/services/java/com/android/server/connectivity/Vpn.java index 6b65e07ee570..55e067853372 100644 --- a/services/java/com/android/server/connectivity/Vpn.java +++ b/services/java/com/android/server/connectivity/Vpn.java @@ -388,6 +388,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { private final VpnConfig mConfig; private final String[] mDaemons; private final String[][] mArguments; + private final LocalSocket[] mSockets; private final String mOuterInterface; private final LegacyVpnInfo mInfo; @@ -398,6 +399,7 @@ public class Vpn extends INetworkManagementEventObserver.Stub { mConfig = config; mDaemons = new String[] {"racoon", "mtpd"}; mArguments = new String[][] {racoon, mtpd}; + mSockets = new LocalSocket[mDaemons.length]; mInfo = new LegacyVpnInfo(); // This is the interface which VPN is running on. @@ -416,10 +418,14 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } public void exit() { - // We assume that everything is reset after the daemons die. + // We assume that everything is reset after stopping the daemons. interrupt(); - for (String daemon : mDaemons) { - SystemProperties.set("ctl.stop", daemon); + for (LocalSocket socket : mSockets) { + try { + socket.close(); + } catch (Exception e) { + // ignore + } } } @@ -462,15 +468,10 @@ public class Vpn extends INetworkManagementEventObserver.Stub { checkpoint(false); mInfo.state = LegacyVpnInfo.STATE_INITIALIZING; - // First stop the daemons. - for (String daemon : mDaemons) { - SystemProperties.set("ctl.stop", daemon); - } - // Wait for the daemons to stop. for (String daemon : mDaemons) { String key = "init.svc." + daemon; - while (!"stopped".equals(SystemProperties.get(key))) { + while (!"stopped".equals(SystemProperties.get(key, "stopped"))) { checkpoint(true); } } @@ -511,27 +512,27 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } // Create the control socket. - LocalSocket socket = new LocalSocket(); + mSockets[i] = new LocalSocket(); LocalSocketAddress address = new LocalSocketAddress( daemon, LocalSocketAddress.Namespace.RESERVED); // Wait for the socket to connect. while (true) { try { - socket.connect(address); + mSockets[i].connect(address); break; } catch (Exception e) { // ignore } checkpoint(true); } - socket.setSoTimeout(500); + mSockets[i].setSoTimeout(500); // Send over the arguments. - OutputStream out = socket.getOutputStream(); + OutputStream out = mSockets[i].getOutputStream(); for (String argument : arguments) { byte[] bytes = argument.getBytes(Charsets.UTF_8); - if (bytes.length > 0xFFFF) { + if (bytes.length >= 0xFFFF) { throw new IllegalArgumentException("Argument is too large"); } out.write(bytes.length >> 8); @@ -539,11 +540,12 @@ public class Vpn extends INetworkManagementEventObserver.Stub { out.write(bytes); checkpoint(false); } + out.write(0xFF); + out.write(0xFF); out.flush(); - socket.shutdownOutput(); // Wait for End-of-File. - InputStream in = socket.getInputStream(); + InputStream in = mSockets[i].getInputStream(); while (true) { try { if (in.read() == -1) { @@ -554,7 +556,6 @@ public class Vpn extends INetworkManagementEventObserver.Stub { } checkpoint(true); } - socket.close(); } // Wait for the daemons to create the new state. @@ -631,6 +632,13 @@ public class Vpn extends INetworkManagementEventObserver.Stub { Log.i(TAG, "Aborting", e); exit(); } finally { + // Kill the daemons if they fail to stop. + if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING) { + for (String daemon : mDaemons) { + SystemProperties.set("ctl.stop", daemon); + } + } + // Do not leave an unstable state. if (mInfo.state == LegacyVpnInfo.STATE_INITIALIZING || mInfo.state == LegacyVpnInfo.STATE_CONNECTING) { diff --git a/services/java/com/android/server/usb/UsbDeviceManager.java b/services/java/com/android/server/usb/UsbDeviceManager.java index a01c9758bb58..8f51466e359a 100644 --- a/services/java/com/android/server/usb/UsbDeviceManager.java +++ b/services/java/com/android/server/usb/UsbDeviceManager.java @@ -568,12 +568,9 @@ public class UsbDeviceManager { notification.sound = null; notification.vibrate = null; - Intent intent = new Intent( - Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - intent.setComponent(new ComponentName("com.android.settings", - "com.android.settings.UsbSettings")); + Intent intent = Intent.makeRestartActivityTask( + new ComponentName("com.android.settings", + "com.android.settings.UsbSettings")); PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); notification.setLatestEventInfo(mContext, title, message, pi); @@ -604,12 +601,9 @@ public class UsbDeviceManager { notification.sound = null; notification.vibrate = null; - Intent intent = new Intent( - Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | - Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); - intent.setComponent(new ComponentName("com.android.settings", - "com.android.settings.DevelopmentSettings")); + Intent intent = Intent.makeRestartActivityTask( + new ComponentName("com.android.settings", + "com.android.settings.DevelopmentSettings")); PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); notification.setLatestEventInfo(mContext, title, message, pi); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 3df94a6555dd..e258b1a45346 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -163,6 +163,7 @@ public class WindowManagerService extends IWindowManager.Stub static final boolean DEBUG_WALLPAPER = false; static final boolean DEBUG_DRAG = false; static final boolean DEBUG_SCREEN_ON = false; + static final boolean DEBUG_SCREENSHOT = false; static final boolean SHOW_SURFACE_ALLOC = false; static final boolean SHOW_TRANSACTIONS = false; static final boolean HIDE_STACK_CRAWLS = true; @@ -4996,6 +4997,14 @@ public class WindowManagerService extends IWindowManager.Stub dh = tmp; rot = (rot == Surface.ROTATION_90) ? Surface.ROTATION_270 : Surface.ROTATION_90; } + if (DEBUG_SCREENSHOT) { + Slog.i(TAG, "Screenshot: " + dw + "x" + dh + " from 0 to " + maxLayer); + for (int i=0; i<mWindows.size(); i++) { + Slog.i(TAG, mWindows.get(i) + ": " + mWindows.get(i).mLayer + + " animLayer=" + mWindows.get(i).mAnimLayer + + " surfaceLayer=" + mWindows.get(i).mSurfaceLayer); + } + } rawss = Surface.screenshot(dw, dh, 0, maxLayer); } diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index 8b2485a16331..f8925b840f7a 100644 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -556,6 +556,7 @@ uint32_t Layer::getEffectiveUsage(uint32_t usage) const // need a hardware-protected path to external video sink usage |= GraphicBuffer::USAGE_PROTECTED; } + usage |= GraphicBuffer::USAGE_HW_COMPOSER; return usage; } diff --git a/tests/RenderScriptTests/FBOTest/res/drawable/robot.png b/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png Binary files differindex f7353fd61c5b..f7353fd61c5b 100644 --- a/tests/RenderScriptTests/FBOTest/res/drawable/robot.png +++ b/tests/RenderScriptTests/FBOTest/res/drawable-nodpi/robot.png diff --git a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png Binary files differindex 856eeff59b9d..856eeff59b9d 100644 --- a/tests/RenderScriptTests/ImageProcessing/res/drawable/city.png +++ b/tests/RenderScriptTests/ImageProcessing/res/drawable-nodpi/city.png diff --git a/tests/RenderScriptTests/ModelViewer/res/drawable/robot.png b/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png Binary files differindex f7353fd61c5b..f7353fd61c5b 100644 --- a/tests/RenderScriptTests/ModelViewer/res/drawable/robot.png +++ b/tests/RenderScriptTests/ModelViewer/res/drawable-nodpi/robot.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/checker.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png Binary files differindex b631e1ee4ba6..b631e1ee4ba6 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/checker.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/checker.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/data.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png Binary files differindex 8e347146e331..8e347146e331 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/data.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/data.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/flares.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png Binary files differindex 3a5c970fc2b9..3a5c970fc2b9 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/flares.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/flares.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png Binary files differindex f9d61727cce8..f9d61727cce8 100755 --- a/tests/RenderScriptTests/PerfTest/res/drawable/globe.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/globe.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/leaf.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png Binary files differindex 3cd37755f549..3cd37755f549 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/leaf.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/leaf.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg Binary files differindex 2f2f10ee8eb2..2f2f10ee8eb2 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/light1.jpg +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/light1.jpg diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/space.jpg b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg Binary files differindex b61f6a3de5dd..b61f6a3de5dd 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/space.jpg +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/space.jpg diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png Binary files differindex e7d145554c00..e7d145554c00 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/test_pattern.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/test_pattern.png diff --git a/tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png Binary files differindex 1e08f3b9ac3e..1e08f3b9ac3e 100644 --- a/tests/RenderScriptTests/PerfTest/res/drawable/torusmap.png +++ b/tests/RenderScriptTests/PerfTest/res/drawable-nodpi/torusmap.png diff --git a/tests/RenderScriptTests/tests/res/drawable/test_pattern.png b/tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png Binary files differindex e7d145554c00..e7d145554c00 100644 --- a/tests/RenderScriptTests/tests/res/drawable/test_pattern.png +++ b/tests/RenderScriptTests/tests/res/drawable-nodpi/test_pattern.png diff --git a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java index 4dd856fc76bd..274edae67c02 100644 --- a/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java +++ b/wifi/java/android/net/wifi/WifiWatchdogStateMachine.java @@ -46,9 +46,9 @@ import java.io.PrintWriter; import java.net.HttpURLConnection; import java.net.InetAddress; import java.net.URL; +import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Set; /** * {@link WifiWatchdogStateMachine} monitors the initial connection to a Wi-Fi @@ -79,7 +79,7 @@ public class WifiWatchdogStateMachine extends StateMachine { private static final int LOW_SIGNAL_CUTOFF = 1; private static final long DEFAULT_DNS_CHECK_SHORT_INTERVAL_MS = 2 * 60 * 1000; - private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 10 * 60 * 1000; + private static final long DEFAULT_DNS_CHECK_LONG_INTERVAL_MS = 30 * 60 * 1000; private static final long DEFAULT_WALLED_GARDEN_INTERVAL_MS = 30 * 60 * 1000; private static final int DEFAULT_MAX_SSID_BLACKLISTS = 7; @@ -661,26 +661,34 @@ public class WifiWatchdogStateMachine extends StateMachine { } class DnsCheckingState extends State { - int dnsCheckSuccesses = 0; - int dnsCheckTries = 0; - String dnsCheckLogStr = ""; - Set<Integer> ids = new HashSet<Integer>(); + List<InetAddress> mDnsList; + int[] dnsCheckSuccesses; + String dnsCheckLogStr; + String[] dnsResponseStrs; + /** Keeps track of active dns pings. Map is from pingID to index in mDnsList */ + HashMap<Integer, Integer> idDnsMap = new HashMap<Integer, Integer>(); @Override public void enter() { - dnsCheckSuccesses = 0; - dnsCheckTries = 0; - ids.clear(); - InetAddress dns = mDnsPinger.getDns(); + mDnsList = mDnsPinger.getDnsList(); + int numDnses = mDnsList.size(); + dnsCheckSuccesses = new int[numDnses]; + dnsResponseStrs = new String[numDnses]; + for (int i = 0; i < numDnses; i++) + dnsResponseStrs[i] = ""; + if (DBG) { - Slog.d(WWSM_TAG, "Starting DNS pings at " + SystemClock.elapsedRealtime()); dnsCheckLogStr = String.format("Pinging %s on ssid [%s]: ", - mDnsPinger.getDns(), mConnectionInfo.getSSID()); + mDnsList, mConnectionInfo.getSSID()); + Slog.d(WWSM_TAG, dnsCheckLogStr); } + idDnsMap.clear(); for (int i=0; i < mNumDnsPings; i++) { - ids.add(mDnsPinger.pingDnsAsync(dns, mDnsPingTimeoutMs, - DNS_INTRATEST_PING_INTERVAL * i)); + for (int j = 0; j < numDnses; j++) { + idDnsMap.put(mDnsPinger.pingDnsAsync(mDnsList.get(j), mDnsPingTimeoutMs, + DNS_INTRATEST_PING_INTERVAL * i), j); + } } } @@ -693,27 +701,24 @@ public class WifiWatchdogStateMachine extends StateMachine { int pingID = msg.arg1; int pingResponseTime = msg.arg2; - if (!ids.contains(pingID)) { + Integer dnsServerId = idDnsMap.get(pingID); + if (dnsServerId == null) { Slog.w(WWSM_TAG, "Received a Dns response with unknown ID!"); return HANDLED; } - ids.remove(pingID); - dnsCheckTries++; + + idDnsMap.remove(pingID); if (pingResponseTime >= 0) - dnsCheckSuccesses++; + dnsCheckSuccesses[dnsServerId]++; if (DBG) { if (pingResponseTime >= 0) { - dnsCheckLogStr += "|" + pingResponseTime; + dnsResponseStrs[dnsServerId] += "|" + pingResponseTime; } else { - dnsCheckLogStr += "|x"; + dnsResponseStrs[dnsServerId] += "|x"; } } - if (VDBG) { - Slog.v(WWSM_TAG, dnsCheckLogStr); - } - /** * After a full ping count, if we have more responses than this * cutoff, the outcome is success; else it is 'failure'. @@ -723,10 +728,10 @@ public class WifiWatchdogStateMachine extends StateMachine { * Our final success count will be at least this big, so we're * guaranteed to succeed. */ - if (dnsCheckSuccesses >= mMinDnsResponses) { + if (dnsCheckSuccesses[dnsServerId] >= mMinDnsResponses) { // DNS CHECKS OK, NOW WALLED GARDEN if (DBG) { - Slog.d(WWSM_TAG, dnsCheckLogStr + "| SUCCESS"); + Slog.d(WWSM_TAG, makeLogString() + " SUCCESS"); } if (!shouldCheckWalledGarden()) { @@ -748,14 +753,9 @@ public class WifiWatchdogStateMachine extends StateMachine { return HANDLED; } - /** - * Our final count will be at most the current count plus the - * remaining pings - we're guaranteed to fail. - */ - int remainingChecks = mNumDnsPings - dnsCheckTries; - if (remainingChecks + dnsCheckSuccesses < mMinDnsResponses) { + if (idDnsMap.isEmpty()) { if (DBG) { - Slog.d(WWSM_TAG, dnsCheckLogStr + "| FAILURE"); + Slog.d(WWSM_TAG, makeLogString() + " FAILURE"); } transitionTo(mDnsCheckFailureState); return HANDLED; @@ -764,12 +764,18 @@ public class WifiWatchdogStateMachine extends StateMachine { return HANDLED; } + private String makeLogString() { + String logStr = dnsCheckLogStr; + for (String respStr : dnsResponseStrs) + logStr += " [" + respStr + "]"; + return logStr; + } + @Override public void exit() { mDnsPinger.cancelPings(); } - private boolean shouldCheckWalledGarden() { if (!mWalledGardenTestEnabled) { if (VDBG) @@ -809,7 +815,8 @@ public class WifiWatchdogStateMachine extends StateMachine { int checkGuard = 0; Long lastCheckTime = null; - int curPingID = 0; + /** Keeps track of dns pings. Map is from pingID to InetAddress used for ping */ + HashMap<Integer, InetAddress> pingInfoMap = new HashMap<Integer, InetAddress>(); @Override public void enter() { @@ -817,7 +824,7 @@ public class WifiWatchdogStateMachine extends StateMachine { signalUnstable = false; checkGuard++; unstableSignalChecks = false; - curPingID = 0; + pingInfoMap.clear(); triggerSingleDnsCheck(); } @@ -853,32 +860,37 @@ public class WifiWatchdogStateMachine extends StateMachine { return HANDLED; } lastCheckTime = SystemClock.elapsedRealtime(); - curPingID = mDnsPinger.pingDnsAsync(mDnsPinger.getDns(), - mDnsPingTimeoutMs, 0); + pingInfoMap.clear(); + for (InetAddress curDns: mDnsPinger.getDnsList()) { + pingInfoMap.put(mDnsPinger.pingDnsAsync(curDns, mDnsPingTimeoutMs, 0), + curDns); + } return HANDLED; case DnsPinger.DNS_PING_RESULT: - if ((short) msg.arg1 != curPingID) { - if (VDBG) { - Slog.v(WWSM_TAG, "Received non-matching DnsPing w/ id: " + - msg.arg1); - } + InetAddress curDnsServer = pingInfoMap.get(msg.arg1); + if (curDnsServer == null) { return HANDLED; } + pingInfoMap.remove(msg.arg1); int responseTime = msg.arg2; if (responseTime >= 0) { if (VDBG) { - Slog.v(WWSM_TAG, "Ran a single DNS ping. Response time: " - + responseTime); + Slog.v(WWSM_TAG, "Single DNS ping OK. Response time: " + + responseTime + " from DNS " + curDnsServer); } + pingInfoMap.clear(); checkGuard++; unstableSignalChecks = false; triggerSingleDnsCheck(); } else { - if (DBG) { - Slog.d(WWSM_TAG, "Single dns ping failure. Starting full checks."); + if (pingInfoMap.isEmpty()) { + if (DBG) { + Slog.d(WWSM_TAG, "Single dns ping failure. All dns servers failed, " + + "starting full checks."); + } + transitionTo(mDnsCheckingState); } - transitionTo(mDnsCheckingState); } return HANDLED; } |