diff options
39 files changed, 909 insertions, 401 deletions
diff --git a/api/current.txt b/api/current.txt index 23b441b776ef..de8cab1fe317 100644 --- a/api/current.txt +++ b/api/current.txt @@ -5502,7 +5502,16 @@ package android.content { field public static final java.lang.String ACTION_WALLPAPER_CHANGED = "android.intent.action.WALLPAPER_CHANGED"; field public static final java.lang.String ACTION_WEB_SEARCH = "android.intent.action.WEB_SEARCH"; field public static final java.lang.String CATEGORY_ALTERNATIVE = "android.intent.category.ALTERNATIVE"; + field public static final java.lang.String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER"; + field public static final java.lang.String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR"; + field public static final java.lang.String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; + field public static final java.lang.String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; + field public static final java.lang.String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; + field public static final java.lang.String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; + field public static final java.lang.String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; field public static final java.lang.String CATEGORY_APP_MARKET = "android.intent.category.APP_MARKET"; + field public static final java.lang.String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING"; + field public static final java.lang.String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; field public static final java.lang.String CATEGORY_BROWSABLE = "android.intent.category.BROWSABLE"; field public static final java.lang.String CATEGORY_CAR_DOCK = "android.intent.category.CAR_DOCK"; field public static final java.lang.String CATEGORY_CAR_MODE = "android.intent.category.CAR_MODE"; @@ -17056,7 +17065,7 @@ package android.provider { field public static final java.lang.String EXTRA_VIDEO_QUALITY = "android.intent.extra.videoQuality"; field public static final java.lang.String INTENT_ACTION_MEDIA_PLAY_FROM_SEARCH = "android.media.action.MEDIA_PLAY_FROM_SEARCH"; field public static final java.lang.String INTENT_ACTION_MEDIA_SEARCH = "android.intent.action.MEDIA_SEARCH"; - field public static final java.lang.String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER"; + field public static final deprecated java.lang.String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER"; field public static final java.lang.String INTENT_ACTION_STILL_IMAGE_CAMERA = "android.media.action.STILL_IMAGE_CAMERA"; field public static final java.lang.String INTENT_ACTION_VIDEO_CAMERA = "android.media.action.VIDEO_CAMERA"; field public static final java.lang.String MEDIA_IGNORE_FILENAME = ".nomedia"; @@ -22215,6 +22224,8 @@ package android.view { field public static final int KEYCODE_BUTTON_Y = 100; // 0x64 field public static final int KEYCODE_BUTTON_Z = 101; // 0x65 field public static final int KEYCODE_C = 31; // 0x1f + field public static final int KEYCODE_CALCULATOR = 210; // 0xd2 + field public static final int KEYCODE_CALENDAR = 208; // 0xd0 field public static final int KEYCODE_CALL = 5; // 0x5 field public static final int KEYCODE_CAMERA = 27; // 0x1b field public static final int KEYCODE_CAPS_LOCK = 115; // 0x73 @@ -22223,6 +22234,7 @@ package android.view { field public static final int KEYCODE_CHANNEL_UP = 166; // 0xa6 field public static final int KEYCODE_CLEAR = 28; // 0x1c field public static final int KEYCODE_COMMA = 55; // 0x37 + field public static final int KEYCODE_CONTACTS = 207; // 0xcf field public static final int KEYCODE_CTRL_LEFT = 113; // 0x71 field public static final int KEYCODE_CTRL_RIGHT = 114; // 0x72 field public static final int KEYCODE_D = 32; // 0x20 @@ -22290,6 +22302,7 @@ package android.view { field public static final int KEYCODE_MINUS = 69; // 0x45 field public static final int KEYCODE_MOVE_END = 123; // 0x7b field public static final int KEYCODE_MOVE_HOME = 122; // 0x7a + field public static final int KEYCODE_MUSIC = 209; // 0xd1 field public static final int KEYCODE_MUTE = 91; // 0x5b field public static final int KEYCODE_N = 42; // 0x2a field public static final int KEYCODE_NOTIFICATION = 83; // 0x53 diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java index 45a42e44f1cd..9948985ce14d 100644 --- a/core/java/android/content/Intent.java +++ b/core/java/android/content/Intent.java @@ -2310,6 +2310,74 @@ public class Intent implements Parcelable, Cloneable { // --------------------------------------------------------------------- // --------------------------------------------------------------------- + // Application launch intent categories (see addCategory()). + + /** + * Used with {@link #ACTION_MAIN} to launch the browser application. + * The activity should be able to browse the Internet. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_BROWSER = "android.intent.category.APP_BROWSER"; + + /** + * Used with {@link #ACTION_MAIN} to launch the calculator application. + * The activity should be able to perform standard arithmetic operations. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_CALCULATOR = "android.intent.category.APP_CALCULATOR"; + + /** + * Used with {@link #ACTION_MAIN} to launch the calendar application. + * The activity should be able to view and manipulate calendar entries. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_CALENDAR = "android.intent.category.APP_CALENDAR"; + + /** + * Used with {@link #ACTION_MAIN} to launch the contacts application. + * The activity should be able to view and manipulate address book entries. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_CONTACTS = "android.intent.category.APP_CONTACTS"; + + /** + * Used with {@link #ACTION_MAIN} to launch the email application. + * The activity should be able to send and receive email. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_EMAIL = "android.intent.category.APP_EMAIL"; + + /** + * Used with {@link #ACTION_MAIN} to launch the gallery application. + * The activity should be able to view and manipulate image and video files + * stored on the device. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_GALLERY = "android.intent.category.APP_GALLERY"; + + /** + * Used with {@link #ACTION_MAIN} to launch the maps application. + * The activity should be able to show the user's current location and surroundings. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_MAPS = "android.intent.category.APP_MAPS"; + + /** + * Used with {@link #ACTION_MAIN} to launch the messaging application. + * The activity should be able to send and receive text messages. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_MESSAGING = "android.intent.category.APP_MESSAGING"; + + /** + * Used with {@link #ACTION_MAIN} to launch the music application. + * The activity should be able to play, browse, or manipulate music files stored on the device. + */ + @SdkConstant(SdkConstantType.INTENT_CATEGORY) + public static final String CATEGORY_APP_MUSIC = "android.intent.category.APP_MUSIC"; + + // --------------------------------------------------------------------- + // --------------------------------------------------------------------- // Standard extra data keys. /** diff --git a/core/java/android/provider/MediaStore.java b/core/java/android/provider/MediaStore.java index 5f111eb8e73c..4e016723eba3 100644 --- a/core/java/android/provider/MediaStore.java +++ b/core/java/android/provider/MediaStore.java @@ -40,8 +40,6 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.UnsupportedEncodingException; -import java.text.Collator; /** * The Media provider contains meta data for all available media on both internal @@ -66,7 +64,10 @@ public final class MediaStore { /** * Activity Action: Launch a music player. * The activity should be able to play, browse, or manipulate music files stored on the device. + * + * @deprecated Use {@link android.content.Intent#CATEGORY_APP_MUSIC} instead. */ + @Deprecated @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION) public static final String INTENT_ACTION_MUSIC_PLAYER = "android.intent.action.MUSIC_PLAYER"; diff --git a/core/java/android/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index fd00dcea6dcf..46a78dc0d58a 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -428,7 +428,8 @@ class AudioPlaybackHandler { final AudioTrack audioTrack = params.getAudioTrack(); if (audioTrack == null) { - params.getDispatcher().dispatchOnError(); + // There was already a call to handleSynthesisDone for + // this token. return; } diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java index 6c3d387a5fd7..f53e42cb4603 100755 --- a/core/java/android/view/KeyEvent.java +++ b/core/java/android/view/KeyEvent.java @@ -579,8 +579,20 @@ public class KeyEvent extends InputEvent implements Parcelable { /** Key code constant: 3D Mode key. * Toggles the display between 2D and 3D mode. */ public static final int KEYCODE_3D_MODE = 206; - - private static final int LAST_KEYCODE = KEYCODE_BUTTON_16; + /** Key code constant: Contacts special function key. + * Used to launch an address book application. */ + public static final int KEYCODE_CONTACTS = 207; + /** Key code constant: Calendar special function key. + * Used to launch a calendar application. */ + public static final int KEYCODE_CALENDAR = 208; + /** Key code constant: Music special function key. + * Used to launch a music player application. */ + public static final int KEYCODE_MUSIC = 209; + /** Key code constant: Calculator special function key. + * Used to launch a calculator application. */ + public static final int KEYCODE_CALCULATOR = 210; + + private static final int LAST_KEYCODE = KEYCODE_CALCULATOR; // NOTE: If you add a new keycode here you must also add it to: // isSystem() @@ -589,6 +601,8 @@ public class KeyEvent extends InputEvent implements Parcelable { // external/webkit/WebKit/android/plugins/ANPKeyCodes.h // frameworks/base/core/res/res/values/attrs.xml // emulator? + // LAST_KEYCODE + // KEYCODE_SYMBOLIC_NAMES // // Also Android currently does not reserve code ranges for vendor- // specific key codes. If you have new key codes to have, you @@ -807,6 +821,10 @@ public class KeyEvent extends InputEvent implements Parcelable { names.append(KEYCODE_LANGUAGE_SWITCH, "KEYCODE_LANGUAGE_SWITCH"); names.append(KEYCODE_MANNER_MODE, "KEYCODE_MANNER_MODE"); names.append(KEYCODE_3D_MODE, "KEYCODE_3D_MODE"); + names.append(KEYCODE_CONTACTS, "KEYCODE_CONTACTS"); + names.append(KEYCODE_CALENDAR, "KEYCODE_CALENDAR"); + names.append(KEYCODE_MUSIC, "KEYCODE_MUSIC"); + names.append(KEYCODE_CALCULATOR, "KEYCODE_CALCULATOR"); }; // Symbolic names of all metakeys in bit order from least significant to most significant. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 2af6e3be3c8f..71551598ae43 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -4604,14 +4604,15 @@ public class WebView extends AbsoluteLayout boolean UIAnimationsRunning = false; // Currently for each draw we compute the animation values; // We may in the future decide to do that independently. - if (mNativeClass != 0 && nativeEvaluateLayersAnimations(mNativeClass)) { + if (mNativeClass != 0 && !canvas.isHardwareAccelerated() + && nativeEvaluateLayersAnimations(mNativeClass)) { UIAnimationsRunning = true; // If we have unfinished (or unstarted) animations, // we ask for a repaint. We only need to do this in software // rendering (with hardware rendering we already have a different // method of requesting a repaint) - if (!canvas.isHardwareAccelerated()) - invalidate(); + mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED); + invalidate(); } // decide which adornments to draw @@ -8796,10 +8797,13 @@ public class WebView extends AbsoluteLayout /** @hide Called by JNI when pages are swapped (only occurs with hardware * acceleration) */ - protected void pageSwapCallback() { + protected void pageSwapCallback(boolean notifyAnimationStarted) { if (inEditingMode()) { didUpdateWebTextViewDimensions(ANYWHERE); } + if (notifyAnimationStarted) { + mWebViewCore.sendMessage(EventHub.NOTIFY_ANIMATION_STARTED); + } } void setNewPicture(final WebViewCore.DrawData draw, boolean updateBaseLayer) { diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 2ad866b80d44..d13600438e9b 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -519,7 +519,12 @@ public final class WebViewCore { /** * Update the layers' content */ - private native boolean nativeUpdateLayers(int baseLayer); + private native boolean nativeUpdateLayers(int nativeClass, int baseLayer); + + /** + * Notify webkit that animations have begun (on the hardware accelerated content) + */ + private native void nativeNotifyAnimationStarted(int nativeClass); private native boolean nativeFocusBoundsChanged(); @@ -1035,6 +1040,8 @@ public final class WebViewCore { static final int PLUGIN_SURFACE_READY = 195; + static final int NOTIFY_ANIMATION_STARTED = 196; + // private message ids private static final int DESTROY = 200; @@ -1594,6 +1601,10 @@ public final class WebViewCore { nativePluginSurfaceReady(); break; + case NOTIFY_ANIMATION_STARTED: + nativeNotifyAnimationStarted(mNativeClass); + break; + case ADD_PACKAGE_NAMES: if (BrowserFrame.sJavaBridge == null) { throw new IllegalStateException("No WebView " + @@ -2015,7 +2026,7 @@ public final class WebViewCore { return; } // Directly update the layers we last passed to the UI side - if (nativeUpdateLayers(mLastDrawData.mBaseLayer)) { + if (nativeUpdateLayers(mNativeClass, mLastDrawData.mBaseLayer)) { // If anything more complex than position has been touched, let's do a full draw webkitDraw(); } diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index cf2c8a6778d7..8c57265ef4cd 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -493,6 +493,9 @@ class ZoomManager { if (mHardwareAccelerated) { mWebView.updateScrollCoordinates(mWebView.getScrollX() - tx, mWebView.getScrollY() - ty); + // By adding webView matrix, we need to offset the canvas a bit + // to make the animation smooth. + canvas.translate(tx, ty); setZoomScale(zoomScale, false); if (mZoomScale == 0) { diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index d0ab8b116d06..af59198fcef1 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1493,6 +1493,10 @@ <enum name="KEYCODE_LANGUAGE_SWITCH" value="204" /> <enum name="KEYCODE_MANNER_MODE" value="205" /> <enum name="KEYCODE_3D_MODE" value="206" /> + <enum name="KEYCODE_CONTACTS" value="207" /> + <enum name="KEYCODE_CALENDAR" value="208" /> + <enum name="KEYCODE_MUSIC" value="209" /> + <enum name="KEYCODE_CALCULATOR" value="210" /> </attr> <!-- ***************************************************************** --> diff --git a/data/keyboards/Generic.kl b/data/keyboards/Generic.kl index 10de6ac9cf12..fdd9040d7348 100644 --- a/data/keyboards/Generic.kl +++ b/data/keyboards/Generic.kl @@ -159,7 +159,7 @@ key 128 MEDIA_STOP # key 137 "KEY_CUT" # key 138 "KEY_HELP" key 139 MENU WAKE_DROPPED -# key 140 "KEY_CALC" +key 140 CALCULATOR # key 141 "KEY_SETUP" key 142 POWER WAKE key 143 POWER WAKE @@ -190,7 +190,7 @@ key 167 MEDIA_RECORD key 168 MEDIA_REWIND key 169 CALL # key 170 "KEY_ISO" -# key 171 "KEY_CONFIG" +key 171 MUSIC key 172 HOME # key 173 "KEY_REFRESH" # key 174 "KEY_EXIT" @@ -232,7 +232,7 @@ key 208 MEDIA_FAST_FORWARD # key 210 "KEY_PRINT" # key 211 "KEY_HP" key 212 CAMERA -# key 213 "KEY_SOUND" +key 213 MUSIC # key 214 "KEY_QUESTION" key 215 ENVELOPE # key 216 "KEY_CHAT" @@ -344,7 +344,7 @@ key 377 TV # key 394 "KEY_DIRECTORY" # key 395 "KEY_LIST" # key 396 "KEY_MEMO" -# key 397 "KEY_CALENDAR" +key 397 CALENDAR # key 398 "KEY_RED" # key 399 "KEY_GREEN" # key 400 "KEY_YELLOW" @@ -364,6 +364,7 @@ key 403 CHANNEL_DOWN # key 414 "KEY_TEEN" # key 415 "KEY_TWEN" +key 429 CONTACTS # key 448 "KEY_DEL_EOL" # key 449 "KEY_DEL_EOS" diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index 15c2bab5946a..a8c76725e7c9 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -60,10 +60,16 @@ public: virtual void onFrameAvailable() = 0; }; - // tex indicates the name OpenGL texture to which images are to be streamed. - // This texture name cannot be changed once the SurfaceTexture is created. + // SurfaceTexture constructs a new SurfaceTexture object. tex indicates the + // name of the OpenGL ES texture to which images are to be streamed. This + // texture name cannot be changed once the SurfaceTexture is created. + // allowSynchronousMode specifies whether or not synchronous mode can be + // enabled. texTarget specifies the OpenGL ES texture target to which the + // texture will be bound in updateTexImage. useFenceSync specifies whether + // fences should be used to synchronize access to buffers if that behavior + // is enabled at compile-time. SurfaceTexture(GLuint tex, bool allowSynchronousMode = true, - GLenum texTarget = GL_TEXTURE_EXTERNAL_OES); + GLenum texTarget = GL_TEXTURE_EXTERNAL_OES, bool useFenceSync = true); virtual ~SurfaceTexture(); @@ -276,7 +282,8 @@ private: mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), mTimestamp(0), - mFrameNumber(0) { + mFrameNumber(0), + mFence(EGL_NO_SYNC_KHR) { mCrop.makeInvalid(); } @@ -349,6 +356,11 @@ private: // mFrameNumber is the number of the queued frame for this slot. uint64_t mFrameNumber; + // mFence is the EGL sync object that must signal before the buffer + // associated with this buffer slot may be dequeued. It is initialized + // to EGL_NO_SYNC_KHR when the buffer is created and (optionally, based + // on a compile-time option) set to a new sync object in updateTexImage. + EGLSyncKHR mFence; }; // mSlots is the array of buffer slots that must be mirrored on the client @@ -472,6 +484,12 @@ private: // It is set by the setName method. String8 mName; + // mUseFenceSync indicates whether creation of the EGL_KHR_fence_sync + // extension should be used to prevent buffers from being dequeued before + // it's safe for them to be written. It gets set at construction time and + // never changes. + const bool mUseFenceSync; + // mMutex is the mutex used to prevent concurrent access to the member // variables of SurfaceTexture objects. It must be locked whenever the // member variables are accessed. diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h index cc63356803df..19646b0c9d06 100644 --- a/include/media/IStreamSource.h +++ b/include/media/IStreamSource.h @@ -52,15 +52,20 @@ struct IStreamListener : public IInterface { static const char *const kKeyResumeAtPTS; // When signalling a discontinuity you can optionally - // signal that this is a "hard" discontinuity, i.e. the format - // or configuration of subsequent stream data differs from that - // currently active. To do so, include a non-zero int32_t value - // under the key "kKeyFormatChange" when issuing the DISCONTINUITY + // specify the type(s) of discontinuity, i.e. if the + // audio format has changed, the video format has changed, + // time has jumped or any combination thereof. + // To do so, include a non-zero int32_t value + // under the key "kKeyDiscontinuityMask" when issuing the DISCONTINUITY // command. - // The new logical stream must start with proper codec initialization + // If there is a change in audio/video format, The new logical stream + // must start with proper codec initialization // information for playback to continue, i.e. SPS and PPS in the case // of AVC video etc. - static const char *const kKeyFormatChange; + // If this key is not present, only a time discontinuity is assumed. + // The value should be a bitmask of values from + // ATSParser::DiscontinuityType. + static const char *const kKeyDiscontinuityMask; virtual void issueCommand( Command cmd, bool synchronous, const sp<AMessage> &msg = NULL) = 0; diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 5822877217c0..3963d9cf5909 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -166,6 +166,8 @@ private: bool allYourBuffersAreBelongToUs(); + size_t countBuffersOwnedByComponent(OMX_U32 portIndex) const; + void deferMessage(const sp<AMessage> &msg); void processDeferredMessages(); diff --git a/include/ui/GraphicBuffer.h b/include/ui/GraphicBuffer.h index b9deafcd3baf..6ab01f4c98ef 100644 --- a/include/ui/GraphicBuffer.h +++ b/include/ui/GraphicBuffer.h @@ -63,6 +63,7 @@ public: USAGE_HW_RENDER = GRALLOC_USAGE_HW_RENDER, USAGE_HW_2D = GRALLOC_USAGE_HW_2D, USAGE_HW_COMPOSER = GRALLOC_USAGE_HW_COMPOSER, + USAGE_HW_VIDEO_ENCODER = GRALLOC_USAGE_HW_VIDEO_ENCODER, USAGE_HW_MASK = GRALLOC_USAGE_HW_MASK }; diff --git a/include/ui/KeycodeLabels.h b/include/ui/KeycodeLabels.h index 2efe8ca0ee50..c5bd0c544673 100755 --- a/include/ui/KeycodeLabels.h +++ b/include/ui/KeycodeLabels.h @@ -231,6 +231,10 @@ static const KeycodeLabel KEYCODES[] = { { "LANGUAGE_SWITCH", 204 }, { "MANNER_MODE", 205 }, { "3D_MODE", 206 }, + { "CONTACTS", 207 }, + { "CALENDAR", 208 }, + { "MUSIC", 209 }, + { "CALCULATOR", 210 }, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index beb23f64065a..fcd287c02276 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -36,6 +36,12 @@ #include <utils/Log.h> #include <utils/String8.h> +// This compile option causes SurfaceTexture to return the buffer that is currently +// attached to the GL texture from dequeueBuffer when no other buffers are +// available. It requires the drivers (Gralloc, GL, OMX IL, and Camera) to do +// implicit cross-process synchronization to prevent the buffer from being +// written to before the buffer has (a) been detached from the GL texture and +// (b) all GL reads from the buffer have completed. #ifdef ALLOW_DEQUEUE_CURRENT_BUFFER #define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true #warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled" @@ -43,6 +49,16 @@ #define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false #endif +// This compile option makes SurfaceTexture use the EGL_KHR_fence_sync extension +// to synchronize access to the buffers. It will cause dequeueBuffer to stall, +// waiting for the GL reads for the buffer being dequeued to complete before +// allowing the buffer to be dequeued. +#ifdef USE_FENCE_SYNC +#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER +#error "USE_FENCE_SYNC and ALLOW_DEQUEUE_CURRENT_BUFFER are incompatible" +#endif +#endif + // Macros for including the SurfaceTexture name in log messages #define ST_LOGV(x, ...) LOGV("[%s] "x, mName.string(), ##__VA_ARGS__) #define ST_LOGD(x, ...) LOGD("[%s] "x, mName.string(), ##__VA_ARGS__) @@ -99,7 +115,7 @@ static int32_t createProcessUniqueId() { } SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, - GLenum texTarget) : + GLenum texTarget, bool useFenceSync) : mDefaultWidth(1), mDefaultHeight(1), mPixelFormat(PIXEL_FORMAT_RGBA_8888), @@ -116,6 +132,11 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mAllowSynchronousMode(allowSynchronousMode), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), +#ifdef USE_FENCE_SYNC + mUseFenceSync(useFenceSync), +#else + mUseFenceSync(false), +#endif mTexTarget(texTarget), mFrameCounter(0) { // Choose a name using the PID and a process-unique ID. @@ -261,195 +282,225 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, return BAD_VALUE; } - Mutex::Autolock lock(mMutex); - status_t returnFlags(OK); + EGLDisplay dpy = EGL_NO_DISPLAY; + EGLSyncKHR fence = EGL_NO_SYNC_KHR; - int found = -1; - int foundSync = -1; - int dequeuedCount = 0; - bool tryAgain = true; - while (tryAgain) { - if (mAbandoned) { - ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); - return NO_INIT; - } - - // We need to wait for the FIFO to drain if the number of buffer - // needs to change. - // - // The condition "number of buffers needs to change" is true if - // - the client doesn't care about how many buffers there are - // - AND the actual number of buffer is different from what was - // set in the last setBufferCountServer() - // - OR - - // setBufferCountServer() was set to a value incompatible with - // the synchronization mode (for instance because the sync mode - // changed since) - // - // As long as this condition is true AND the FIFO is not empty, we - // wait on mDequeueCondition. - - const int minBufferCountNeeded = mSynchronousMode ? - MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; - - const bool numberOfBuffersNeedsToChange = !mClientBufferCount && - ((mServerBufferCount != mBufferCount) || - (mServerBufferCount < minBufferCountNeeded)); - - if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { - // wait for the FIFO to drain - mDequeueCondition.wait(mMutex); - // NOTE: we continue here because we need to reevaluate our - // whole state (eg: we could be abandoned or disconnected) - continue; - } + { // Scope for the lock + Mutex::Autolock lock(mMutex); - if (numberOfBuffersNeedsToChange) { - // here we're guaranteed that mQueue is empty - freeAllBuffersLocked(); - mBufferCount = mServerBufferCount; - if (mBufferCount < minBufferCountNeeded) - mBufferCount = minBufferCountNeeded; - mCurrentTexture = INVALID_BUFFER_SLOT; - returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; - } + int found = -1; + int foundSync = -1; + int dequeuedCount = 0; + bool tryAgain = true; + while (tryAgain) { + if (mAbandoned) { + ST_LOGE("dequeueBuffer: SurfaceTexture has been abandoned!"); + return NO_INIT; + } - // look for a free buffer to give to the client - found = INVALID_BUFFER_SLOT; - foundSync = INVALID_BUFFER_SLOT; - dequeuedCount = 0; - for (int i = 0; i < mBufferCount; i++) { - const int state = mSlots[i].mBufferState; - if (state == BufferSlot::DEQUEUED) { - dequeuedCount++; + // We need to wait for the FIFO to drain if the number of buffer + // needs to change. + // + // The condition "number of buffers needs to change" is true if + // - the client doesn't care about how many buffers there are + // - AND the actual number of buffer is different from what was + // set in the last setBufferCountServer() + // - OR - + // setBufferCountServer() was set to a value incompatible with + // the synchronization mode (for instance because the sync mode + // changed since) + // + // As long as this condition is true AND the FIFO is not empty, we + // wait on mDequeueCondition. + + const int minBufferCountNeeded = mSynchronousMode ? + MIN_SYNC_BUFFER_SLOTS : MIN_ASYNC_BUFFER_SLOTS; + + const bool numberOfBuffersNeedsToChange = !mClientBufferCount && + ((mServerBufferCount != mBufferCount) || + (mServerBufferCount < minBufferCountNeeded)); + + if (!mQueue.isEmpty() && numberOfBuffersNeedsToChange) { + // wait for the FIFO to drain + mDequeueCondition.wait(mMutex); + // NOTE: we continue here because we need to reevaluate our + // whole state (eg: we could be abandoned or disconnected) + continue; } - // if buffer is FREE it CANNOT be current - LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), - "dequeueBuffer: buffer %d is both FREE and current!", i); + if (numberOfBuffersNeedsToChange) { + // here we're guaranteed that mQueue is empty + freeAllBuffersLocked(); + mBufferCount = mServerBufferCount; + if (mBufferCount < minBufferCountNeeded) + mBufferCount = minBufferCountNeeded; + mCurrentTexture = INVALID_BUFFER_SLOT; + returnFlags |= ISurfaceTexture::RELEASE_ALL_BUFFERS; + } - if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { - if (state == BufferSlot::FREE || i == mCurrentTexture) { - foundSync = i; - if (i != mCurrentTexture) { - found = i; - break; - } + // look for a free buffer to give to the client + found = INVALID_BUFFER_SLOT; + foundSync = INVALID_BUFFER_SLOT; + dequeuedCount = 0; + for (int i = 0; i < mBufferCount; i++) { + const int state = mSlots[i].mBufferState; + if (state == BufferSlot::DEQUEUED) { + dequeuedCount++; } - } else { - if (state == BufferSlot::FREE) { - /** For Asynchronous mode, we need to return the oldest of free buffers - * There is only one instance when the Framecounter overflows, this logic - * might return the earlier buffer to client. Which is a negligible impact - **/ - if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + + // if buffer is FREE it CANNOT be current + LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i), + "dequeueBuffer: buffer %d is both FREE and current!", + i); + + if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) { + if (state == BufferSlot::FREE || i == mCurrentTexture) { foundSync = i; - found = i; + if (i != mCurrentTexture) { + found = i; + break; + } + } + } else { + if (state == BufferSlot::FREE) { + /* We return the oldest of the free buffers to avoid + * stalling the producer if possible. This is because + * the consumer may still have pending reads of the + * buffers in flight. + */ + bool isOlder = mSlots[i].mFrameNumber < + mSlots[found].mFrameNumber; + if (found < 0 || isOlder) { + foundSync = i; + found = i; + } } } } - } - // clients are not allowed to dequeue more than one buffer - // if they didn't set a buffer count. - if (!mClientBufferCount && dequeuedCount) { - ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " - "setting the buffer count"); - return -EINVAL; - } + // clients are not allowed to dequeue more than one buffer + // if they didn't set a buffer count. + if (!mClientBufferCount && dequeuedCount) { + ST_LOGE("dequeueBuffer: can't dequeue multiple buffers without " + "setting the buffer count"); + return -EINVAL; + } + + // See whether a buffer has been queued since the last + // setBufferCount so we know whether to perform the + // MIN_UNDEQUEUED_BUFFERS check below. + bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; + if (bufferHasBeenQueued) { + // make sure the client is not trying to dequeue more buffers + // than allowed. + const int avail = mBufferCount - (dequeuedCount+1); + if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { + ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " + "(dequeued=%d)", + MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), + dequeuedCount); + return -EBUSY; + } + } - // See whether a buffer has been queued since the last setBufferCount so - // we know whether to perform the MIN_UNDEQUEUED_BUFFERS check below. - bool bufferHasBeenQueued = mCurrentTexture != INVALID_BUFFER_SLOT; - if (bufferHasBeenQueued) { - // make sure the client is not trying to dequeue more buffers - // than allowed. - const int avail = mBufferCount - (dequeuedCount+1); - if (avail < (MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode))) { - ST_LOGE("dequeueBuffer: MIN_UNDEQUEUED_BUFFERS=%d exceeded " - "(dequeued=%d)", - MIN_UNDEQUEUED_BUFFERS-int(mSynchronousMode), - dequeuedCount); - return -EBUSY; + // we're in synchronous mode and didn't find a buffer, we need to + // wait for some buffers to be consumed + tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); + if (tryAgain) { + mDequeueCondition.wait(mMutex); } } - // we're in synchronous mode and didn't find a buffer, we need to wait - // for some buffers to be consumed - tryAgain = mSynchronousMode && (foundSync == INVALID_BUFFER_SLOT); - if (tryAgain) { - mDequeueCondition.wait(mMutex); + if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { + // foundSync guaranteed to be != INVALID_BUFFER_SLOT + found = foundSync; } - } - if (mSynchronousMode && found == INVALID_BUFFER_SLOT) { - // foundSync guaranteed to be != INVALID_BUFFER_SLOT - found = foundSync; - } + if (found == INVALID_BUFFER_SLOT) { + // This should not happen. + ST_LOGE("dequeueBuffer: no available buffer slots"); + return -EBUSY; + } - if (found == INVALID_BUFFER_SLOT) { - // This should not happen. - ST_LOGE("dequeueBuffer: no available buffer slots"); - return -EBUSY; - } + const int buf = found; + *outBuf = found; - const int buf = found; - *outBuf = found; + const bool useDefaultSize = !w && !h; + if (useDefaultSize) { + // use the default size + w = mDefaultWidth; + h = mDefaultHeight; + } - const bool useDefaultSize = !w && !h; - if (useDefaultSize) { - // use the default size - w = mDefaultWidth; - h = mDefaultHeight; - } + const bool updateFormat = (format != 0); + if (!updateFormat) { + // keep the current (or default) format + format = mPixelFormat; + } + + // 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; + + const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); + if ((buffer == NULL) || + (uint32_t(buffer->width) != w) || + (uint32_t(buffer->height) != h) || + (uint32_t(buffer->format) != format) || + ((uint32_t(buffer->usage) & usage) != usage)) + { + usage |= GraphicBuffer::USAGE_HW_TEXTURE; + status_t error; + sp<GraphicBuffer> graphicBuffer( + mGraphicBufferAlloc->createGraphicBuffer( + w, h, format, usage, &error)); + if (graphicBuffer == 0) { + ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " + "failed"); + return error; + } + if (updateFormat) { + mPixelFormat = format; + } + mSlots[buf].mGraphicBuffer = graphicBuffer; + mSlots[buf].mRequestBufferCalled = false; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; + if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { + eglDestroyImageKHR(mSlots[buf].mEglDisplay, + mSlots[buf].mEglImage); + mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; + mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; + } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } + returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + } - const bool updateFormat = (format != 0); - if (!updateFormat) { - // keep the current (or default) format - format = mPixelFormat; + dpy = mSlots[buf].mEglDisplay; + fence = mSlots[buf].mFence; + mSlots[buf].mFence = EGL_NO_SYNC_KHR; } - // 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; - - const sp<GraphicBuffer>& buffer(mSlots[buf].mGraphicBuffer); - if ((buffer == NULL) || - (uint32_t(buffer->width) != w) || - (uint32_t(buffer->height) != h) || - (uint32_t(buffer->format) != format) || - ((uint32_t(buffer->usage) & usage) != usage)) - { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; - status_t error; - sp<GraphicBuffer> graphicBuffer( - mGraphicBufferAlloc->createGraphicBuffer( - w, h, format, usage, &error)); - if (graphicBuffer == 0) { - ST_LOGE("dequeueBuffer: SurfaceComposer::createGraphicBuffer " - "failed"); - return error; - } - if (updateFormat) { - mPixelFormat = format; + if (fence != EGL_NO_SYNC_KHR) { + EGLint result = eglClientWaitSyncKHR(dpy, fence, 0, 1000000000); + // If something goes wrong, log the error, but return the buffer without + // synchronizing access to it. It's too late at this point to abort the + // dequeue operation. + if (result == EGL_FALSE) { + LOGE("dequeueBuffer: error waiting for fence: %#x", eglGetError()); + } else if (result == EGL_TIMEOUT_EXPIRED_KHR) { + LOGE("dequeueBuffer: timeout waiting for fence"); } - mSlots[buf].mGraphicBuffer = graphicBuffer; - mSlots[buf].mRequestBufferCalled = false; - if (mSlots[buf].mEglImage != EGL_NO_IMAGE_KHR) { - eglDestroyImageKHR(mSlots[buf].mEglDisplay, mSlots[buf].mEglImage); - mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; - mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; - } - if (mCurrentTexture == buf) { - // The current texture no longer references the buffer in this slot - // since we just allocated a new buffer. - mCurrentTexture = INVALID_BUFFER_SLOT; - } - returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; + eglDestroySyncKHR(dpy, fence); } + ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf, mSlots[buf].mGraphicBuffer->handle, returnFlags); + return returnFlags; } @@ -712,8 +763,8 @@ status_t SurfaceTexture::updateTexImage() { // Update the GL texture object. EGLImageKHR image = mSlots[buf].mEglImage; + EGLDisplay dpy = eglGetCurrentDisplay(); if (image == EGL_NO_IMAGE_KHR) { - EGLDisplay dpy = eglGetCurrentDisplay(); if (mSlots[buf].mGraphicBuffer == 0) { ST_LOGE("buffer at slot %d is null", buf); return BAD_VALUE; @@ -746,16 +797,32 @@ status_t SurfaceTexture::updateTexImage() { return -EINVAL; } - ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", mCurrentTexture, - mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, buf, - mSlots[buf].mGraphicBuffer->handle); + if (mCurrentTexture != INVALID_BUFFER_SLOT) { + if (mUseFenceSync) { + EGLSyncKHR fence = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, + NULL); + if (fence == EGL_NO_SYNC_KHR) { + LOGE("updateTexImage: error creating fence: %#x", + eglGetError()); + return -EINVAL; + } + glFlush(); + mSlots[mCurrentTexture].mFence = fence; + } + } + + ST_LOGV("updateTexImage: (slot=%d buf=%p) -> (slot=%d buf=%p)", + mCurrentTexture, + mCurrentTextureBuf != NULL ? mCurrentTextureBuf->handle : 0, + buf, mSlots[buf].mGraphicBuffer->handle); if (mCurrentTexture != INVALID_BUFFER_SLOT) { // The current buffer becomes FREE if it was still in the queued // state. If it has already been given to the client // (synchronous mode), then it stays in DEQUEUED state. - if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) + if (mSlots[mCurrentTexture].mBufferState == BufferSlot::QUEUED) { mSlots[mCurrentTexture].mBufferState = BufferSlot::FREE; + } } // Update the SurfaceTexture state. diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp index 6d1b95126995..c3139042836d 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -536,6 +536,20 @@ void fillRGBA8Buffer(uint8_t* buf, int w, int h, int stride) { } } +void fillRGBA8BufferSolid(uint8_t* buf, int w, int h, int stride, uint8_t r, + uint8_t g, uint8_t b, uint8_t a) { + const size_t PIXEL_SIZE = 4; + for (int y = 0; y < h; y++) { + for (int x = 0; x < h; x++) { + off_t offset = (y * stride + x) * PIXEL_SIZE; + buf[offset + 0] = r; + buf[offset + 1] = g; + buf[offset + 2] = b; + buf[offset + 3] = a; + } + } +} + TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferNpot) { const int texWidth = 64; const int texHeight = 66; @@ -1616,4 +1630,101 @@ TEST_F(SurfaceTextureGLThreadToGLTest, } } +class SurfaceTextureFBOTest : public SurfaceTextureGLTest { +protected: + + virtual void SetUp() { + SurfaceTextureGLTest::SetUp(); + + glGenFramebuffers(1, &mFbo); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glGenTextures(1, &mFboTex); + glBindTexture(GL_TEXTURE_2D, mFboTex); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, getSurfaceWidth(), + getSurfaceHeight(), 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); + glBindTexture(GL_TEXTURE_2D, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, + GL_TEXTURE_2D, mFboTex, 0); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError()); + } + + virtual void TearDown() { + SurfaceTextureGLTest::TearDown(); + + glDeleteTextures(1, &mFboTex); + glDeleteFramebuffers(1, &mFbo); + } + + GLuint mFbo; + GLuint mFboTex; +}; + +// This test is intended to verify that proper synchronization is done when +// rendering into an FBO. +TEST_F(SurfaceTextureFBOTest, BlitFromCpuFilledBufferToFbo) { + const int texWidth = 64; + const int texHeight = 64; + + ASSERT_EQ(NO_ERROR, native_window_set_buffers_geometry(mANW.get(), + texWidth, texHeight, HAL_PIXEL_FORMAT_RGBA_8888)); + ASSERT_EQ(NO_ERROR, native_window_set_usage(mANW.get(), + GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN)); + + android_native_buffer_t* anb; + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + sp<GraphicBuffer> buf(new GraphicBuffer(anb, false)); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer())); + + // Fill the buffer with green + uint8_t* img = NULL; + buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 0, 255, + 0, 255); + buf->unlock(); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer())); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + drawTexture(); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + + for (int i = 0; i < 4; i++) { + SCOPED_TRACE(String8::format("frame %d", i).string()); + + ASSERT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb)); + ASSERT_TRUE(anb != NULL); + + buf = new GraphicBuffer(anb, false); + ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), + buf->getNativeBuffer())); + + // Fill the buffer with red + ASSERT_EQ(NO_ERROR, buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, + (void**)(&img))); + fillRGBA8BufferSolid(img, texWidth, texHeight, buf->getStride(), 255, 0, + 0, 255); + ASSERT_EQ(NO_ERROR, buf->unlock()); + ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), + buf->getNativeBuffer())); + + ASSERT_EQ(NO_ERROR, mST->updateTexImage()); + + drawTexture(); + + EXPECT_TRUE(checkPixel( 24, 39, 255, 0, 0, 255)); + } + + glBindFramebuffer(GL_FRAMEBUFFER, mFbo); + + EXPECT_TRUE(checkPixel( 24, 39, 0, 255, 0, 255)); +} + } // namespace android diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp index b311f3522518..078be9446c88 100644 --- a/media/libmedia/IStreamSource.cpp +++ b/media/libmedia/IStreamSource.cpp @@ -30,7 +30,7 @@ namespace android { const char *const IStreamListener::kKeyResumeAtPTS = "resume-at-PTS"; // static -const char *const IStreamListener::kKeyFormatChange = "format-change"; +const char *const IStreamListener::kKeyDiscontinuityMask = "discontinuity-mask"; enum { // IStreamSource diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 2a5c0a6c53c6..93ab7043cf30 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -54,6 +54,7 @@ NuPlayer::NuPlayer() mVideoEOS(false), mScanSourcesPending(false), mScanSourcesGeneration(0), + mTimeDiscontinuityPending(false), mFlushingAudio(NONE), mFlushingVideo(NONE), mResetInProgress(false), @@ -462,11 +463,24 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { { LOGV("kWhatReset"); + if (mRenderer != NULL) { + // There's an edge case where the renderer owns all output + // buffers and is paused, therefore the decoder will not read + // more input data and will never encounter the matching + // discontinuity. To avoid this, we resume the renderer. + + if (mFlushingAudio == AWAITING_DISCONTINUITY + || mFlushingVideo == AWAITING_DISCONTINUITY) { + mRenderer->resume(); + } + } + if (mFlushingAudio != NONE || mFlushingVideo != NONE) { // We're currently flushing, postpone the reset until that's // completed. - LOGV("postponing reset"); + LOGV("postponing reset mFlushingAudio=%d, mFlushingVideo=%d", + mFlushingAudio, mFlushingVideo); mResetPostponed = true; break; @@ -477,6 +491,8 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { break; } + mTimeDiscontinuityPending = true; + if (mAudioDecoder != NULL) { flushDecoder(true /* audio */, true /* needShutdown */); } @@ -540,7 +556,10 @@ void NuPlayer::finishFlushIfPossible() { LOGV("both audio and video are flushed now."); - mRenderer->signalTimeDiscontinuity(); + if (mTimeDiscontinuityPending) { + mRenderer->signalTimeDiscontinuity(); + mTimeDiscontinuityPending = false; + } if (mAudioDecoder != NULL) { mAudioDecoder->signalResume(); @@ -663,10 +682,15 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); bool formatChange = - type == ATSParser::DISCONTINUITY_FORMATCHANGE; + (audio && + (type & ATSParser::DISCONTINUITY_AUDIO_FORMAT)) + || (!audio && + (type & ATSParser::DISCONTINUITY_VIDEO_FORMAT)); + + bool timeChange = (type & ATSParser::DISCONTINUITY_TIME) != 0; - LOGV("%s discontinuity (formatChange=%d)", - audio ? "audio" : "video", formatChange); + LOGI("%s discontinuity (formatChange=%d, time=%d)", + audio ? "audio" : "video", formatChange, timeChange); if (audio) { mSkipRenderingAudioUntilMediaTimeUs = -1; @@ -674,26 +698,45 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mSkipRenderingVideoUntilMediaTimeUs = -1; } - sp<AMessage> extra; - if (accessUnit->meta()->findMessage("extra", &extra) - && extra != NULL) { - int64_t resumeAtMediaTimeUs; - if (extra->findInt64( - "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) { - LOGI("suppressing rendering of %s until %lld us", - audio ? "audio" : "video", resumeAtMediaTimeUs); - - if (audio) { - mSkipRenderingAudioUntilMediaTimeUs = - resumeAtMediaTimeUs; - } else { - mSkipRenderingVideoUntilMediaTimeUs = - resumeAtMediaTimeUs; + if (timeChange) { + sp<AMessage> extra; + if (accessUnit->meta()->findMessage("extra", &extra) + && extra != NULL) { + int64_t resumeAtMediaTimeUs; + if (extra->findInt64( + "resume-at-mediatimeUs", &resumeAtMediaTimeUs)) { + LOGI("suppressing rendering of %s until %lld us", + audio ? "audio" : "video", resumeAtMediaTimeUs); + + if (audio) { + mSkipRenderingAudioUntilMediaTimeUs = + resumeAtMediaTimeUs; + } else { + mSkipRenderingVideoUntilMediaTimeUs = + resumeAtMediaTimeUs; + } } } } - flushDecoder(audio, formatChange); + mTimeDiscontinuityPending = + mTimeDiscontinuityPending || timeChange; + + if (formatChange || timeChange) { + flushDecoder(audio, formatChange); + } else { + // This stream is unaffected by the discontinuity + + if (audio) { + mFlushingAudio = FLUSHED; + } else { + mFlushingVideo = FLUSHED; + } + + finishFlushIfPossible(); + + return -EWOULDBLOCK; + } } reply->setInt32("err", err); @@ -794,6 +837,11 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2) { } void NuPlayer::flushDecoder(bool audio, bool needShutdown) { + if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) { + LOGI("flushDecoder %s without decoder present", + audio ? "audio" : "video"); + } + // Make sure we don't continue to scan sources until we finish flushing. ++mScanSourcesGeneration; mScanSourcesPending = false; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h index f23deea60765..ffc710ee05c1 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.h +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h @@ -112,6 +112,10 @@ private: SHUT_DOWN, }; + // Once the current flush is complete this indicates whether the + // notion of time has changed. + bool mTimeDiscontinuityPending; + FlushStatus mFlushingAudio; FlushStatus mFlushingVideo; bool mResetInProgress; diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 640e9fac9c07..0cb7f45b6f62 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -628,11 +628,16 @@ void NuPlayer::Renderer::onPause() { mAudioSink->pause(); } + LOGV("now paused audio queue has %d entries, video has %d entries", + mAudioQueue.size(), mVideoQueue.size()); + mPaused = true; } void NuPlayer::Renderer::onResume() { - CHECK(mPaused); + if (!mPaused) { + return; + } if (mHasAudio) { mAudioSink->start(); diff --git a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp index f79565425665..2e63b3b6afbe 100644 --- a/media/libmediaplayerservice/nuplayer/StreamingSource.cpp +++ b/media/libmediaplayerservice/nuplayer/StreamingSource.cpp @@ -63,17 +63,22 @@ status_t NuPlayer::StreamingSource::feedMoreTSData() { mFinalResult = ERROR_END_OF_STREAM; break; } else if (n == INFO_DISCONTINUITY) { - ATSParser::DiscontinuityType type = ATSParser::DISCONTINUITY_SEEK; + int32_t type = ATSParser::DISCONTINUITY_SEEK; - int32_t formatChange; + int32_t mask; if (extra != NULL && extra->findInt32( - IStreamListener::kKeyFormatChange, &formatChange) - && formatChange != 0) { - type = ATSParser::DISCONTINUITY_FORMATCHANGE; + IStreamListener::kKeyDiscontinuityMask, &mask)) { + if (mask == 0) { + LOGE("Client specified an illegal discontinuity type."); + return ERROR_UNSUPPORTED; + } + + type = mask; } - mTSParser->signalDiscontinuity(type, extra); + mTSParser->signalDiscontinuity( + (ATSParser::DiscontinuityType)type, extra); } else if (n < 0) { CHECK_EQ(n, -EWOULDBLOCK); break; diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index d947760c19b6..dbc9b7ecb5df 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -342,6 +342,7 @@ void ACodec::initiateSetup(const sp<AMessage> &msg) { } void ACodec::signalFlush() { + LOGV("[%s] signalFlush", mComponentName.c_str()); (new AMessage(kWhatFlush, id()))->post(); } @@ -1092,6 +1093,20 @@ status_t ACodec::initNativeWindow() { return OK; } +size_t ACodec::countBuffersOwnedByComponent(OMX_U32 portIndex) const { + size_t n = 0; + + for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { + const BufferInfo &info = mBuffers[portIndex].itemAt(i); + + if (info.mStatus == BufferInfo::OWNED_BY_COMPONENT) { + ++n; + } + } + + return n; +} + bool ACodec::allYourBuffersAreBelongToUs( OMX_U32 portIndex) { for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { @@ -2041,6 +2056,14 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) { case kWhatFlush: { + LOGV("[%s] ExecutingState flushing now " + "(codec owns %d/%d input, %d/%d output).", + mCodec->mComponentName.c_str(), + mCodec->countBuffersOwnedByComponent(kPortIndexInput), + mCodec->mBuffers[kPortIndexInput].size(), + mCodec->countBuffersOwnedByComponent(kPortIndexOutput), + mCodec->mBuffers[kPortIndexOutput].size()); + mActive = false; CHECK_EQ(mCodec->mOMX->sendCommand( @@ -2180,6 +2203,12 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent( err); mCodec->signalError(); + + // This is technically not correct, since we were unable + // to allocate output buffers and therefore the output port + // remains disabled. It is necessary however to allow us + // to shutdown the codec properly. + mCodec->changeState(mCodec->mExecutingState); } return true; @@ -2408,6 +2437,9 @@ bool ACodec::FlushingState::onMessageReceived(const sp<AMessage> &msg) { bool ACodec::FlushingState::onOMXEvent( OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) { + LOGV("[%s] FlushingState onOMXEvent(%d,%ld)", + mCodec->mComponentName.c_str(), event, data1); + switch (event) { case OMX_EventCmdComplete: { diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp index 2b27ee222d6a..86b33d1c4fd1 100644 --- a/media/libstagefright/SurfaceMediaSource.cpp +++ b/media/libstagefright/SurfaceMediaSource.cpp @@ -336,7 +336,7 @@ status_t SurfaceMediaSource::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, (uint32_t(buffer->height) != h) || (uint32_t(buffer->format) != format) || ((uint32_t(buffer->usage) & usage) != usage)) { - usage |= GraphicBuffer::USAGE_HW_TEXTURE; + usage |= GraphicBuffer::USAGE_HW_VIDEO_ENCODER; status_t error; sp<GraphicBuffer> graphicBuffer( mGraphicBufferAlloc->createGraphicBuffer( diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 72f12827f02d..6cec63a6af71 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -123,6 +123,9 @@ private: void extractAACFrames(const sp<ABuffer> &buffer); + bool isAudio() const; + bool isVideo() const; + DISALLOW_EVIL_CONSTRUCTORS(Stream); }; @@ -401,7 +404,7 @@ ATSParser::Stream::Stream( case STREAMTYPE_H264: mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::H264); break; - case STREAMTYPE_MPEG2_AUDIO_ATDS: + case STREAMTYPE_MPEG2_AUDIO_ADTS: mQueue = new ElementaryStreamQueue(ElementaryStreamQueue::AAC); break; case STREAMTYPE_MPEG1_AUDIO: @@ -486,6 +489,31 @@ status_t ATSParser::Stream::parse( return OK; } +bool ATSParser::Stream::isVideo() const { + switch (mStreamType) { + case STREAMTYPE_H264: + case STREAMTYPE_MPEG1_VIDEO: + case STREAMTYPE_MPEG2_VIDEO: + case STREAMTYPE_MPEG4_VIDEO: + return true; + + default: + return false; + } +} + +bool ATSParser::Stream::isAudio() const { + switch (mStreamType) { + case STREAMTYPE_MPEG1_AUDIO: + case STREAMTYPE_MPEG2_AUDIO: + case STREAMTYPE_MPEG2_AUDIO_ADTS: + return true; + + default: + return false; + } +} + void ATSParser::Stream::signalDiscontinuity( DiscontinuityType type, const sp<AMessage> &extra) { if (mQueue == NULL) { @@ -495,34 +523,34 @@ void ATSParser::Stream::signalDiscontinuity( mPayloadStarted = false; mBuffer->setRange(0, 0); - switch (type) { - case DISCONTINUITY_SEEK: - case DISCONTINUITY_FORMATCHANGE: - { - bool isASeek = (type == DISCONTINUITY_SEEK); - - mQueue->clear(!isASeek); + bool clearFormat = false; + if (isAudio()) { + if (type & DISCONTINUITY_AUDIO_FORMAT) { + clearFormat = true; + } + } else { + if (type & DISCONTINUITY_VIDEO_FORMAT) { + clearFormat = true; + } + } - uint64_t resumeAtPTS; - if (extra != NULL - && extra->findInt64( - IStreamListener::kKeyResumeAtPTS, - (int64_t *)&resumeAtPTS)) { - int64_t resumeAtMediaTimeUs = - mProgram->convertPTSToTimestamp(resumeAtPTS); + mQueue->clear(clearFormat); - extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs); - } + if (type & DISCONTINUITY_TIME) { + uint64_t resumeAtPTS; + if (extra != NULL + && extra->findInt64( + IStreamListener::kKeyResumeAtPTS, + (int64_t *)&resumeAtPTS)) { + int64_t resumeAtMediaTimeUs = + mProgram->convertPTSToTimestamp(resumeAtPTS); - if (mSource != NULL) { - mSource->queueDiscontinuity(type, extra); - } - break; + extra->setInt64("resume-at-mediatimeUs", resumeAtMediaTimeUs); } + } - default: - TRESPASS(); - break; + if (mSource != NULL) { + mSource->queueDiscontinuity(type, extra); } } @@ -764,10 +792,7 @@ sp<MediaSource> ATSParser::Stream::getSource(SourceType type) { switch (type) { case VIDEO: { - if (mStreamType == STREAMTYPE_H264 - || mStreamType == STREAMTYPE_MPEG1_VIDEO - || mStreamType == STREAMTYPE_MPEG2_VIDEO - || mStreamType == STREAMTYPE_MPEG4_VIDEO) { + if (isVideo()) { return mSource; } break; @@ -775,9 +800,7 @@ sp<MediaSource> ATSParser::Stream::getSource(SourceType type) { case AUDIO: { - if (mStreamType == STREAMTYPE_MPEG1_AUDIO - || mStreamType == STREAMTYPE_MPEG2_AUDIO - || mStreamType == STREAMTYPE_MPEG2_AUDIO_ATDS) { + if (isAudio()) { return mSource; } break; diff --git a/media/libstagefright/mpeg2ts/ATSParser.h b/media/libstagefright/mpeg2ts/ATSParser.h index 878e5342430c..c8038d1835be 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.h +++ b/media/libstagefright/mpeg2ts/ATSParser.h @@ -33,9 +33,18 @@ struct MediaSource; struct ATSParser : public RefBase { enum DiscontinuityType { - DISCONTINUITY_NONE, - DISCONTINUITY_SEEK, - DISCONTINUITY_FORMATCHANGE + DISCONTINUITY_NONE = 0, + DISCONTINUITY_TIME = 1, + DISCONTINUITY_AUDIO_FORMAT = 2, + DISCONTINUITY_VIDEO_FORMAT = 4, + + DISCONTINUITY_SEEK = DISCONTINUITY_TIME, + + // For legacy reasons this also implies a time discontinuity. + DISCONTINUITY_FORMATCHANGE = + DISCONTINUITY_AUDIO_FORMAT + | DISCONTINUITY_VIDEO_FORMAT + | DISCONTINUITY_TIME, }; enum Flags { @@ -71,7 +80,7 @@ struct ATSParser : public RefBase { STREAMTYPE_MPEG2_VIDEO = 0x02, STREAMTYPE_MPEG1_AUDIO = 0x03, STREAMTYPE_MPEG2_AUDIO = 0x04, - STREAMTYPE_MPEG2_AUDIO_ATDS = 0x0f, + STREAMTYPE_MPEG2_AUDIO_ADTS = 0x0f, STREAMTYPE_MPEG4_VIDEO = 0x10, STREAMTYPE_H264 = 0x1b, }; diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp index ce07e32ddc26..f782ce554539 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp @@ -29,8 +29,17 @@ namespace android { AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta) - : mFormat(meta), + : mIsAudio(false), + mFormat(meta), mEOSResult(OK) { + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strncasecmp("audio/", mime, 6)) { + mIsAudio = true; + } else { + CHECK(!strncasecmp("video/", mime, 6)); + } } void AnotherPacketSource::setFormat(const sp<MetaData> &meta) { @@ -67,8 +76,7 @@ status_t AnotherPacketSource::dequeueAccessUnit(sp<ABuffer> *buffer) { int32_t discontinuity; if ((*buffer)->meta()->findInt32("discontinuity", &discontinuity)) { - - if (discontinuity == ATSParser::DISCONTINUITY_FORMATCHANGE) { + if (wasFormatChange(discontinuity)) { mFormat.clear(); } @@ -96,7 +104,7 @@ status_t AnotherPacketSource::read( int32_t discontinuity; if (buffer->meta()->findInt32("discontinuity", &discontinuity)) { - if (discontinuity == ATSParser::DISCONTINUITY_FORMATCHANGE) { + if (wasFormatChange(discontinuity)) { mFormat.clear(); } @@ -117,6 +125,15 @@ status_t AnotherPacketSource::read( return mEOSResult; } +bool AnotherPacketSource::wasFormatChange( + int32_t discontinuityType) const { + if (mIsAudio) { + return (discontinuityType & ATSParser::DISCONTINUITY_AUDIO_FORMAT) != 0; + } + + return (discontinuityType & ATSParser::DISCONTINUITY_VIDEO_FORMAT) != 0; +} + void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) { int32_t damaged; if (buffer->meta()->findInt32("damaged", &damaged) && damaged) { diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h index 439c78531fc0..c99f7f247f91 100644 --- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h +++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h @@ -61,10 +61,13 @@ private: Mutex mLock; Condition mCondition; + bool mIsAudio; sp<MetaData> mFormat; List<sp<ABuffer> > mBuffers; status_t mEOSResult; + bool wasFormatChange(int32_t discontinuityType) const; + DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource); }; diff --git a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp index f55be6e208b1..a089dbfa6050 100644 --- a/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2PSExtractor.cpp @@ -543,7 +543,7 @@ MPEG2PSExtractor::Track::Track( case ATSParser::STREAMTYPE_H264: mode = ElementaryStreamQueue::H264; break; - case ATSParser::STREAMTYPE_MPEG2_AUDIO_ATDS: + case ATSParser::STREAMTYPE_MPEG2_AUDIO_ADTS: mode = ElementaryStreamQueue::AAC; break; case ATSParser::STREAMTYPE_MPEG1_AUDIO: diff --git a/native/include/android/keycodes.h b/native/include/android/keycodes.h index 5d49775cb2c8..8414ff6eadc9 100644 --- a/native/include/android/keycodes.h +++ b/native/include/android/keycodes.h @@ -250,6 +250,10 @@ enum { AKEYCODE_LANGUAGE_SWITCH = 204, AKEYCODE_MANNER_MODE = 205, AKEYCODE_3D_MODE = 206, + AKEYCODE_CONTACTS = 207, + AKEYCODE_CALENDAR = 208, + AKEYCODE_MUSIC = 209, + AKEYCODE_CALCULATOR = 210, // NOTE: If you add a new keycode here you must also add it to several other files. // Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. diff --git a/packages/SettingsProvider/res/xml/bookmarks.xml b/packages/SettingsProvider/res/xml/bookmarks.xml index 83229f410d59..454f4562a239 100644 --- a/packages/SettingsProvider/res/xml/bookmarks.xml +++ b/packages/SettingsProvider/res/xml/bookmarks.xml @@ -19,6 +19,7 @@ Bookmarks for vendor apps should be added to a bookmarks resource overlay; not here. Typical shortcuts (not necessarily defined here): + 'a': Calculator 'b': Browser 'c': Contacts 'e': Email @@ -32,27 +33,27 @@ --> <bookmarks> <bookmark - package="com.android.browser" - class="com.android.browser.BrowserActivity" + category="android.intent.category.APP_CALCULATOR" + shortcut="a" /> + <bookmark + category="android.intent.category.APP_BROWSER" shortcut="b" /> <bookmark - package="com.android.contacts" - class="com.android.contacts.activities.ContactsFrontDoor" + category="android.intent.category.APP_CONTACTS" shortcut="c" /> <bookmark - package="com.google.android.email" - class="com.android.email.activity.Welcome" + category="android.intent.category.APP_EMAIL" shortcut="e" /> <bookmark - package="com.google.android.calendar" - class="com.android.calendar.LaunchActivity" + category="android.intent.category.APP_CALENDAR" shortcut="l" /> <bookmark - package="com.android.music" - class="com.android.music.MusicBrowserActivity" + category="android.intent.category.APP_MAPS" + shortcut="m" /> + <bookmark + category="android.intent.category.APP_MUSIC" shortcut="p" /> <bookmark - package="com.android.mms" - class="com.android.mms.ui.ConversationList" + category="android.intent.category.APP_MESSAGING" shortcut="s" /> </bookmarks> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index aa08e64353d8..080d345e51d9 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -63,7 +63,7 @@ public class DatabaseHelper extends SQLiteOpenHelper { // database gets upgraded properly. At a minimum, please confirm that 'upgradeVersion' // is properly propagated through your change. Not doing so will result in a loss of user // settings. - private static final int DATABASE_VERSION = 70; + private static final int DATABASE_VERSION = 71; private Context mContext; @@ -946,6 +946,12 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 70; } + if (upgradeVersion == 70) { + // Update all built-in bookmarks. Some of the package names have changed. + loadBookmarks(db); + upgradeVersion = 71; + } + // *** Remember to update DATABASE_VERSION above! if (upgradeVersion != currentVersion) { @@ -1086,16 +1092,11 @@ public class DatabaseHelper extends SQLiteOpenHelper { * Loads the default set of bookmarked shortcuts from an xml file. * * @param db The database to write the values into - * @param startingIndex The zero-based position at which bookmarks in this file should begin */ - private int loadBookmarks(SQLiteDatabase db, int startingIndex) { - Intent intent = new Intent(Intent.ACTION_MAIN, null); - intent.addCategory(Intent.CATEGORY_LAUNCHER); + private void loadBookmarks(SQLiteDatabase db) { ContentValues values = new ContentValues(); PackageManager packageManager = mContext.getPackageManager(); - int i = startingIndex; - try { XmlResourceParser parser = mContext.getResources().getXml(R.xml.bookmarks); XmlUtils.beginDocument(parser, "bookmarks"); @@ -1118,54 +1119,60 @@ public class DatabaseHelper extends SQLiteOpenHelper { String pkg = parser.getAttributeValue(null, "package"); String cls = parser.getAttributeValue(null, "class"); String shortcutStr = parser.getAttributeValue(null, "shortcut"); + String category = parser.getAttributeValue(null, "category"); int shortcutValue = shortcutStr.charAt(0); if (TextUtils.isEmpty(shortcutStr)) { Log.w(TAG, "Unable to get shortcut for: " + pkg + "/" + cls); + continue; } - ActivityInfo info = null; - ComponentName cn = new ComponentName(pkg, cls); - try { - info = packageManager.getActivityInfo(cn, 0); - } catch (PackageManager.NameNotFoundException e) { - String[] packages = packageManager.canonicalToCurrentPackageNames( - new String[] { pkg }); - cn = new ComponentName(packages[0], cls); + final Intent intent; + final String title; + if (pkg != null && cls != null) { + ActivityInfo info = null; + ComponentName cn = new ComponentName(pkg, cls); try { info = packageManager.getActivityInfo(cn, 0); - } catch (PackageManager.NameNotFoundException e1) { - Log.w(TAG, "Unable to add bookmark: " + pkg + "/" + cls, e); + } catch (PackageManager.NameNotFoundException e) { + String[] packages = packageManager.canonicalToCurrentPackageNames( + new String[] { pkg }); + cn = new ComponentName(packages[0], cls); + try { + info = packageManager.getActivityInfo(cn, 0); + } catch (PackageManager.NameNotFoundException e1) { + Log.w(TAG, "Unable to add bookmark: " + pkg + "/" + cls, e); + continue; + } } - } - - if (info != null) { + + intent = new Intent(Intent.ACTION_MAIN, null); + intent.addCategory(Intent.CATEGORY_LAUNCHER); intent.setComponent(cn); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - values.put(Settings.Bookmarks.INTENT, intent.toUri(0)); - values.put(Settings.Bookmarks.TITLE, - info.loadLabel(packageManager).toString()); - values.put(Settings.Bookmarks.SHORTCUT, shortcutValue); - db.insert("bookmarks", null, values); - i++; + title = info.loadLabel(packageManager).toString(); + } else if (category != null) { + intent = new Intent(Intent.ACTION_MAIN, null); + intent.addCategory(category); + title = ""; + } else { + Log.w(TAG, "Unable to add bookmark for shortcut " + shortcutStr + + ": missing package/class or category attributes"); + continue; } + + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + values.put(Settings.Bookmarks.INTENT, intent.toUri(0)); + values.put(Settings.Bookmarks.TITLE, title); + values.put(Settings.Bookmarks.SHORTCUT, shortcutValue); + db.delete("bookmarks", "shortcut = ?", + new String[] { Integer.toString(shortcutValue) }); + db.insert("bookmarks", null, values); } } catch (XmlPullParserException e) { Log.w(TAG, "Got execption parsing bookmarks.", e); } catch (IOException e) { Log.w(TAG, "Got execption parsing bookmarks.", e); } - - return i; - } - - /** - * Loads the default set of bookmark packages. - * - * @param db The database to write the values into - */ - private void loadBookmarks(SQLiteDatabase db) { - loadBookmarks(db, 0); } /** diff --git a/packages/SystemUI/res/layout/global_screenshot.xml b/packages/SystemUI/res/layout/global_screenshot.xml index d416af929680..8b337eada622 100644 --- a/packages/SystemUI/res/layout/global_screenshot.xml +++ b/packages/SystemUI/res/layout/global_screenshot.xml @@ -19,23 +19,18 @@ <ImageView android:id="@+id/global_screenshot_background" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#FF000000" + android:src="@android:color/black" android:visibility="gone" /> - <FrameLayout - android:id="@+id/global_screenshot_container" + <ImageView android:id="@+id/global_screenshot" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:background="@drawable/screenshot_panel" - android:visibility="gone"> - <ImageView android:id="@+id/global_screenshot" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:adjustViewBounds="true" /> - </FrameLayout> + android:visibility="gone" + android:adjustViewBounds="true" /> <ImageView android:id="@+id/global_screenshot_flash" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="#FFFFFFFF" + android:src="@android:color/white" android:visibility="gone" /> </FrameLayout> diff --git a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java index ad37603f4a07..2dcd80d74d1b 100644 --- a/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java +++ b/packages/SystemUI/src/com/android/systemui/screenshot/GlobalScreenshot.java @@ -38,11 +38,9 @@ import android.net.Uri; import android.os.AsyncTask; import android.os.Environment; import android.os.Process; -import android.os.ServiceManager; import android.provider.MediaStore; import android.util.DisplayMetrics; import android.view.Display; -import android.view.IWindowManager; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.Surface; @@ -50,9 +48,7 @@ import android.view.View; import android.view.ViewGroup; import android.view.WindowManager; import android.view.animation.Interpolator; -import android.widget.FrameLayout; import android.widget.ImageView; - import com.android.systemui.R; import java.io.File; @@ -77,7 +73,6 @@ class SaveImageInBackgroundData { */ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Void, SaveImageInBackgroundData> { - private static final String TAG = "SaveImageInBackgroundTask"; private static final String SCREENSHOTS_DIR_NAME = "Screenshots"; private static final String SCREENSHOT_FILE_NAME_TEMPLATE = "Screenshot_%s.png"; private static final String SCREENSHOT_FILE_PATH_TEMPLATE = "%s/%s/%s"; @@ -85,11 +80,8 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi private int mNotificationId; private NotificationManager mNotificationManager; private Notification.Builder mNotificationBuilder; - private Intent mLaunchIntent; - private String mImageDir; private String mImageFileName; private String mImageFilePath; - private String mImageDate; private long mImageTime; // WORKAROUND: We want the same notification across screenshots that we update so that we don't @@ -105,11 +97,11 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi // Prepare all the output metadata mImageTime = System.currentTimeMillis(); - mImageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime)); - mImageDir = Environment.getExternalStoragePublicDirectory( + String imageDate = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date(mImageTime)); + String imageDir = Environment.getExternalStoragePublicDirectory( Environment.DIRECTORY_PICTURES).getAbsolutePath(); - mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, mImageDate); - mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, mImageDir, + mImageFileName = String.format(SCREENSHOT_FILE_NAME_TEMPLATE, imageDate); + mImageFilePath = String.format(SCREENSHOT_FILE_PATH_TEMPLATE, imageDir, SCREENSHOTS_DIR_NAME, mImageFileName); // Create the large notification icon @@ -190,7 +182,7 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi } return params[0]; - }; + } @Override protected void onPostExecute(SaveImageInBackgroundData params) { @@ -202,14 +194,14 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi Resources r = params.context.getResources(); // Create the intent to show the screenshot in gallery - mLaunchIntent = new Intent(Intent.ACTION_VIEW); - mLaunchIntent.setDataAndType(params.imageUri, "image/png"); - mLaunchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + Intent launchIntent = new Intent(Intent.ACTION_VIEW); + launchIntent.setDataAndType(params.imageUri, "image/png"); + launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mNotificationBuilder .setContentTitle(r.getString(R.string.screenshot_saved_title)) .setContentText(r.getString(R.string.screenshot_saved_text)) - .setContentIntent(PendingIntent.getActivity(params.context, 0, mLaunchIntent, 0)) + .setContentIntent(PendingIntent.getActivity(params.context, 0, launchIntent, 0)) .setWhen(System.currentTimeMillis()) .setAutoCancel(true); @@ -218,7 +210,7 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi mNotificationManager.notify(mNotificationId, n); } params.finisher.run(); - }; + } } /** @@ -228,7 +220,6 @@ class SaveImageInBackgroundTask extends AsyncTask<SaveImageInBackgroundData, Voi * type of gallery? */ class GlobalScreenshot { - private static final String TAG = "GlobalScreenshot"; private static final int SCREENSHOT_NOTIFICATION_ID = 789; private static final int SCREENSHOT_FLASH_TO_PEAK_DURATION = 130; private static final int SCREENSHOT_DROP_IN_DURATION = 430; @@ -244,8 +235,6 @@ class GlobalScreenshot { private static final float SCREENSHOT_DROP_OUT_MIN_SCALE_OFFSET = 0f; private Context mContext; - private LayoutInflater mLayoutInflater; - private IWindowManager mIWindowManager; private WindowManager mWindowManager; private WindowManager.LayoutParams mWindowLayoutParams; private NotificationManager mNotificationManager; @@ -256,7 +245,6 @@ class GlobalScreenshot { private Bitmap mScreenBitmap; private View mScreenshotLayout; private ImageView mBackgroundView; - private FrameLayout mScreenshotContainerView; private ImageView mScreenshotView; private ImageView mScreenshotFlash; @@ -273,14 +261,13 @@ class GlobalScreenshot { public GlobalScreenshot(Context context) { Resources r = context.getResources(); mContext = context; - mLayoutInflater = (LayoutInflater) + LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Inflate the screenshot layout mDisplayMatrix = new Matrix(); - mScreenshotLayout = mLayoutInflater.inflate(R.layout.global_screenshot, null); + mScreenshotLayout = layoutInflater.inflate(R.layout.global_screenshot, null); mBackgroundView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_background); - mScreenshotContainerView = (FrameLayout) mScreenshotLayout.findViewById(R.id.global_screenshot_container); mScreenshotView = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot); mScreenshotFlash = (ImageView) mScreenshotLayout.findViewById(R.id.global_screenshot_flash); mScreenshotLayout.setFocusable(true); @@ -293,8 +280,6 @@ class GlobalScreenshot { }); // Setup the window that we are going to use - mIWindowManager = IWindowManager.Stub.asInterface( - ServiceManager.getService(Context.WINDOW_SERVICE)); mWindowLayoutParams = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, 0, 0, WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY, @@ -428,8 +413,8 @@ class GlobalScreenshot { mScreenshotLayout.post(new Runnable() { @Override public void run() { - mScreenshotContainerView.setLayerType(View.LAYER_TYPE_HARDWARE, null); - mScreenshotContainerView.buildLayer(); + mScreenshotView.setLayerType(View.LAYER_TYPE_HARDWARE, null); + mScreenshotView.buildLayer(); mScreenshotAnimation.start(); } }); @@ -463,20 +448,16 @@ class GlobalScreenshot { anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { - mBackgroundView.setFastAlpha(0f); + mBackgroundView.setAlpha(0f); mBackgroundView.setVisibility(View.VISIBLE); - mBackgroundView.fastInvalidate(); - mScreenshotContainerView.setFastAlpha(0f); - mScreenshotContainerView.setFastTranslationX(0f); - mScreenshotContainerView.setFastTranslationY(0f); - mScreenshotContainerView.setFastScaleX(SCREENSHOT_SCALE + mBgPaddingScale); - mScreenshotContainerView.setFastScaleY(SCREENSHOT_SCALE + mBgPaddingScale); - mScreenshotContainerView.setVisibility(View.VISIBLE); - mScreenshotContainerView.fastInvalidate(); - mScreenshotFlash.setFastAlpha(0f); + mScreenshotView.setAlpha(0f); + mScreenshotView.setTranslationX(0f); + mScreenshotView.setTranslationY(0f); + mScreenshotView.setScaleX(SCREENSHOT_SCALE + mBgPaddingScale); + mScreenshotView.setScaleY(SCREENSHOT_SCALE + mBgPaddingScale); + mScreenshotView.setVisibility(View.VISIBLE); + mScreenshotFlash.setAlpha(0f); mScreenshotFlash.setVisibility(View.VISIBLE); - mScreenshotFlash.fastInvalidate(); - mScreenshotLayout.invalidate(); } @Override public void onAnimationEnd(android.animation.Animator animation) { @@ -486,19 +467,15 @@ class GlobalScreenshot { anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - float t = ((Float) animation.getAnimatedValue()).floatValue(); + float t = (Float) animation.getAnimatedValue(); float scaleT = (SCREENSHOT_SCALE + mBgPaddingScale) - - (float) scaleInterpolator.getInterpolation(t) + - scaleInterpolator.getInterpolation(t) * (SCREENSHOT_SCALE - SCREENSHOT_DROP_IN_MIN_SCALE); - mBackgroundView.setFastAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); - mBackgroundView.fastInvalidate(); - mScreenshotContainerView.setFastAlpha(t); - mScreenshotContainerView.setFastScaleX(scaleT); - mScreenshotContainerView.setFastScaleY(scaleT); - mScreenshotContainerView.fastInvalidate(); - mScreenshotFlash.setFastAlpha(flashAlphaInterpolator.getInterpolation(t)); - mScreenshotFlash.fastInvalidate(); - mScreenshotLayout.invalidate(); + mBackgroundView.setAlpha(scaleInterpolator.getInterpolation(t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(t); + mScreenshotView.setScaleX(scaleT); + mScreenshotView.setScaleY(scaleT); + mScreenshotFlash.setAlpha(flashAlphaInterpolator.getInterpolation(t)); } }); return anim; @@ -511,8 +488,8 @@ class GlobalScreenshot { @Override public void onAnimationEnd(Animator animation) { mBackgroundView.setVisibility(View.GONE); - mScreenshotContainerView.setVisibility(View.GONE); - mScreenshotContainerView.setLayerType(View.LAYER_TYPE_NONE, null); + mScreenshotView.setVisibility(View.GONE); + mScreenshotView.setLayerType(View.LAYER_TYPE_NONE, null); } }); @@ -522,17 +499,13 @@ class GlobalScreenshot { anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - float t = ((Float) animation.getAnimatedValue()).floatValue(); + float t = (Float) animation.getAnimatedValue(); float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) - - (float) t * (SCREENSHOT_DROP_IN_MIN_SCALE - - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE); - mBackgroundView.setFastAlpha((1f - t) * BACKGROUND_ALPHA); - mBackgroundView.fastInvalidate(); - mScreenshotContainerView.setFastAlpha(1f - t); - mScreenshotContainerView.setFastScaleX(scaleT); - mScreenshotContainerView.setFastScaleY(scaleT); - mScreenshotContainerView.fastInvalidate(); - mScreenshotLayout.invalidate(); + - t * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_FAST_DROP_OUT_MIN_SCALE); + mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(1f - t); + mScreenshotView.setScaleX(scaleT); + mScreenshotView.setScaleY(scaleT); } }); } else { @@ -563,19 +536,16 @@ class GlobalScreenshot { anim.addUpdateListener(new AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { - float t = ((Float) animation.getAnimatedValue()).floatValue(); + float t = (Float) animation.getAnimatedValue(); float scaleT = (SCREENSHOT_DROP_IN_MIN_SCALE + mBgPaddingScale) - - (float) scaleInterpolator.getInterpolation(t) + - scaleInterpolator.getInterpolation(t) * (SCREENSHOT_DROP_IN_MIN_SCALE - SCREENSHOT_DROP_OUT_MIN_SCALE); - mBackgroundView.setFastAlpha((1f - t) * BACKGROUND_ALPHA); - mBackgroundView.fastInvalidate(); - mScreenshotContainerView.setFastAlpha(1f - scaleInterpolator.getInterpolation(t)); - mScreenshotContainerView.setFastScaleX(scaleT); - mScreenshotContainerView.setFastScaleY(scaleT); - mScreenshotContainerView.setFastTranslationX(t * finalPos.x); - mScreenshotContainerView.setFastTranslationY(t * finalPos.y); - mScreenshotContainerView.fastInvalidate(); - mScreenshotLayout.invalidate(); + mBackgroundView.setAlpha((1f - t) * BACKGROUND_ALPHA); + mScreenshotView.setAlpha(1f - scaleInterpolator.getInterpolation(t)); + mScreenshotView.setScaleX(scaleT); + mScreenshotView.setScaleY(scaleT); + mScreenshotView.setTranslationX(t * finalPos.x); + mScreenshotView.setTranslationY(t * finalPos.y); } }); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 00320d4fa2dd..0655624b2e30 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -69,6 +69,7 @@ import android.util.DisplayMetrics; import android.util.EventLog; import android.util.Log; import android.util.Slog; +import android.util.SparseArray; import android.view.Gravity; import android.view.HapticFeedbackConstants; import android.view.IWindowManager; @@ -230,7 +231,30 @@ public class PhoneWindowManager implements WindowManagerPolicy { // Useful scan codes. private static final int SW_LID = 0x00; private static final int BTN_MOUSE = 0x110; - + + /* Table of Application Launch keys. Maps from key codes to intent categories. + * + * These are special keys that are used to launch particular kinds of applications, + * such as a web browser. HID defines nearly a hundred of them in the Consumer (0x0C) + * usage page. We don't support quite that many yet... + */ + static SparseArray<String> sApplicationLaunchKeyCategories; + static { + sApplicationLaunchKeyCategories = new SparseArray<String>(); + sApplicationLaunchKeyCategories.append( + KeyEvent.KEYCODE_EXPLORER, Intent.CATEGORY_APP_BROWSER); + sApplicationLaunchKeyCategories.append( + KeyEvent.KEYCODE_ENVELOPE, Intent.CATEGORY_APP_EMAIL); + sApplicationLaunchKeyCategories.append( + KeyEvent.KEYCODE_CONTACTS, Intent.CATEGORY_APP_CONTACTS); + sApplicationLaunchKeyCategories.append( + KeyEvent.KEYCODE_CALENDAR, Intent.CATEGORY_APP_CALENDAR); + sApplicationLaunchKeyCategories.append( + KeyEvent.KEYCODE_MUSIC, Intent.CATEGORY_APP_MUSIC); + sApplicationLaunchKeyCategories.append( + KeyEvent.KEYCODE_CALCULATOR, Intent.CATEGORY_APP_CALCULATOR); + } + /** * Lock protecting internal state. Must not call out into window * manager with lock held. (This lock will be acquired in places @@ -1649,6 +1673,23 @@ public class PhoneWindowManager implements WindowManagerPolicy { } } + // Handle application launch keys. + if (down && repeatCount == 0) { + String category = sApplicationLaunchKeyCategories.get(keyCode); + if (category != null) { + Intent intent = new Intent(Intent.ACTION_MAIN); + intent.addCategory(category); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException ex) { + Slog.w(TAG, "Dropping application launch key because " + + "the activity to which it is registered was not found: " + + "keyCode=" + keyCode + ", category=" + category, ex); + } + } + } + return 0; } diff --git a/services/java/com/android/server/WallpaperManagerService.java b/services/java/com/android/server/WallpaperManagerService.java index 7fa404e85494..4925a4e46e51 100644 --- a/services/java/com/android/server/WallpaperManagerService.java +++ b/services/java/com/android/server/WallpaperManagerService.java @@ -476,6 +476,13 @@ class WallpaperManagerService extends IWallpaperManager.Stub { ParcelFileDescriptor updateWallpaperBitmapLocked(String name) { if (name == null) name = ""; try { + if (!WALLPAPER_DIR.exists()) { + WALLPAPER_DIR.mkdir(); + FileUtils.setPermissions( + WALLPAPER_DIR.getPath(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, + -1, -1); + } ParcelFileDescriptor fd = ParcelFileDescriptor.open(WALLPAPER_FILE, MODE_CREATE|MODE_READ_WRITE); mName = name; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 15ef056ee255..367844dde026 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -2720,6 +2720,10 @@ public final class ActivityManagerService extends ActivityManagerNative } if (!r.finishing) { Slog.w(TAG, "Force removing " + r + ": app died, no saved state"); + EventLog.writeEvent(EventLogTags.AM_FINISH_ACTIVITY, + System.identityHashCode(r), + r.task.taskId, r.shortComponentName, + "proc died without state saved"); } r.makeFinishing(); mMainStack.mHistory.remove(i); @@ -13596,6 +13600,7 @@ public final class ActivityManagerService extends ActivityManagerNative adj = ProcessList.PERCEPTIBLE_APP_ADJ; app.adjType = "stopping"; } + app.hidden = false; app.foregroundActivities = true; } } diff --git a/services/surfaceflinger/SurfaceTextureLayer.cpp b/services/surfaceflinger/SurfaceTextureLayer.cpp index 4390ca19fc24..5020e0007ae7 100644 --- a/services/surfaceflinger/SurfaceTextureLayer.cpp +++ b/services/surfaceflinger/SurfaceTextureLayer.cpp @@ -28,7 +28,7 @@ namespace android { SurfaceTextureLayer::SurfaceTextureLayer(GLuint tex, const sp<Layer>& layer) - : SurfaceTexture(tex), mLayer(layer) { + : SurfaceTexture(tex, true, GL_TEXTURE_EXTERNAL_OES, false), mLayer(layer) { } SurfaceTextureLayer::~SurfaceTextureLayer() { diff --git a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java index b1cef15ce639..10802b449d03 100644 --- a/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java +++ b/tests/TileBenchmark/src/com/test/tilebenchmark/ProfiledWebView.java @@ -110,9 +110,9 @@ public class ProfiledWebView extends WebView { * been redrawn. */ @Override - protected void pageSwapCallback() { + protected void pageSwapCallback(boolean startAnim) { mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis; - super.pageSwapCallback(); + super.pageSwapCallback(startAnim); Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis"); mIsTesting = true; |