diff options
26 files changed, 1129 insertions, 67 deletions
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 93baefd..f8650eb 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -98,3 +98,27 @@ LOCAL_MODULE_TAGS := debug LOCAL_MODULE:= audioloop include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + stream.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libstagefright liblog libutils libbinder libsurfaceflinger_client \ + libstagefright_foundation libmedia + +LOCAL_C_INCLUDES:= \ + $(JNI_H_INCLUDE) \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax + +LOCAL_CFLAGS += -Wno-multichar + +LOCAL_MODULE_TAGS := debug + +LOCAL_MODULE:= stream + +include $(BUILD_EXECUTABLE) diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp new file mode 100644 index 0000000..f2b5638 --- /dev/null +++ b/cmds/stagefright/stream.cpp @@ -0,0 +1,168 @@ +#include <binder/ProcessState.h> + +#include <media/IStreamSource.h> +#include <media/mediaplayer.h> +#include <media/stagefright/foundation/ADebug.h> + +#include <binder/IServiceManager.h> +#include <media/IMediaPlayerService.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <fcntl.h> + +using namespace android; + +struct MyStreamSource : public BnStreamSource { + // Caller retains ownership of fd. + MyStreamSource(int fd); + + virtual void setListener(const sp<IStreamListener> &listener); + virtual void setBuffers(const Vector<sp<IMemory> > &buffers); + + virtual void onBufferAvailable(size_t index); + +protected: + virtual ~MyStreamSource(); + +private: + int mFd; + + sp<IStreamListener> mListener; + Vector<sp<IMemory> > mBuffers; + + DISALLOW_EVIL_CONSTRUCTORS(MyStreamSource); +}; + +MyStreamSource::MyStreamSource(int fd) + : mFd(fd) { + CHECK_GE(fd, 0); +} + +MyStreamSource::~MyStreamSource() { +} + +void MyStreamSource::setListener(const sp<IStreamListener> &listener) { + mListener = listener; +} + +void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) { + mBuffers = buffers; +} + +void MyStreamSource::onBufferAvailable(size_t index) { + CHECK_LT(index, mBuffers.size()); + sp<IMemory> mem = mBuffers.itemAt(index); + + ssize_t n = read(mFd, mem->pointer(), mem->size()); + if (n <= 0) { + mListener->queueCommand(IStreamListener::EOS); + } else { + mListener->queueBuffer(index, n); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +struct MyClient : public BnMediaPlayerClient { + MyClient() + : mEOS(false) { + } + + virtual void notify(int msg, int ext1, int ext2) { + Mutex::Autolock autoLock(mLock); + + if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) { + mEOS = true; + mCondition.signal(); + } + } + + void waitForEOS() { + Mutex::Autolock autoLock(mLock); + while (!mEOS) { + mCondition.wait(mLock); + } + } + +protected: + virtual ~MyClient() { + } + +private: + Mutex mLock; + Condition mCondition; + + bool mEOS; + + DISALLOW_EVIL_CONSTRUCTORS(MyClient); +}; + +int main(int argc, char **argv) { + android::ProcessState::self()->startThreadPool(); + + if (argc != 2) { + fprintf(stderr, "Usage: %s filename\n", argv[0]); + return 1; + } + + sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient; + CHECK_EQ(composerClient->initCheck(), (status_t)OK); + + sp<SurfaceControl> control = + composerClient->createSurface( + getpid(), + String8("A Surface"), + 0, + 1280, + 800, + PIXEL_FORMAT_RGB_565, + 0); + + CHECK(control != NULL); + CHECK(control->isValid()); + + CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + CHECK_EQ(control->setLayer(30000), (status_t)OK); + CHECK_EQ(control->show(), (status_t)OK); + CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + + sp<Surface> surface = control->getSurface(); + CHECK(surface != NULL); + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("media.player")); + sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + + CHECK(service.get() != NULL); + + int fd = open(argv[1], O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'.", argv[1]); + return 1; + } + + sp<MyClient> client = new MyClient; + + sp<IMediaPlayer> player = + service->create(getpid(), client, new MyStreamSource(fd), 0); + + if (player != NULL) { + player->setVideoSurface(surface); + player->start(); + + client->waitForEOS(); + + player->stop(); + } else { + fprintf(stderr, "failed to instantiate player.\n"); + } + + close(fd); + fd = -1; + + composerClient->dispose(); + + return 0; +} diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java index d596a7f..e967522 100644 --- a/core/java/android/view/Surface.java +++ b/core/java/android/view/Surface.java @@ -374,6 +374,19 @@ public class Surface implements Parcelable { } /** + * Copy the current screen contents into a bitmap and return it. + * + * @param width The desired width of the returned bitmap; the raw + * screen will be scaled down to this size. + * @param height The desired height of the returned bitmap; the raw + * screen will be scaled down to this size. + * @return Returns a Bitmap containing the screen contents. + * + * @hide + */ + public static native Bitmap screenshot(int width, int height); + + /** * set surface parameters. * needs to be inside open/closeTransaction block */ diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java index 784951f..51016f5 100644 --- a/core/java/android/view/WindowManager.java +++ b/core/java/android/view/WindowManager.java @@ -171,6 +171,7 @@ public interface WindowManager extends ViewManager { @ViewDebug.IntToString(from = TYPE_SYSTEM_OVERLAY, to = "TYPE_SYSTEM_OVERLAY"), @ViewDebug.IntToString(from = TYPE_PRIORITY_PHONE, to = "TYPE_PRIORITY_PHONE"), @ViewDebug.IntToString(from = TYPE_STATUS_BAR_PANEL, to = "TYPE_STATUS_BAR_PANEL"), + @ViewDebug.IntToString(from = TYPE_STATUS_BAR_SUB_PANEL, to = "TYPE_STATUS_BAR_SUB_PANEL"), @ViewDebug.IntToString(from = TYPE_SYSTEM_DIALOG, to = "TYPE_SYSTEM_DIALOG"), @ViewDebug.IntToString(from = TYPE_KEYGUARD_DIALOG, to = "TYPE_KEYGUARD_DIALOG"), @ViewDebug.IntToString(from = TYPE_SYSTEM_ERROR, to = "TYPE_SYSTEM_ERROR"), @@ -351,7 +352,7 @@ public interface WindowManager extends ViewManager { public static final int TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13; /** - * Window type: panel that slides out from the status bar + * Window type: panel that slides out from over the status bar */ public static final int TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14; @@ -375,6 +376,13 @@ public interface WindowManager extends ViewManager { public static final int TYPE_DRAG = FIRST_SYSTEM_WINDOW+16; /** + * Window type: panel that slides out from under the status bar + * @hide + */ + public static final int TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+17; + + + /** * End of types of system windows. */ public static final int LAST_SYSTEM_WINDOW = 2999; diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp index c4d6d1b..9a85edc 100644 --- a/core/jni/android_view_Surface.cpp +++ b/core/jni/android_view_Surface.cpp @@ -19,7 +19,9 @@ #include <stdio.h> #include "android_util_Binder.h" +#include "android/graphics/GraphicsJNI.h" +#include <binder/IMemory.h> #include <surfaceflinger/SurfaceComposerClient.h> #include <surfaceflinger/Surface.h> #include <ui/Region.h> @@ -91,15 +93,6 @@ struct no_t { static no_t no; -static __attribute__((noinline)) -void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL) -{ - if (!env->ExceptionOccurred()) { - jclass npeClazz = env->FindClass(exc); - env->ThrowNew(npeClazz, msg); - } -} - // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- @@ -444,6 +437,56 @@ static void Surface_unfreezeDisplay( } } +class ScreenshotBitmap : public SkBitmap { +public: + ScreenshotBitmap() { + } + + status_t update(int width, int height) { + status_t res = (width > 0 && height > 0) + ? mScreenshot.update(width, height) + : mScreenshot.update(); + if (res != NO_ERROR) { + return res; + } + + void const* base = mScreenshot.getPixels(); + uint32_t w = mScreenshot.getWidth(); + uint32_t h = mScreenshot.getHeight(); + uint32_t s = mScreenshot.getStride(); + uint32_t f = mScreenshot.getFormat(); + + ssize_t bpr = s * android::bytesPerPixel(f); + setConfig(convertPixelFormat(f), w, h, bpr); + if (f == PIXEL_FORMAT_RGBX_8888) { + setIsOpaque(true); + } + if (w > 0 && h > 0) { + setPixels((void*)base); + } else { + // be safe with an empty bitmap. + setPixels(NULL); + } + + return NO_ERROR; + } + +private: + ScreenshotClient mScreenshot; +}; + +static jobject Surface_screenshot(JNIEnv* env, jobject clazz, jint width, jint height) +{ + ScreenshotBitmap* bitmap = new ScreenshotBitmap(); + + if (bitmap->update(width, height) != NO_ERROR) { + delete bitmap; + return 0; + } + + return GraphicsJNI::createBitmap(env, bitmap, false, NULL); +} + static void Surface_setLayer( JNIEnv* env, jobject clazz, jint zorder) { @@ -669,6 +712,7 @@ static JNINativeMethod gSurfaceMethods[] = { {"setOrientation", "(III)V", (void*)Surface_setOrientation }, {"freezeDisplay", "(I)V", (void*)Surface_freezeDisplay }, {"unfreezeDisplay", "(I)V", (void*)Surface_unfreezeDisplay }, + {"screenshot", "(II)Landroid/graphics/Bitmap;", (void*)Surface_screenshot }, {"setLayer", "(I)V", (void*)Surface_setLayer }, {"setPosition", "(II)V",(void*)Surface_setPosition }, {"setSize", "(II)V",(void*)Surface_setSize }, diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 15b5db4..61223b3 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -195,6 +195,10 @@ <!-- XXXXXX END OF RESOURCES USING WRONG NAMING CONVENTION --> + <!-- If true, the screen can be rotated via the accelerometer in all 4 + rotations as the default behavior. --> + <bool name="config_allowAllRotations">true</bool> + <!-- The number of degrees to rotate the display when the keyboard is open. --> <integer name="config_lidOpenRotation">90</integer> diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h index 9416ca1..0bfb166 100644 --- a/include/media/IMediaPlayerService.h +++ b/include/media/IMediaPlayerService.h @@ -32,6 +32,7 @@ namespace android { class IMediaRecorder; class IOMX; +struct IStreamSource; class IMediaPlayerService: public IInterface { @@ -45,6 +46,11 @@ public: int audioSessionId = 0) = 0; virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length, int audioSessionId) = 0; + + virtual sp<IMediaPlayer> create( + pid_t pid, const sp<IMediaPlayerClient> &client, + const sp<IStreamSource> &source, int audioSessionId) = 0; + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0; virtual sp<IOMX> getOMX() = 0; diff --git a/include/media/IStreamSource.h b/include/media/IStreamSource.h new file mode 100644 index 0000000..6291124 --- /dev/null +++ b/include/media/IStreamSource.h @@ -0,0 +1,66 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ANDROID_ISTREAMSOURCE_H_ + +#define ANDROID_ISTREAMSOURCE_H_ + +#include <binder/IInterface.h> + +namespace android { + +struct IMemory; +struct IStreamListener; + +struct IStreamSource : public IInterface { + DECLARE_META_INTERFACE(StreamSource); + + virtual void setListener(const sp<IStreamListener> &listener) = 0; + virtual void setBuffers(const Vector<sp<IMemory> > &buffers) = 0; + + virtual void onBufferAvailable(size_t index) = 0; +}; + +struct IStreamListener : public IInterface { + DECLARE_META_INTERFACE(StreamListener); + + enum Command { + FLUSH, + DISCONTINUITY, + EOS + }; + + virtual void queueBuffer(size_t index, size_t size) = 0; + virtual void queueCommand(Command cmd) = 0; +}; + +//////////////////////////////////////////////////////////////////////////////// + +struct BnStreamSource : public BnInterface<IStreamSource> { + virtual status_t onTransact( + uint32_t code, const Parcel &data, Parcel *reply, + uint32_t flags = 0); +}; + +struct BnStreamListener : public BnInterface<IStreamListener> { + virtual status_t onTransact( + uint32_t code, const Parcel &data, Parcel *reply, + uint32_t flags = 0); +}; + +} // namespace android + +#endif // ANDROID_ISTREAMSOURCE_H_ diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 672931e..e7f1d6d 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -106,6 +106,11 @@ public: const KeyedVector<String8, String8> *headers = NULL) = 0; virtual status_t setDataSource(int fd, int64_t offset, int64_t length) = 0; + + virtual status_t setDataSource(const sp<IStreamSource> &source) { + return INVALID_OPERATION; + } + virtual status_t setVideoSurface(const sp<Surface>& surface) = 0; virtual status_t prepare() = 0; virtual status_t prepareAsync() = 0; diff --git a/include/ui/InputDispatcher.h b/include/ui/InputDispatcher.h index 15a3925..d0812de 100644 --- a/include/ui/InputDispatcher.h +++ b/include/ui/InputDispatcher.h @@ -179,8 +179,10 @@ struct InputWindow { TYPE_INPUT_METHOD = FIRST_SYSTEM_WINDOW+11, TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12, TYPE_WALLPAPER = FIRST_SYSTEM_WINDOW+13, - TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+14, + TYPE_STATUS_BAR_SUB_PANEL = FIRST_SYSTEM_WINDOW+14, TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15, + TYPE_DRAG = FIRST_SYSTEM_WINDOW+16, + TYPE_STATUS_BAR_PANEL = FIRST_SYSTEM_WINDOW+17, LAST_SYSTEM_WINDOW = 2999, }; diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk index 2e5cbe3..731c09d 100644 --- a/media/libmedia/Android.mk +++ b/media/libmedia/Android.mk @@ -15,6 +15,7 @@ LOCAL_SRC_FILES:= \ IMediaRecorderClient.cpp \ IMediaPlayer.cpp \ IMediaRecorder.cpp \ + IStreamSource.cpp \ Metadata.cpp \ mediarecorder.cpp \ IMediaMetadataRetriever.cpp \ diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp index 4abfa75..77199e1 100644 --- a/media/libmedia/IMediaPlayerService.cpp +++ b/media/libmedia/IMediaPlayerService.cpp @@ -23,6 +23,7 @@ #include <media/IMediaPlayerService.h> #include <media/IMediaRecorder.h> #include <media/IOMX.h> +#include <media/IStreamSource.h> #include <utils/Errors.h> // for status_t @@ -31,6 +32,7 @@ namespace android { enum { CREATE_URL = IBinder::FIRST_CALL_TRANSACTION, CREATE_FD, + CREATE_STREAM, DECODE_URL, DECODE_FD, CREATE_MEDIA_RECORDER, @@ -107,6 +109,21 @@ public: return interface_cast<IMediaPlayer>(reply.readStrongBinder());; } + virtual sp<IMediaPlayer> create( + pid_t pid, const sp<IMediaPlayerClient> &client, + const sp<IStreamSource> &source, int audioSessionId) { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor()); + data.writeInt32(static_cast<int32_t>(pid)); + data.writeStrongBinder(client->asBinder()); + data.writeStrongBinder(source->asBinder()); + data.writeInt32(static_cast<int32_t>(audioSessionId)); + + remote()->transact(CREATE_STREAM, data, &reply); + + return interface_cast<IMediaPlayer>(reply.readStrongBinder());; + } + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) { Parcel data, reply; @@ -184,6 +201,27 @@ status_t BnMediaPlayerService::onTransact( reply->writeStrongBinder(player->asBinder()); return NO_ERROR; } break; + case CREATE_STREAM: + { + CHECK_INTERFACE(IMediaPlayerService, data, reply); + + pid_t pid = static_cast<pid_t>(data.readInt32()); + + sp<IMediaPlayerClient> client = + interface_cast<IMediaPlayerClient>(data.readStrongBinder()); + + sp<IStreamSource> source = + interface_cast<IStreamSource>(data.readStrongBinder()); + + int audioSessionId = static_cast<int>(data.readInt32()); + + sp<IMediaPlayer> player = + create(pid, client, source, audioSessionId); + + reply->writeStrongBinder(player->asBinder()); + return OK; + break; + } case DECODE_URL: { CHECK_INTERFACE(IMediaPlayerService, data, reply); const char* url = data.readCString(); diff --git a/media/libmedia/IStreamSource.cpp b/media/libmedia/IStreamSource.cpp new file mode 100644 index 0000000..89f2b44 --- /dev/null +++ b/media/libmedia/IStreamSource.cpp @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "IStreamSource" +#include <utils/Log.h> + +#include <media/IStreamSource.h> + +#include <binder/IMemory.h> +#include <binder/Parcel.h> + +namespace android { + +enum { + // IStreamSource + SET_LISTENER = IBinder::FIRST_CALL_TRANSACTION, + SET_BUFFERS, + ON_BUFFER_AVAILABLE, + + // IStreamListener + QUEUE_BUFFER, + QUEUE_COMMAND, +}; + +struct BpStreamSource : public BpInterface<IStreamSource> { + BpStreamSource(const sp<IBinder> &impl) + : BpInterface<IStreamSource>(impl) { + } + + virtual void setListener(const sp<IStreamListener> &listener) { + Parcel data, reply; + data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + remote()->transact(SET_LISTENER, data, &reply); + } + + virtual void setBuffers(const Vector<sp<IMemory> > &buffers) { + Parcel data, reply; + data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); + data.writeInt32(static_cast<int32_t>(buffers.size())); + for (size_t i = 0; i < buffers.size(); ++i) { + data.writeStrongBinder(buffers.itemAt(i)->asBinder()); + } + remote()->transact(SET_BUFFERS, data, &reply); + } + + virtual void onBufferAvailable(size_t index) { + Parcel data, reply; + data.writeInterfaceToken(IStreamSource::getInterfaceDescriptor()); + data.writeInt32(static_cast<int32_t>(index)); + remote()->transact( + ON_BUFFER_AVAILABLE, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(StreamSource, "android.hardware.IStreamSource"); + +status_t BnStreamSource::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { + switch (code) { + case SET_LISTENER: + { + CHECK_INTERFACE(IStreamSource, data, reply); + setListener( + interface_cast<IStreamListener>(data.readStrongBinder())); + break; + } + + case SET_BUFFERS: + { + CHECK_INTERFACE(IStreamSource, data, reply); + size_t n = static_cast<size_t>(data.readInt32()); + Vector<sp<IMemory> > buffers; + for (size_t i = 0; i < n; ++i) { + sp<IMemory> mem = + interface_cast<IMemory>(data.readStrongBinder()); + + buffers.push(mem); + } + setBuffers(buffers); + break; + } + + case ON_BUFFER_AVAILABLE: + { + CHECK_INTERFACE(IStreamSource, data, reply); + onBufferAvailable(static_cast<size_t>(data.readInt32())); + break; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } + + return OK; +} + +//////////////////////////////////////////////////////////////////////////////// + +struct BpStreamListener : public BpInterface<IStreamListener> { + BpStreamListener(const sp<IBinder> &impl) + : BpInterface<IStreamListener>(impl) { + } + + virtual void queueBuffer(size_t index, size_t size) { + Parcel data, reply; + data.writeInterfaceToken(IStreamListener::getInterfaceDescriptor()); + data.writeInt32(static_cast<int32_t>(index)); + data.writeInt32(static_cast<int32_t>(size)); + + remote()->transact(QUEUE_BUFFER, data, &reply, IBinder::FLAG_ONEWAY); + } + + virtual void queueCommand(Command cmd) { + Parcel data, reply; + data.writeInterfaceToken(IStreamListener::getInterfaceDescriptor()); + data.writeInt32(static_cast<int32_t>(cmd)); + + remote()->transact(QUEUE_COMMAND, data, &reply, IBinder::FLAG_ONEWAY); + } +}; + +IMPLEMENT_META_INTERFACE(StreamListener, "android.hardware.IStreamListener"); + +status_t BnStreamListener::onTransact( + uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) { + switch (code) { + case QUEUE_BUFFER: + { + CHECK_INTERFACE(IStreamListener, data, reply); + size_t index = static_cast<size_t>(data.readInt32()); + size_t size = static_cast<size_t>(data.readInt32()); + + queueBuffer(index, size); + break; + } + + case QUEUE_COMMAND: + { + CHECK_INTERFACE(IStreamListener, data, reply); + Command cmd = static_cast<Command>(data.readInt32()); + + queueCommand(cmd); + break; + } + + default: + return BBinder::onTransact(code, data, reply, flags); + } + + return OK; +} + +} // namespace android diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 00e510b..63d09d6 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -278,6 +278,26 @@ sp<IMediaPlayer> MediaPlayerService::create(pid_t pid, const sp<IMediaPlayerClie return c; } +sp<IMediaPlayer> MediaPlayerService::create( + pid_t pid, const sp<IMediaPlayerClient> &client, + const sp<IStreamSource> &source, int audioSessionId) { + int32_t connId = android_atomic_inc(&mNextConnId); + sp<Client> c = new Client(this, pid, connId, client, audioSessionId); + + LOGV("Create new client(%d) from pid %d, audioSessionId=%d", + connId, pid, audioSessionId); + + if (OK != c->setDataSource(source)) { + c.clear(); + } else { + wp<Client> w = c; + Mutex::Autolock lock(mLock); + mClients.add(w); + } + + return c; +} + sp<IOMX> MediaPlayerService::getOMX() { Mutex::Autolock autoLock(mLock); @@ -864,6 +884,30 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64 return mStatus; } +status_t MediaPlayerService::Client::setDataSource( + const sp<IStreamSource> &source) { + // create the right type of player + sp<MediaPlayerBase> p = createPlayer(STAGEFRIGHT_PLAYER); + + if (p == NULL) { + return NO_INIT; + } + + if (!p->hardwareOutput()) { + mAudioOutput = new AudioOutput(mAudioSessionId); + static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput); + } + + // now set data source + mStatus = p->setDataSource(source); + + if (mStatus == OK) { + mPlayer = p; + } + + return mStatus; +} + status_t MediaPlayerService::Client::setVideoSurface(const sp<Surface>& surface) { LOGV("[%d] setVideoSurface(%p)", mConnId, surface.get()); diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 184324c..62f8ed6 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -191,6 +191,11 @@ public: const KeyedVector<String8, String8> *headers, int audioSessionId); virtual sp<IMediaPlayer> create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length, int audioSessionId); + + virtual sp<IMediaPlayer> create( + pid_t pid, const sp<IMediaPlayerClient> &client, + const sp<IStreamSource> &source, int audioSessionId); + virtual sp<IMemory> decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual sp<IMemory> decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat); virtual sp<IOMX> getOMX(); @@ -234,6 +239,9 @@ private: const KeyedVector<String8, String8> *headers); status_t setDataSource(int fd, int64_t offset, int64_t length); + + status_t setDataSource(const sp<IStreamSource> &source); + static void notify(void* cookie, int msg, int ext1, int ext2); pid_t pid() const { return mPid; } diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp index 58ef99b..da564dc 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.cpp +++ b/media/libmediaplayerservice/StagefrightPlayer.cpp @@ -44,6 +44,10 @@ status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length return mPlayer->setDataSource(dup(fd), offset, length); } +status_t StagefrightPlayer::setDataSource(const sp<IStreamSource> &source) { + return mPlayer->setDataSource(source); +} + status_t StagefrightPlayer::setVideoSurface(const sp<Surface> &surface) { LOGV("setVideoSurface"); diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h index c4a2588..fc72bfb 100644 --- a/media/libmediaplayerservice/StagefrightPlayer.h +++ b/media/libmediaplayerservice/StagefrightPlayer.h @@ -35,6 +35,9 @@ public: const char *url, const KeyedVector<String8, String8> *headers); virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + + virtual status_t setDataSource(const sp<IStreamSource> &source); + virtual status_t setVideoSurface(const sp<Surface> &surface); virtual status_t prepare(); virtual status_t prepareAsync(); diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index ec58919..a804866 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -34,13 +34,16 @@ #include "UDPPusher.h" #include <binder/IPCThreadState.h> +#include <binder/MemoryDealer.h> +#include <media/IStreamSource.h> +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/AudioPlayer.h> #include <media/stagefright/DataSource.h> #include <media/stagefright/FileSource.h> #include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaExtractor.h> -#include <media/stagefright/MediaDebug.h> #include <media/stagefright/MediaSource.h> #include <media/stagefright/MetaData.h> #include <media/stagefright/OMXCodec.h> @@ -155,6 +158,201 @@ private: const AwesomeNativeWindowRenderer &); }; +//////////////////////////////////////////////////////////////////////////////// + +struct QueueDataSource; + +struct QueueListener : public BnStreamListener { + QueueListener(QueueDataSource *owner) + : mOwner(owner) { + } + + void clearOwner(); + + virtual void queueBuffer(size_t index, size_t size); + virtual void queueCommand(Command cmd); + +private: + Mutex mLock; + + QueueDataSource *mOwner; + + DISALLOW_EVIL_CONSTRUCTORS(QueueListener); +}; + +struct QueueDataSource : public DataSource { + QueueDataSource(const sp<IStreamSource> &source); + + virtual status_t initCheck() const; + + virtual ssize_t readAt(off64_t offset, void *data, size_t size); + + virtual void queueBuffer(size_t index, size_t size); + virtual void queueCommand(IStreamListener::Command cmd); + +protected: + virtual ~QueueDataSource(); + +private: + enum { + kNumBuffers = 16 + }; + + struct BufferInfo { + size_t mIndex; + size_t mOffset; + size_t mSize; + }; + + Mutex mLock; + Condition mCondition; + + sp<IStreamSource> mSource; + sp<QueueListener> mListener; + sp<MemoryDealer> mDealer; + Vector<sp<IMemory> > mBuffers; + + List<BufferInfo> mFilledBuffers; + + off64_t mPosition; + bool mEOS; + + DISALLOW_EVIL_CONSTRUCTORS(QueueDataSource); +}; + +QueueDataSource::QueueDataSource(const sp<IStreamSource> &source) + : mSource(source), + mPosition(0), + mEOS(false) { + mListener = new QueueListener(this); + mSource->setListener(mListener); + + static const size_t kBufferSize = 8192; + + mDealer = new MemoryDealer(kNumBuffers * kBufferSize); + for (size_t i = 0; i < kNumBuffers; ++i) { + sp<IMemory> mem = mDealer->allocate(kBufferSize); + CHECK(mem != NULL); + + mBuffers.push(mem); + } + mSource->setBuffers(mBuffers); + + for (size_t i = 0; i < kNumBuffers; ++i) { + mSource->onBufferAvailable(i); + } +} + +QueueDataSource::~QueueDataSource() { + Mutex::Autolock autoLock(mLock); + + while (mFilledBuffers.size() < kNumBuffers && !mEOS) { + mCondition.wait(mLock); + } + + mListener->clearOwner(); +} + +status_t QueueDataSource::initCheck() const { + return OK; +} + +ssize_t QueueDataSource::readAt(off64_t offset, void *data, size_t size) { + if (offset != mPosition) { + return -EPIPE; + } + + Mutex::Autolock autoLock(mLock); + + size_t sizeDone = 0; + + while (sizeDone < size) { + while (mFilledBuffers.empty() && !mEOS) { + mCondition.wait(mLock); + } + + if (mFilledBuffers.empty()) { + if (sizeDone > 0) { + mPosition += sizeDone; + return sizeDone; + } + return ERROR_END_OF_STREAM; + } + + BufferInfo &info = *mFilledBuffers.begin(); + + size_t copy = size - sizeDone; + if (copy > info.mSize) { + copy = info.mSize; + } + + memcpy((uint8_t *)data + sizeDone, + (const uint8_t *)mBuffers.itemAt(info.mIndex)->pointer() + + info.mOffset, + copy); + + info.mSize -= copy; + info.mOffset += copy; + sizeDone += copy; + + if (info.mSize == 0) { + mSource->onBufferAvailable(info.mIndex); + mFilledBuffers.erase(mFilledBuffers.begin()); + } + } + + mPosition += sizeDone; + + return sizeDone; +} + +void QueueDataSource::queueBuffer(size_t index, size_t size) { + Mutex::Autolock autoLock(mLock); + + CHECK_LT(index, mBuffers.size()); + CHECK_LE(size, mBuffers.itemAt(index)->size()); + + BufferInfo info; + info.mIndex = index; + info.mSize = size; + info.mOffset = 0; + + mFilledBuffers.push_back(info); + mCondition.signal(); +} + +void QueueDataSource::queueCommand(IStreamListener::Command cmd) { + Mutex::Autolock autoLock(mLock); + + if (cmd == IStreamListener::EOS) { + mEOS = true; + mCondition.signal(); + } +} + +void QueueListener::clearOwner() { + Mutex::Autolock autoLock(mLock); + mOwner = NULL; +} + +void QueueListener::queueBuffer(size_t index, size_t size) { + Mutex::Autolock autoLock(mLock); + if (mOwner == NULL) { + return; + } + mOwner->queueBuffer(index, size); +} + +void QueueListener::queueCommand(Command cmd) { + Mutex::Autolock autoLock(mLock); + if (mOwner == NULL) { + return; + } + mOwner->queueCommand(cmd); +} + +//////////////////////////////////////////////////////////////////////////////// + AwesomePlayer::AwesomePlayer() : mQueueStarted(false), mTimeSource(NULL), @@ -164,7 +362,7 @@ AwesomePlayer::AwesomePlayer() mExtractorFlags(0), mVideoBuffer(NULL), mDecryptHandle(NULL) { - CHECK_EQ(mClient.connect(), OK); + CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -264,6 +462,26 @@ status_t AwesomePlayer::setDataSource( return setDataSource_l(dataSource); } +status_t AwesomePlayer::setDataSource(const sp<IStreamSource> &source) { + Mutex::Autolock autoLock(mLock); + + reset_l(); + + sp<DataSource> dataSource = new QueueDataSource(source); + +#if 0 + sp<MediaExtractor> extractor = + MediaExtractor::Create(dataSource, MEDIA_MIMETYPE_CONTAINER_MPEG2TS); + + return setDataSource_l(extractor); +#else + sp<NuCachedSource2> cached = new NuCachedSource2(dataSource); + dataSource = cached; + + return setDataSource_l(dataSource); +#endif +} + status_t AwesomePlayer::setDataSource_l( const sp<DataSource> &dataSource) { sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); @@ -619,7 +837,8 @@ void AwesomePlayer::partial_reset_l() { IPCThreadState::self()->flushCommands(); } - CHECK_EQ(OK, initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData)); + CHECK_EQ((status_t)OK, + initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData)); } void AwesomePlayer::onStreamDone() { @@ -1171,7 +1390,7 @@ void AwesomePlayer::onVideoEvent() { options.clearSeekTo(); if (err != OK) { - CHECK_EQ(mVideoBuffer, NULL); + CHECK(mVideoBuffer == NULL); if (err == INFO_FORMAT_CHANGED) { LOGV("VideoSource signalled format change."); diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index e33f467..46f4a35 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -67,6 +67,8 @@ struct AwesomePlayer { status_t setDataSource(int fd, int64_t offset, int64_t length); + status_t setDataSource(const sp<IStreamSource> &source); + void reset(); status_t prepare(); diff --git a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml index 5fa8b3b..fb4cf3f 100644 --- a/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml +++ b/packages/SystemUI/res/layout-xlarge/sysbar_panel_notifications.xml @@ -20,9 +20,8 @@ android:layout_height="match_parent" android:layout_width="match_parent" android:animateLayoutChanges="true" - android:background="@drawable/bg_scrim_notification" android:paddingTop="32dp" - android:paddingBottom="32dp" + android:paddingBottom="@dimen/status_bar_panel_bottom_offset" android:orientation="vertical" android:gravity="right" > @@ -66,6 +65,7 @@ android:layout_height="wrap_content" android:layout_width="match_parent" android:layout_below="@id/clock" + android:layout_marginTop="4dp" android:layout_marginRight="48dp" android:gravity="right" /> @@ -76,9 +76,10 @@ android:layout_width="wrap_content" android:layout_alignParentLeft="true" android:layout_below="@id/date" - android:layout_marginTop="16dp" android:layout_marginLeft="48dp" - android:baseline="17dp" + android:layout_marginTop="18dp" + android:layout_marginRight="8dp" + android:baseline="15dp" /> <TextView @@ -97,7 +98,8 @@ android:layout_width="wrap_content" android:layout_toRightOf="@id/battery_text" android:layout_alignBaseline="@id/battery" - android:baseline="21dp" + android:layout_marginRight="8dp" + android:baseline="15dp" /> <TextView @@ -114,11 +116,11 @@ android:id="@+id/settings_button" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_alignParentRight="true" android:layout_alignBaseline="@id/battery" + android:layout_alignParentRight="true" android:paddingRight="16dp" android:src="@drawable/ic_notification_open" - android:baseline="17dp" + android:baseline="21dp" /> <ImageView @@ -130,7 +132,7 @@ android:paddingRight="16dp" android:visibility="invisible" android:src="@drawable/status_bar_veto" - android:baseline="17dp" + android:baseline="21dp" /> </com.android.systemui.statusbar.tablet.NotificationTitleArea> diff --git a/packages/SystemUI/res/values-xlarge/dimens.xml b/packages/SystemUI/res/values-xlarge/dimens.xml index 009b7a8..5ae3982 100644 --- a/packages/SystemUI/res/values-xlarge/dimens.xml +++ b/packages/SystemUI/res/values-xlarge/dimens.xml @@ -22,6 +22,8 @@ <dimen name="notification_large_icon_height">60dp</dimen> <!-- The width of the ticker, including the icon --> <dimen name="notification_ticker_width">360dp</dimen> + <!-- Status bar panel bottom offset (height of status bar - overlap) --> + <dimen name="status_bar_panel_bottom_offset">36dp</dimen> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index 2dad81c..915fa2f 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -294,7 +294,7 @@ public class PhoneStatusBar extends StatusBar { WindowManager.LayoutParams lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL @@ -1254,7 +1254,7 @@ public class PhoneStatusBar extends StatusBar { lp = new WindowManager.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT, - WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, + WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, @@ -1282,7 +1282,7 @@ public class PhoneStatusBar extends StatusBar { lp.height = getExpandedHeight(); lp.x = 0; mTrackingPosition = lp.y = -disph; // sufficiently large negative - lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; + lp.type = WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; lp.flags = WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java index 7f743b2..b05fe1a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletTicker.java @@ -163,6 +163,7 @@ public class TabletTicker extends Handler { WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE + | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL, PixelFormat.TRANSLUCENT); lp.gravity = Gravity.BOTTOM | Gravity.RIGHT; diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index 1c1a46e..f892e9e 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -103,6 +103,7 @@ import static android.view.WindowManager.LayoutParams.TYPE_SEARCH_BAR; import static android.view.WindowManager.LayoutParams.TYPE_SECURE_SYSTEM_OVERLAY; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR; import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_PANEL; +import static android.view.WindowManager.LayoutParams.TYPE_STATUS_BAR_SUB_PANEL; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; import static android.view.WindowManager.LayoutParams.TYPE_SYSTEM_ERROR; @@ -148,31 +149,32 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final int APPLICATION_LAYER = 2; static final int PHONE_LAYER = 3; static final int SEARCH_BAR_LAYER = 4; - static final int STATUS_BAR_PANEL_LAYER = 5; + static final int STATUS_BAR_SUB_PANEL_LAYER = 5; static final int SYSTEM_DIALOG_LAYER = 6; // toasts and the plugged-in battery thing static final int TOAST_LAYER = 7; static final int STATUS_BAR_LAYER = 8; + static final int STATUS_BAR_PANEL_LAYER = 9; // SIM errors and unlock. Not sure if this really should be in a high layer. - static final int PRIORITY_PHONE_LAYER = 9; + static final int PRIORITY_PHONE_LAYER = 10; // like the ANR / app crashed dialogs - static final int SYSTEM_ALERT_LAYER = 10; + static final int SYSTEM_ALERT_LAYER = 11; // system-level error dialogs - static final int SYSTEM_ERROR_LAYER = 11; + static final int SYSTEM_ERROR_LAYER = 12; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_LAYER = 12; + static final int INPUT_METHOD_LAYER = 13; // on-screen keyboards and other such input method user interfaces go here. - static final int INPUT_METHOD_DIALOG_LAYER = 13; + static final int INPUT_METHOD_DIALOG_LAYER = 14; // the keyguard; nothing on top of these can take focus, since they are // responsible for power management when displayed. - static final int KEYGUARD_LAYER = 14; - static final int KEYGUARD_DIALOG_LAYER = 15; + static final int KEYGUARD_LAYER = 15; + static final int KEYGUARD_DIALOG_LAYER = 16; // the drag layer: input for drag-and-drop is associated with this window, // which sits above all other focusable windows - static final int DRAG_LAYER = 16; + static final int DRAG_LAYER = 17; // things in here CAN NOT take focus, but are shown on top of everything else. - static final int SYSTEM_OVERLAY_LAYER = 17; - static final int SECURE_SYSTEM_OVERLAY_LAYER = 18; + static final int SYSTEM_OVERLAY_LAYER = 18; + static final int SECURE_SYSTEM_OVERLAY_LAYER = 19; static final int APPLICATION_MEDIA_SUBLAYER = -2; static final int APPLICATION_MEDIA_OVERLAY_SUBLAYER = -1; @@ -239,6 +241,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { int mUserRotationMode = WindowManagerPolicy.USER_ROTATION_FREE; int mUserRotation = Surface.ROTATION_0; + boolean mAllowAllRotations; boolean mCarDockEnablesAccelerometer; boolean mDeskDockEnablesAccelerometer; int mLidKeyboardAccessibility; @@ -274,10 +277,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + // The current size of the screen; really; (ir)regardless of whether the status + // bar can be hidden or not + int mUnrestrictedScreenLeft, mUnrestrictedScreenTop; + int mUnrestrictedScreenWidth, mUnrestrictedScreenHeight; // The current size of the screen; these may be different than (0,0)-(dw,dh) // if the status bar can't be hidden; in that case it effectively carves out // that area of the display from all other windows. - int mScreenLeft, mScreenTop, mScreenWidth, mScreenHeight; + int mRestrictedScreenLeft, mRestrictedScreenTop; + int mRestrictedScreenWidth, mRestrictedScreenHeight; // During layout, the current screen borders with all outer decoration // (status bar, input method dock) accounted for. int mCurLeft, mCurTop, mCurRight, mCurBottom; @@ -650,6 +658,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { com.android.internal.R.integer.config_carDockRotation); mDeskDockRotation = readRotation( com.android.internal.R.integer.config_deskDockRotation); + mAllowAllRotations = mContext.getResources().getBoolean( + com.android.internal.R.bool.config_allowAllRotations); mCarDockEnablesAccelerometer = mContext.getResources().getBoolean( com.android.internal.R.bool.config_carDockEnablesAccelerometer); mDeskDockEnablesAccelerometer = mContext.getResources().getBoolean( @@ -906,6 +916,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { return STATUS_BAR_LAYER; case TYPE_STATUS_BAR_PANEL: return STATUS_BAR_PANEL_LAYER; + case TYPE_STATUS_BAR_SUB_PANEL: + return STATUS_BAR_SUB_PANEL_LAYER; case TYPE_SYSTEM_DIALOG: return SYSTEM_DIALOG_LAYER; case TYPE_SEARCH_BAR: @@ -1126,6 +1138,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { "PhoneWindowManager"); mStatusBarPanels.add(win); break; + case TYPE_STATUS_BAR_SUB_PANEL: + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.STATUS_BAR_SERVICE, + "PhoneWindowManager"); + mStatusBarPanels.add(win); + break; case TYPE_KEYGUARD: if (mKeyguard != null) { return WindowManagerImpl.ADD_MULTIPLE_SINGLETON; @@ -1401,8 +1419,8 @@ public class PhoneWindowManager implements WindowManagerPolicy { if ((fl & (FLAG_LAYOUT_IN_SCREEN | FLAG_FULLSCREEN | FLAG_LAYOUT_INSET_DECOR)) == (FLAG_LAYOUT_IN_SCREEN | FLAG_LAYOUT_INSET_DECOR)) { contentInset.set(mCurLeft, mCurTop, - (mScreenLeft+mScreenWidth) - mCurRight, - (mScreenTop+mScreenHeight) - mCurBottom); + (mRestrictedScreenLeft+mRestrictedScreenWidth) - mCurRight, + (mRestrictedScreenTop+mRestrictedScreenHeight) - mCurBottom); } else { contentInset.setEmpty(); } @@ -1410,9 +1428,12 @@ public class PhoneWindowManager implements WindowManagerPolicy { /** {@inheritDoc} */ public void beginLayoutLw(int displayWidth, int displayHeight) { - mScreenLeft = mScreenTop = 0; - mScreenWidth = displayWidth; - mScreenHeight = displayHeight; + mUnrestrictedScreenLeft = mUnrestrictedScreenTop = 0; + mUnrestrictedScreenWidth = displayWidth; + mUnrestrictedScreenHeight = displayHeight; + mRestrictedScreenLeft = mRestrictedScreenTop = 0; + mRestrictedScreenWidth = displayWidth; + mRestrictedScreenHeight = displayHeight; mDockLeft = mContentLeft = mCurLeft = 0; mDockTop = mContentTop = mCurTop = 0; mDockRight = mContentRight = mCurRight = displayWidth; @@ -1451,16 +1472,18 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else { // Status bar can't go away; the part of the screen it // covers does not exist for anything behind it. - if (mScreenTop == r.top) { - mScreenTop = r.bottom; - mScreenHeight -= (r.bottom-r.top); - } else if ((mScreenHeight-mScreenTop) == r.bottom) { - mScreenHeight -= (r.bottom-r.top); + if (mRestrictedScreenTop == r.top) { + mRestrictedScreenTop = r.bottom; + mRestrictedScreenHeight -= (r.bottom-r.top); + } else if ((mRestrictedScreenHeight-mRestrictedScreenTop) == r.bottom) { + mRestrictedScreenHeight -= (r.bottom-r.top); } - mContentTop = mCurTop = mDockTop = mScreenTop; - mContentBottom = mCurBottom = mDockBottom = mScreenTop+mScreenHeight; - if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mScreenTop=" + mScreenTop - + " mScreenHeight=" + mScreenHeight); + mContentTop = mCurTop = mDockTop = mRestrictedScreenTop; + mContentBottom = mCurBottom = mDockBottom + = mRestrictedScreenTop + mRestrictedScreenHeight; + if (DEBUG_LAYOUT) Log.v(TAG, "Status bar: mRestrictedScreenTop=" + + mRestrictedScreenTop + + " mRestrictedScreenHeight=" + mRestrictedScreenHeight); } } } @@ -1552,10 +1575,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { // frame is the same as the one we are attached to. setAttachedWindowFrames(win, fl, sim, attached, true, pf, df, cf, vf); } else { - pf.left = df.left = mScreenLeft; - pf.top = df.top = mScreenTop; - pf.right = df.right = mScreenLeft+mScreenWidth; - pf.bottom = df.bottom = mScreenTop+mScreenHeight; + if (attrs.type == TYPE_STATUS_BAR_PANEL) { + // Status bar panels are the only windows who can go on top of + // the status bar. They are protected by the STATUS_BAR_SERVICE + // permission, so they have the same privileges as the status + // bar itself. + pf.left = df.left = mUnrestrictedScreenLeft; + pf.top = df.top = mUnrestrictedScreenTop; + pf.right = df.right = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; + pf.bottom = df.bottom = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + } else { + pf.left = df.left = mRestrictedScreenLeft; + pf.top = df.top = mRestrictedScreenTop; + pf.right = df.right = mRestrictedScreenLeft+mRestrictedScreenWidth; + pf.bottom = df.bottom = mRestrictedScreenTop+mRestrictedScreenHeight; + } if (adjust != SOFT_INPUT_ADJUST_RESIZE) { cf.left = mDockLeft; cf.top = mDockTop; @@ -1579,10 +1613,20 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if ((fl & FLAG_LAYOUT_IN_SCREEN) != 0) { // A window that has requested to fill the entire screen just // gets everything, period. - pf.left = df.left = cf.left = mScreenLeft; - pf.top = df.top = cf.top = mScreenTop; - pf.right = df.right = cf.right = mScreenLeft+mScreenWidth; - pf.bottom = df.bottom = cf.bottom = mScreenTop+mScreenHeight; + if (attrs.type == TYPE_STATUS_BAR_PANEL) { + pf.left = df.left = cf.left = mUnrestrictedScreenLeft; + pf.top = df.top = cf.top = mUnrestrictedScreenTop; + pf.right = df.right = cf.right + = mUnrestrictedScreenLeft+mUnrestrictedScreenWidth; + pf.bottom = df.bottom = cf.bottom + = mUnrestrictedScreenTop+mUnrestrictedScreenHeight; + } else { + pf.left = df.left = cf.left = mRestrictedScreenLeft; + pf.top = df.top = cf.top = mRestrictedScreenTop; + pf.right = df.right = cf.right = mRestrictedScreenLeft+mRestrictedScreenWidth; + pf.bottom = df.bottom = cf.bottom + = mRestrictedScreenTop+mRestrictedScreenHeight; + } if (adjust != SOFT_INPUT_ADJUST_NOTHING) { vf.left = mCurLeft; vf.top = mCurTop; @@ -1841,9 +1885,10 @@ public class PhoneWindowManager implements WindowManagerPolicy { rect.union(w.getShownFrameLw()); } } - final int insetw = mScreenWidth/10; - final int inseth = mScreenHeight/10; - if (rect.contains(insetw, inseth, mScreenWidth-insetw, mScreenHeight-inseth)) { + final int insetw = mRestrictedScreenWidth/10; + final int inseth = mRestrictedScreenHeight/10; + if (rect.contains(insetw, inseth, mRestrictedScreenWidth-insetw, + mRestrictedScreenHeight-inseth)) { // All of the status bar windows put together cover the // screen, so the app can't be seen. (Note this test doesn't // work if the rects of these windows are at off offsets or @@ -2302,7 +2347,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { return getCurrentPortraitRotation(lastRotation); } - mOrientationListener.setAllow180Rotation( + mOrientationListener.setAllow180Rotation(mAllowAllRotations || orientation == ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR); // case for nosensor meaning ignore sensor and consider only lid diff --git a/services/java/com/android/server/ScreenRotationAnimation.java b/services/java/com/android/server/ScreenRotationAnimation.java new file mode 100644 index 0000000..299567a --- /dev/null +++ b/services/java/com/android/server/ScreenRotationAnimation.java @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server; // TODO: use com.android.server.wm, once things move there + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Matrix; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.Rect; +import android.util.DisplayMetrics; +import android.util.Slog; +import android.view.Display; +import android.view.Surface; +import android.view.SurfaceSession; + +class ScreenRotationAnimation { + private static final String TAG = "ScreenRotationAnimation"; + + Surface mSurface; + int mWidth, mHeight; + + int mBaseRotation; + int mCurRotation; + int mDeltaRotation; + + final Matrix mMatrix = new Matrix(); + final float[] mTmpFloats = new float[9]; + + public ScreenRotationAnimation(Display display, SurfaceSession session) { + final DisplayMetrics dm = new DisplayMetrics(); + display.getMetrics(dm); + + Bitmap screenshot = Surface.screenshot(0, 0); + + if (screenshot != null) { + // Screenshot does NOT include rotation! + mBaseRotation = 0; + mWidth = screenshot.getWidth(); + mHeight = screenshot.getHeight(); + } else { + // Just in case. + mBaseRotation = display.getRotation(); + mWidth = dm.widthPixels; + mHeight = dm.heightPixels; + } + + Surface.openTransaction(); + if (mSurface != null) { + mSurface.destroy(); + mSurface = null; + } + try { + mSurface = new Surface(session, 0, "FreezeSurface", + -1, mWidth, mHeight, PixelFormat.OPAQUE, 0); + } catch (Surface.OutOfResourcesException e) { + Slog.w(TAG, "Unable to allocate freeze surface", e); + } + mSurface.setLayer(WindowManagerService.TYPE_LAYER_MULTIPLIER * 200); + setRotation(display.getRotation()); + + Rect dirty = new Rect(0, 0, mWidth, mHeight); + Canvas c = null; + try { + c = mSurface.lockCanvas(dirty); + } catch (IllegalArgumentException e) { + Slog.w(TAG, "Unable to lock surface", e); + return; + } catch (Surface.OutOfResourcesException e) { + Slog.w(TAG, "Unable to lock surface", e); + return; + } + if (c == null) { + Slog.w(TAG, "Null surface"); + return; + } + + if (screenshot != null) { + c.drawBitmap(screenshot, 0, 0, new Paint(0)); + } else { + c.drawColor(Color.GREEN); + } + + mSurface.unlockCanvasAndPost(c); + Surface.closeTransaction(); + + screenshot.recycle(); + } + + // Must be called while in a transaction. + public void setRotation(int rotation) { + mCurRotation = rotation; + int delta = mCurRotation - mBaseRotation; + if (delta < 0) delta += 4; + mDeltaRotation = delta; + + switch (delta) { + case Surface.ROTATION_0: + mMatrix.reset(); + break; + case Surface.ROTATION_90: + mMatrix.setRotate(90, 0, 0); + mMatrix.postTranslate(0, mWidth); + break; + case Surface.ROTATION_180: + mMatrix.setRotate(180, 0, 0); + mMatrix.postTranslate(mWidth, mHeight); + break; + case Surface.ROTATION_270: + mMatrix.setRotate(270, 0, 0); + mMatrix.postTranslate(mHeight, 0); + break; + } + + mMatrix.getValues(mTmpFloats); + mSurface.setPosition((int)mTmpFloats[Matrix.MTRANS_X], + (int)mTmpFloats[Matrix.MTRANS_Y]); + mSurface.setMatrix( + mTmpFloats[Matrix.MSCALE_X], mTmpFloats[Matrix.MSKEW_X], + mTmpFloats[Matrix.MSKEW_Y], mTmpFloats[Matrix.MSCALE_Y]); + + if (false) { + float[] srcPnts = new float[] { 0, 0, mWidth, mHeight }; + float[] dstPnts = new float[8]; + mMatrix.mapPoints(dstPnts, srcPnts); + Slog.i(TAG, "**** ROTATION: " + delta); + Slog.i(TAG, "Original : (" + srcPnts[0] + "," + srcPnts[1] + + ")-(" + srcPnts[2] + "," + srcPnts[3] + ")"); + Slog.i(TAG, "Transformed: (" + dstPnts[0] + "," + dstPnts[1] + + ")-(" + dstPnts[2] + "," + dstPnts[3] + ")"); + } + } + + public void dismiss() { + mSurface.destroy(); + } +} diff --git a/services/java/com/android/server/WindowManagerService.java b/services/java/com/android/server/WindowManagerService.java index b3ea836..5c32c38 100644 --- a/services/java/com/android/server/WindowManagerService.java +++ b/services/java/com/android/server/WindowManagerService.java @@ -201,6 +201,12 @@ public class WindowManagerService extends IWindowManager.Stub */ static final int DEFAULT_FADE_IN_OUT_DURATION = 400; + /** + * If true, the window manager will do its own custom freezing and general + * management of the screen during rotation. + */ + static final boolean CUSTOM_SCREEN_ROTATION = true; + // Maximum number of milliseconds to wait for input event injection. // FIXME is this value reasonable? private static final int INJECTION_TIMEOUT_MILLIS = 30 * 1000; @@ -374,6 +380,7 @@ public class WindowManagerService extends IWindowManager.Stub boolean mBlurShown; Watermark mWatermark; StrictModeFlash mStrictModeFlash; + ScreenRotationAnimation mScreenRotationAnimation; int mTransactionSequence = 0; @@ -4998,7 +5005,18 @@ public class WindowManagerService extends IWindowManager.Stub Slog.i(TAG, "Setting rotation to " + rotation + ", animFlags=" + animFlags); mInputManager.setDisplayOrientation(0, rotation); if (mDisplayEnabled) { - Surface.setOrientation(0, rotation, animFlags); + if (CUSTOM_SCREEN_ROTATION) { + Surface.freezeDisplay(0); + Surface.openTransaction(); + if (mScreenRotationAnimation != null) { + mScreenRotationAnimation.setRotation(rotation); + } + Surface.closeTransaction(); + Surface.setOrientation(0, rotation, animFlags); + Surface.unfreezeDisplay(0); + } else { + Surface.setOrientation(0, rotation, animFlags); + } } for (int i=mWindows.size()-1; i>=0; i--) { WindowState w = mWindows.get(i); @@ -10817,7 +10835,14 @@ public class WindowManagerService extends IWindowManager.Stub File file = new File("/data/system/frozen"); Debug.startMethodTracing(file.toString(), 8 * 1024 * 1024); } - Surface.freezeDisplay(0); + + if (CUSTOM_SCREEN_ROTATION) { + if (mScreenRotationAnimation == null) { + mScreenRotationAnimation = new ScreenRotationAnimation(mDisplay, mFxSession); + } + } else { + Surface.freezeDisplay(0); + } } private void stopFreezingDisplayLocked() { @@ -10834,7 +10859,15 @@ public class WindowManagerService extends IWindowManager.Stub if (PROFILE_ORIENTATION) { Debug.stopMethodTracing(); } - Surface.unfreezeDisplay(0); + + if (CUSTOM_SCREEN_ROTATION) { + if (mScreenRotationAnimation != null) { + mScreenRotationAnimation.dismiss(); + mScreenRotationAnimation = null; + } + } else { + Surface.unfreezeDisplay(0); + } mInputMonitor.thawInputDispatchingLw(); |
