diff options
Diffstat (limited to 'services')
24 files changed, 1817 insertions, 480 deletions
diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index 5c3b43fc..dca795c 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -26,11 +26,15 @@ #include "AudioResamplerSinc.h" #include "AudioResamplerCubic.h" +#ifdef __arm__ +#include <machine/cpu-features.h> +#endif + namespace android { -#ifdef __ARM_ARCH_5E__ // optimized asm option +#ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 -#endif // __ARM_ARCH_5E__ +#endif // __ARM_HAVE_HALFWORD_MULTIPLY // ---------------------------------------------------------------------------- class AudioResamplerOrder1 : public AudioResampler { diff --git a/services/input/Android.mk b/services/input/Android.mk index 96431bc..f9f8623f 100644 --- a/services/input/Android.mk +++ b/services/input/Android.mk @@ -22,7 +22,9 @@ LOCAL_SRC_FILES:= \ InputManager.cpp \ InputReader.cpp \ InputWindow.cpp \ - PointerController.cpp + PointerController.cpp \ + SpotController.cpp \ + SpriteController.cpp LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/services/input/InputReader.h b/services/input/InputReader.h index cf9b13d..9ed1391 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -20,6 +20,7 @@ #include "EventHub.h" #include "InputDispatcher.h" #include "PointerController.h" +#include "SpotController.h" #include <ui/Input.h> #include <ui/DisplayInfo.h> @@ -89,6 +90,9 @@ public: /* Gets a pointer controller associated with the specified cursor device (ie. a mouse). */ virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) = 0; + + /* Gets a spot controller associated with the specified touch pad device. */ + virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId) = 0; }; diff --git a/services/input/PointerController.cpp b/services/input/PointerController.cpp index a4ee295..15effb7 100644 --- a/services/input/PointerController.cpp +++ b/services/input/PointerController.cpp @@ -49,8 +49,11 @@ static const nsecs_t FADE_FRAME_INTERVAL = 1000000000LL / 60; static const float FADE_DECAY_PER_FRAME = float(FADE_FRAME_INTERVAL) / FADE_DURATION; -PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLayer) : - mLooper(looper), mPointerLayer(pointerLayer) { +PointerController::PointerController(const sp<Looper>& looper, + const sp<SpriteController>& spriteController) : + mLooper(looper), mSpriteController(spriteController) { + mHandler = new WeakMessageHandler(this); + AutoMutex _l(mLock); mLocked.displayWidth = -1; @@ -61,34 +64,20 @@ PointerController::PointerController(const sp<Looper>& looper, int32_t pointerLa mLocked.pointerY = 0; mLocked.buttonState = 0; - mLocked.iconBitmap = NULL; - mLocked.iconHotSpotX = 0; - mLocked.iconHotSpotY = 0; - mLocked.fadeAlpha = 1; mLocked.inactivityFadeDelay = INACTIVITY_FADE_DELAY_NORMAL; - mLocked.wantVisible = false; mLocked.visible = false; - mLocked.drawn = false; - mHandler = new WeakMessageHandler(this); + mLocked.sprite = mSpriteController->createSprite(); } PointerController::~PointerController() { mLooper->removeMessages(mHandler); - if (mSurfaceControl != NULL) { - mSurfaceControl->clear(); - mSurfaceControl.clear(); - } - - if (mSurfaceComposerClient != NULL) { - mSurfaceComposerClient->dispose(); - mSurfaceComposerClient.clear(); - } + AutoMutex _l(mLock); - delete mLocked.iconBitmap; + mLocked.sprite.clear(); } bool PointerController::getBounds(float* outMinX, float* outMinY, @@ -214,75 +203,11 @@ void PointerController::setInactivityFadeDelay(InactivityFadeDelay inactivityFad } void PointerController::updateLocked() { - bool wantVisibleAndHavePointerIcon = mLocked.wantVisible && mLocked.iconBitmap; - - if (wantVisibleAndHavePointerIcon) { - // Want the pointer to be visible. - // Ensure the surface is created and drawn. - if (!createSurfaceIfNeededLocked() || !drawPointerIfNeededLocked()) { - return; - } - } else { - // Don't want the pointer to be visible. - // If it is not visible then we are done. - if (mSurfaceControl == NULL || !mLocked.visible) { - return; - } - } - - status_t status = mSurfaceComposerClient->openTransaction(); - if (status) { - LOGE("Error opening surface transaction to update pointer surface."); - return; - } - - if (wantVisibleAndHavePointerIcon) { - status = mSurfaceControl->setPosition( - mLocked.pointerX - mLocked.iconHotSpotX, - mLocked.pointerY - mLocked.iconHotSpotY); - if (status) { - LOGE("Error %d moving pointer surface.", status); - goto CloseTransaction; - } - - status = mSurfaceControl->setAlpha(mLocked.fadeAlpha); - if (status) { - LOGE("Error %d setting pointer surface alpha.", status); - goto CloseTransaction; - } - - if (!mLocked.visible) { - status = mSurfaceControl->setLayer(mPointerLayer); - if (status) { - LOGE("Error %d setting pointer surface layer.", status); - goto CloseTransaction; - } - - status = mSurfaceControl->show(mPointerLayer); - if (status) { - LOGE("Error %d showing pointer surface.", status); - goto CloseTransaction; - } - - mLocked.visible = true; - } - } else { - if (mLocked.visible) { - status = mSurfaceControl->hide(); - if (status) { - LOGE("Error %d hiding pointer surface.", status); - goto CloseTransaction; - } - - mLocked.visible = false; - } - } - -CloseTransaction: - status = mSurfaceComposerClient->closeTransaction(); - if (status) { - LOGE("Error closing surface transaction to update pointer surface."); - } + mLocked.sprite->openTransaction(); + mLocked.sprite->setPosition(mLocked.pointerX, mLocked.pointerY); + mLocked.sprite->setAlpha(mLocked.fadeAlpha); + mLocked.sprite->setVisible(mLocked.visible); + mLocked.sprite->closeTransaction(); } void PointerController::setDisplaySize(int32_t width, int32_t height) { @@ -339,7 +264,7 @@ void PointerController::setDisplayOrientation(int32_t orientation) { case DISPLAY_ORIENTATION_90: temp = x; x = y; - y = mLocked.displayWidth - x; + y = mLocked.displayWidth - temp; break; case DISPLAY_ORIENTATION_180: x = mLocked.displayWidth - x; @@ -365,106 +290,7 @@ void PointerController::setDisplayOrientation(int32_t orientation) { void PointerController::setPointerIcon(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) { AutoMutex _l(mLock); - if (mLocked.iconBitmap) { - delete mLocked.iconBitmap; - mLocked.iconBitmap = NULL; - } - - if (bitmap) { - mLocked.iconBitmap = new SkBitmap(); - bitmap->copyTo(mLocked.iconBitmap, SkBitmap::kARGB_8888_Config); - } - - mLocked.iconHotSpotX = hotSpotX; - mLocked.iconHotSpotY = hotSpotY; - mLocked.drawn = false; -} - -bool PointerController::createSurfaceIfNeededLocked() { - if (!mLocked.iconBitmap) { - // If we don't have a pointer icon, then no point allocating a surface now. - return false; - } - - if (mSurfaceComposerClient == NULL) { - mSurfaceComposerClient = new SurfaceComposerClient(); - } - - if (mSurfaceControl == NULL) { - mSurfaceControl = mSurfaceComposerClient->createSurface(getpid(), - String8("Pointer Icon"), 0, - mLocked.iconBitmap->width(), mLocked.iconBitmap->height(), - PIXEL_FORMAT_RGBA_8888); - if (mSurfaceControl == NULL) { - LOGE("Error creating pointer surface."); - return false; - } - } - return true; -} - -bool PointerController::drawPointerIfNeededLocked() { - if (!mLocked.drawn) { - if (!mLocked.iconBitmap) { - return false; - } - - if (!resizeSurfaceLocked(mLocked.iconBitmap->width(), mLocked.iconBitmap->height())) { - return false; - } - - sp<Surface> surface = mSurfaceControl->getSurface(); - - Surface::SurfaceInfo surfaceInfo; - status_t status = surface->lock(&surfaceInfo); - if (status) { - LOGE("Error %d locking pointer surface before drawing.", status); - return false; - } - - SkBitmap surfaceBitmap; - ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format); - surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, surfaceInfo.w, surfaceInfo.h, bpr); - surfaceBitmap.setPixels(surfaceInfo.bits); - - SkCanvas surfaceCanvas; - surfaceCanvas.setBitmapDevice(surfaceBitmap); - - SkPaint paint; - paint.setXfermodeMode(SkXfermode::kSrc_Mode); - surfaceCanvas.drawBitmap(*mLocked.iconBitmap, 0, 0, &paint); - - status = surface->unlockAndPost(); - if (status) { - LOGE("Error %d unlocking pointer surface after drawing.", status); - return false; - } - } - - mLocked.drawn = true; - return true; -} - -bool PointerController::resizeSurfaceLocked(int32_t width, int32_t height) { - status_t status = mSurfaceComposerClient->openTransaction(); - if (status) { - LOGE("Error opening surface transaction to resize pointer surface."); - return false; - } - - status = mSurfaceControl->setSize(width, height); - if (status) { - LOGE("Error %d setting pointer surface size.", status); - return false; - } - - status = mSurfaceComposerClient->closeTransaction(); - if (status) { - LOGE("Error closing surface transaction to resize pointer surface."); - return false; - } - - return true; + mLocked.sprite->setBitmap(bitmap, hotSpotX, hotSpotY); } void PointerController::handleMessage(const Message& message) { @@ -481,7 +307,7 @@ bool PointerController::unfadeBeforeUpdateLocked() { sendFadeStepMessageDelayedLocked(getInactivityFadeDelayTimeLocked()); if (isFadingLocked()) { - mLocked.wantVisible = true; + mLocked.visible = true; mLocked.fadeAlpha = 1; return true; // update required to effect the unfade } @@ -501,11 +327,11 @@ void PointerController::startInactivityFadeDelayLocked() { } void PointerController::fadeStepLocked() { - if (mLocked.wantVisible) { + if (mLocked.visible) { mLocked.fadeAlpha -= FADE_DECAY_PER_FRAME; if (mLocked.fadeAlpha < 0) { mLocked.fadeAlpha = 0; - mLocked.wantVisible = false; + mLocked.visible = false; } else { sendFadeStepMessageDelayedLocked(FADE_FRAME_INTERVAL); } @@ -514,7 +340,7 @@ void PointerController::fadeStepLocked() { } bool PointerController::isFadingLocked() { - return !mLocked.wantVisible || mLocked.fadeAlpha != 1; + return !mLocked.visible || mLocked.fadeAlpha != 1; } nsecs_t PointerController::getInactivityFadeDelayTimeLocked() { diff --git a/services/input/PointerController.h b/services/input/PointerController.h index e1dab5c..d467a5a 100644 --- a/services/input/PointerController.h +++ b/services/input/PointerController.h @@ -17,16 +17,14 @@ #ifndef _UI_POINTER_CONTROLLER_H #define _UI_POINTER_CONTROLLER_H +#include "SpriteController.h" + #include <ui/DisplayInfo.h> #include <ui/Input.h> #include <utils/RefBase.h> #include <utils/Looper.h> #include <utils/String8.h> -#include <surfaceflinger/Surface.h> -#include <surfaceflinger/SurfaceComposerClient.h> -#include <surfaceflinger/ISurfaceComposer.h> - #include <SkBitmap.h> namespace android { @@ -86,7 +84,7 @@ public: INACTIVITY_FADE_DELAY_SHORT = 1, }; - PointerController(const sp<Looper>& looper, int32_t pointerLayer); + PointerController(const sp<Looper>& looper, const sp<SpriteController>& spriteController); virtual bool getBounds(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; @@ -111,9 +109,8 @@ private: mutable Mutex mLock; sp<Looper> mLooper; - int32_t mPointerLayer; - sp<SurfaceComposerClient> mSurfaceComposerClient; - sp<SurfaceControl> mSurfaceControl; + sp<SpriteController> mSpriteController; + sp<WeakMessageHandler> mHandler; struct Locked { int32_t displayWidth; @@ -124,26 +121,17 @@ private: float pointerY; uint32_t buttonState; - SkBitmap* iconBitmap; - float iconHotSpotX; - float iconHotSpotY; - float fadeAlpha; InactivityFadeDelay inactivityFadeDelay; - bool wantVisible; bool visible; - bool drawn; - } mLocked; - sp<WeakMessageHandler> mHandler; + sp<Sprite> sprite; + } mLocked; bool getBoundsLocked(float* outMinX, float* outMinY, float* outMaxX, float* outMaxY) const; void setPositionLocked(float x, float y); void updateLocked(); - bool createSurfaceIfNeededLocked(); - bool drawPointerIfNeededLocked(); - bool resizeSurfaceLocked(int32_t width, int32_t height); void handleMessage(const Message& message); bool unfadeBeforeUpdateLocked(); diff --git a/services/input/SpotController.cpp b/services/input/SpotController.cpp new file mode 100644 index 0000000..dffad81 --- /dev/null +++ b/services/input/SpotController.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2011 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_TAG "SpotController" + +//#define LOG_NDEBUG 0 + +// Log debug messages about spot updates +#define DEBUG_SPOT_UPDATES 0 + +#include "SpotController.h" + +#include <cutils/log.h> + +namespace android { + +// --- SpotController --- + +SpotController::SpotController(const sp<Looper>& looper, + const sp<SpriteController>& spriteController) : + mLooper(looper), mSpriteController(spriteController) { + mHandler = new WeakMessageHandler(this); +} + +SpotController::~SpotController() { + mLooper->removeMessages(mHandler); +} + +void SpotController:: handleMessage(const Message& message) { +} + +} // namespace android diff --git a/services/input/SpotController.h b/services/input/SpotController.h new file mode 100644 index 0000000..1d091d7 --- /dev/null +++ b/services/input/SpotController.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2011 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 _UI_SPOT_CONTROLLER_H +#define _UI_SPOT_CONTROLLER_H + +#include "SpriteController.h" + +#include <utils/RefBase.h> +#include <utils/Looper.h> + +#include <SkBitmap.h> + +namespace android { + +/* + * Interface for displaying spots on screen that visually represent the positions + * of fingers on a touch pad. + * + * The spot controller is responsible for providing synchronization and for tracking + * display orientation changes if needed. + */ +class SpotControllerInterface : public virtual RefBase { +protected: + SpotControllerInterface() { } + virtual ~SpotControllerInterface() { } + +public: + +}; + + +/* + * Sprite-based spot controller implementation. + */ +class SpotController : public SpotControllerInterface, public MessageHandler { +protected: + virtual ~SpotController(); + +public: + SpotController(const sp<Looper>& looper, const sp<SpriteController>& spriteController); + +private: + mutable Mutex mLock; + + sp<Looper> mLooper; + sp<SpriteController> mSpriteController; + sp<WeakMessageHandler> mHandler; + + struct Locked { + } mLocked; + + void handleMessage(const Message& message); +}; + +} // namespace android + +#endif // _UI_SPOT_CONTROLLER_H diff --git a/services/input/SpriteController.cpp b/services/input/SpriteController.cpp new file mode 100644 index 0000000..c6d4390 --- /dev/null +++ b/services/input/SpriteController.cpp @@ -0,0 +1,472 @@ +/* + * Copyright (C) 2011 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_TAG "Sprites" + +//#define LOG_NDEBUG 0 + +#include "SpriteController.h" + +#include <cutils/log.h> +#include <utils/String8.h> + +#include <SkBitmap.h> +#include <SkCanvas.h> +#include <SkColor.h> +#include <SkPaint.h> +#include <SkXfermode.h> + +namespace android { + +// --- SpriteController --- + +SpriteController::SpriteController(const sp<Looper>& looper, int32_t overlayLayer) : + mLooper(looper), mOverlayLayer(overlayLayer) { + mHandler = new WeakMessageHandler(this); +} + +SpriteController::~SpriteController() { + mLooper->removeMessages(mHandler); + + if (mSurfaceComposerClient != NULL) { + mSurfaceComposerClient->dispose(); + mSurfaceComposerClient.clear(); + } +} + +sp<Sprite> SpriteController::createSprite() { + return new SpriteImpl(this); +} + +void SpriteController::invalidateSpriteLocked(const sp<SpriteImpl>& sprite) { + bool wasEmpty = mInvalidatedSprites.isEmpty(); + mInvalidatedSprites.push(sprite); + if (wasEmpty) { + mLooper->sendMessage(mHandler, Message(MSG_UPDATE_SPRITES)); + } +} + +void SpriteController::disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl) { + bool wasEmpty = mDisposedSurfaces.isEmpty(); + mDisposedSurfaces.push(surfaceControl); + if (wasEmpty) { + mLooper->sendMessage(mHandler, Message(MSG_DISPOSE_SURFACES)); + } +} + +void SpriteController::handleMessage(const Message& message) { + switch (message.what) { + case MSG_UPDATE_SPRITES: + doUpdateSprites(); + break; + case MSG_DISPOSE_SURFACES: + doDisposeSurfaces(); + break; + } +} + +void SpriteController::doUpdateSprites() { + // Collect information about sprite updates. + // Each sprite update record includes a reference to its associated sprite so we can + // be certain the sprites will not be deleted while this function runs. Sprites + // may invalidate themselves again during this time but we will handle those changes + // in the next iteration. + Vector<SpriteUpdate> updates; + size_t numSprites; + { // acquire lock + AutoMutex _l(mLock); + + numSprites = mInvalidatedSprites.size(); + for (size_t i = 0; i < numSprites; i++) { + const sp<SpriteImpl>& sprite = mInvalidatedSprites.itemAt(i); + + updates.push(SpriteUpdate(sprite, sprite->getStateLocked())); + sprite->resetDirtyLocked(); + } + mInvalidatedSprites.clear(); + } // release lock + + // Create missing surfaces. + bool surfaceChanged = false; + for (size_t i = 0; i < numSprites; i++) { + SpriteUpdate& update = updates.editItemAt(i); + + if (update.state.surfaceControl == NULL && update.state.wantSurfaceVisible()) { + update.state.surfaceWidth = update.state.bitmap.width(); + update.state.surfaceHeight = update.state.bitmap.height(); + update.state.surfaceDrawn = false; + update.state.surfaceVisible = false; + update.state.surfaceControl = obtainSurface( + update.state.surfaceWidth, update.state.surfaceHeight); + if (update.state.surfaceControl != NULL) { + update.surfaceChanged = surfaceChanged = true; + } + } + } + + // Resize sprites if needed, inside a global transaction. + bool haveGlobalTransaction = false; + for (size_t i = 0; i < numSprites; i++) { + SpriteUpdate& update = updates.editItemAt(i); + + if (update.state.surfaceControl != NULL && update.state.wantSurfaceVisible()) { + int32_t desiredWidth = update.state.bitmap.width(); + int32_t desiredHeight = update.state.bitmap.height(); + if (update.state.surfaceWidth < desiredWidth + || update.state.surfaceHeight < desiredHeight) { + if (!haveGlobalTransaction) { + SurfaceComposerClient::openGlobalTransaction(); + haveGlobalTransaction = true; + } + + status_t status = update.state.surfaceControl->setSize(desiredWidth, desiredHeight); + if (status) { + LOGE("Error %d resizing sprite surface from %dx%d to %dx%d", + status, update.state.surfaceWidth, update.state.surfaceHeight, + desiredWidth, desiredHeight); + } else { + update.state.surfaceWidth = desiredWidth; + update.state.surfaceHeight = desiredHeight; + update.state.surfaceDrawn = false; + update.surfaceChanged = surfaceChanged = true; + + if (update.state.surfaceVisible) { + status = update.state.surfaceControl->hide(); + if (status) { + LOGE("Error %d hiding sprite surface after resize.", status); + } else { + update.state.surfaceVisible = false; + } + } + } + } + } + } + if (haveGlobalTransaction) { + SurfaceComposerClient::closeGlobalTransaction(); + } + + // Redraw sprites if needed. + for (size_t i = 0; i < numSprites; i++) { + SpriteUpdate& update = updates.editItemAt(i); + + if ((update.state.dirty & DIRTY_BITMAP) && update.state.surfaceDrawn) { + update.state.surfaceDrawn = false; + update.surfaceChanged = surfaceChanged = true; + } + + if (update.state.surfaceControl != NULL && !update.state.surfaceDrawn + && update.state.wantSurfaceVisible()) { + sp<Surface> surface = update.state.surfaceControl->getSurface(); + Surface::SurfaceInfo surfaceInfo; + status_t status = surface->lock(&surfaceInfo); + if (status) { + LOGE("Error %d locking sprite surface before drawing.", status); + } else { + SkBitmap surfaceBitmap; + ssize_t bpr = surfaceInfo.s * bytesPerPixel(surfaceInfo.format); + surfaceBitmap.setConfig(SkBitmap::kARGB_8888_Config, + surfaceInfo.w, surfaceInfo.h, bpr); + surfaceBitmap.setPixels(surfaceInfo.bits); + + SkCanvas surfaceCanvas; + surfaceCanvas.setBitmapDevice(surfaceBitmap); + + SkPaint paint; + paint.setXfermodeMode(SkXfermode::kSrc_Mode); + surfaceCanvas.drawBitmap(update.state.bitmap, 0, 0, &paint); + + if (surfaceInfo.w > uint32_t(update.state.bitmap.width())) { + paint.setColor(0); // transparent fill color + surfaceCanvas.drawRectCoords(update.state.bitmap.width(), 0, + surfaceInfo.w, update.state.bitmap.height(), paint); + } + if (surfaceInfo.h > uint32_t(update.state.bitmap.height())) { + paint.setColor(0); // transparent fill color + surfaceCanvas.drawRectCoords(0, update.state.bitmap.height(), + surfaceInfo.w, surfaceInfo.h, paint); + } + + status = surface->unlockAndPost(); + if (status) { + LOGE("Error %d unlocking and posting sprite surface after drawing.", status); + } else { + update.state.surfaceDrawn = true; + update.surfaceChanged = surfaceChanged = true; + } + } + } + } + + // Set sprite surface properties and make them visible. + bool haveTransaction = false; + for (size_t i = 0; i < numSprites; i++) { + SpriteUpdate& update = updates.editItemAt(i); + + bool wantSurfaceVisibleAndDrawn = update.state.wantSurfaceVisible() + && update.state.surfaceDrawn; + bool becomingVisible = wantSurfaceVisibleAndDrawn && !update.state.surfaceVisible; + bool becomingHidden = !wantSurfaceVisibleAndDrawn && update.state.surfaceVisible; + if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden + || (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA + | DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER + | DIRTY_VISIBILITY | DIRTY_HOTSPOT))))) { + status_t status; + if (!haveTransaction) { + status = mSurfaceComposerClient->openTransaction(); + if (status) { + LOGE("Error %d opening transation to update sprite surface.", status); + break; + } + haveTransaction = true; + } + + if (wantSurfaceVisibleAndDrawn + && (becomingVisible || (update.state.dirty & DIRTY_ALPHA))) { + status = update.state.surfaceControl->setAlpha(update.state.alpha); + if (status) { + LOGE("Error %d setting sprite surface alpha.", status); + } + } + + if (wantSurfaceVisibleAndDrawn + && (becomingVisible || (update.state.dirty & (DIRTY_POSITION + | DIRTY_HOTSPOT)))) { + status = update.state.surfaceControl->setPosition( + update.state.positionX - update.state.hotSpotX, + update.state.positionY - update.state.hotSpotY); + if (status) { + LOGE("Error %d setting sprite surface position.", status); + } + } + + if (wantSurfaceVisibleAndDrawn + && (becomingVisible + || (update.state.dirty & DIRTY_TRANSFORMATION_MATRIX))) { + status = update.state.surfaceControl->setMatrix( + update.state.transformationMatrix.dsdx, + update.state.transformationMatrix.dtdx, + update.state.transformationMatrix.dsdy, + update.state.transformationMatrix.dtdy); + if (status) { + LOGE("Error %d setting sprite surface transformation matrix.", status); + } + } + + int32_t surfaceLayer = mOverlayLayer + update.state.layer; + if (wantSurfaceVisibleAndDrawn + && (becomingVisible || (update.state.dirty & DIRTY_LAYER))) { + status = update.state.surfaceControl->setLayer(surfaceLayer); + if (status) { + LOGE("Error %d setting sprite surface layer.", status); + } + } + + if (becomingVisible) { + status = update.state.surfaceControl->show(surfaceLayer); + if (status) { + LOGE("Error %d showing sprite surface.", status); + } else { + update.state.surfaceVisible = true; + update.surfaceChanged = surfaceChanged = true; + } + } else if (becomingHidden) { + status = update.state.surfaceControl->hide(); + if (status) { + LOGE("Error %d hiding sprite surface.", status); + } else { + update.state.surfaceVisible = false; + update.surfaceChanged = surfaceChanged = true; + } + } + } + } + + if (haveTransaction) { + status_t status = mSurfaceComposerClient->closeTransaction(); + if (status) { + LOGE("Error %d closing transaction to update sprite surface.", status); + } + } + + // If any surfaces were changed, write back the new surface properties to the sprites. + if (surfaceChanged) { // acquire lock + AutoMutex _l(mLock); + + for (size_t i = 0; i < numSprites; i++) { + const SpriteUpdate& update = updates.itemAt(i); + + if (update.surfaceChanged) { + update.sprite->setSurfaceLocked(update.state.surfaceControl, + update.state.surfaceWidth, update.state.surfaceHeight, + update.state.surfaceDrawn, update.state.surfaceVisible); + } + } + } // release lock + + // Clear the sprite update vector outside the lock. It is very important that + // we do not clear sprite references inside the lock since we could be releasing + // the last remaining reference to the sprite here which would result in the + // sprite being deleted and the lock being reacquired by the sprite destructor + // while already held. + updates.clear(); +} + +void SpriteController::doDisposeSurfaces() { + // Collect disposed surfaces. + Vector<sp<SurfaceControl> > disposedSurfaces; + { // acquire lock + disposedSurfaces = mDisposedSurfaces; + mDisposedSurfaces.clear(); + } // release lock + + // Release the last reference to each surface outside of the lock. + // We don't want the surfaces to be deleted while we are holding our lock. + disposedSurfaces.clear(); +} + +void SpriteController::ensureSurfaceComposerClient() { + if (mSurfaceComposerClient == NULL) { + mSurfaceComposerClient = new SurfaceComposerClient(); + } +} + +sp<SurfaceControl> SpriteController::obtainSurface(int32_t width, int32_t height) { + ensureSurfaceComposerClient(); + + sp<SurfaceControl> surfaceControl = mSurfaceComposerClient->createSurface( + getpid(), String8("Sprite"), 0, width, height, PIXEL_FORMAT_RGBA_8888); + if (surfaceControl == NULL) { + LOGE("Error creating sprite surface."); + return NULL; + } + return surfaceControl; +} + + +// --- SpriteController::SpriteImpl --- + +SpriteController::SpriteImpl::SpriteImpl(const sp<SpriteController> controller) : + mController(controller), mTransactionNestingCount(0) { +} + +SpriteController::SpriteImpl::~SpriteImpl() { + AutoMutex _m(mController->mLock); + + // Let the controller take care of deleting the last reference to sprite + // surfaces so that we do not block the caller on an IPC here. + if (mState.surfaceControl != NULL) { + mController->disposeSurfaceLocked(mState.surfaceControl); + mState.surfaceControl.clear(); + } +} + +void SpriteController::SpriteImpl::setBitmap(const SkBitmap* bitmap, + float hotSpotX, float hotSpotY) { + AutoMutex _l(mController->mLock); + + if (bitmap) { + bitmap->copyTo(&mState.bitmap, SkBitmap::kARGB_8888_Config); + } else { + mState.bitmap.reset(); + } + + uint32_t dirty = DIRTY_BITMAP; + if (mState.hotSpotX != hotSpotX || mState.hotSpotY != hotSpotY) { + mState.hotSpotX = hotSpotX; + mState.hotSpotY = hotSpotY; + dirty |= DIRTY_HOTSPOT; + } + + invalidateLocked(dirty); +} + +void SpriteController::SpriteImpl::setVisible(bool visible) { + AutoMutex _l(mController->mLock); + + if (mState.visible != visible) { + mState.visible = visible; + invalidateLocked(DIRTY_VISIBILITY); + } +} + +void SpriteController::SpriteImpl::setPosition(float x, float y) { + AutoMutex _l(mController->mLock); + + if (mState.positionX != x || mState.positionY != y) { + mState.positionX = x; + mState.positionY = y; + invalidateLocked(DIRTY_POSITION); + } +} + +void SpriteController::SpriteImpl::setLayer(int32_t layer) { + AutoMutex _l(mController->mLock); + + if (mState.layer != layer) { + mState.layer = layer; + invalidateLocked(DIRTY_LAYER); + } +} + +void SpriteController::SpriteImpl::setAlpha(float alpha) { + AutoMutex _l(mController->mLock); + + if (mState.alpha != alpha) { + mState.alpha = alpha; + invalidateLocked(DIRTY_ALPHA); + } +} + +void SpriteController::SpriteImpl::setTransformationMatrix( + const SpriteTransformationMatrix& matrix) { + AutoMutex _l(mController->mLock); + + if (mState.transformationMatrix != matrix) { + mState.transformationMatrix = matrix; + invalidateLocked(DIRTY_TRANSFORMATION_MATRIX); + } +} + +void SpriteController::SpriteImpl::openTransaction() { + AutoMutex _l(mController->mLock); + + mTransactionNestingCount += 1; +} + +void SpriteController::SpriteImpl::closeTransaction() { + AutoMutex _l(mController->mLock); + + LOG_ALWAYS_FATAL_IF(mTransactionNestingCount == 0, + "Sprite closeTransaction() called but there is no open sprite transaction"); + + mTransactionNestingCount -= 1; + if (mTransactionNestingCount == 0 && mState.dirty) { + mController->invalidateSpriteLocked(this); + } +} + +void SpriteController::SpriteImpl::invalidateLocked(uint32_t dirty) { + if (mTransactionNestingCount > 0) { + bool wasDirty = mState.dirty; + mState.dirty |= dirty; + if (!wasDirty) { + mController->invalidateSpriteLocked(this); + } + } +} + +} // namespace android diff --git a/services/input/SpriteController.h b/services/input/SpriteController.h new file mode 100644 index 0000000..27afb5e --- /dev/null +++ b/services/input/SpriteController.h @@ -0,0 +1,251 @@ +/* + * Copyright (C) 2011 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 _UI_SPRITES_H +#define _UI_SPRITES_H + +#include <utils/RefBase.h> +#include <utils/Looper.h> + +#include <surfaceflinger/Surface.h> +#include <surfaceflinger/SurfaceComposerClient.h> +#include <surfaceflinger/ISurfaceComposer.h> + +#include <SkBitmap.h> + +namespace android { + +/* + * Transformation matrix for a sprite. + */ +struct SpriteTransformationMatrix { + inline SpriteTransformationMatrix() : dsdx(1.0f), dtdx(0.0f), dsdy(0.0f), dtdy(1.0f) { } + + float dsdx; + float dtdx; + float dsdy; + float dtdy; + + inline bool operator== (const SpriteTransformationMatrix& other) { + return dsdx == other.dsdx + && dtdx == other.dtdx + && dsdy == other.dsdy + && dtdy == other.dtdy; + } + + inline bool operator!= (const SpriteTransformationMatrix& other) { + return !(*this == other); + } +}; + +/* + * A sprite is a simple graphical object that is displayed on-screen above other layers. + * The basic sprite class is an interface. + * The implementation is provided by the sprite controller. + */ +class Sprite : public RefBase { +protected: + Sprite() { } + virtual ~Sprite() { } + +public: + /* Sets the bitmap that is drawn by the sprite. + * The sprite retains a copy of the bitmap for subsequent rendering. */ + virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY) = 0; + + /* Sets whether the sprite is visible. */ + virtual void setVisible(bool visible) = 0; + + /* Sets the sprite position on screen, relative to the sprite's hot spot. */ + virtual void setPosition(float x, float y) = 0; + + /* Sets the layer of the sprite, relative to the system sprite overlay layer. + * Layer 0 is the overlay layer, > 0 appear above this layer. */ + virtual void setLayer(int32_t layer) = 0; + + /* Sets the sprite alpha blend ratio between 0.0 and 1.0. */ + virtual void setAlpha(float alpha) = 0; + + /* Sets the sprite transformation matrix. */ + virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix) = 0; + + /* Opens or closes a transaction to perform a batch of sprite updates as part of + * a single operation such as setPosition and setAlpha. It is not necessary to + * open a transaction when updating a single property. + * Calls to openTransaction() nest and must be matched by an equal number + * of calls to closeTransaction(). */ + virtual void openTransaction() = 0; + virtual void closeTransaction() = 0; +}; + +/* + * Displays sprites on the screen. + * + * This interface is used by PointerController and SpotController to draw pointers or + * spot representations of fingers. It is not intended for general purpose use + * by other components. + * + * All sprite position updates and rendering is performed asynchronously. + * + * Clients are responsible for animating sprites by periodically updating their properties. + */ +class SpriteController : public MessageHandler { +protected: + virtual ~SpriteController(); + +public: + SpriteController(const sp<Looper>& looper, int32_t overlayLayer); + + /* Creates a new sprite, initially invisible. */ + sp<Sprite> createSprite(); + +private: + enum { + MSG_UPDATE_SPRITES, + MSG_DISPOSE_SURFACES, + }; + + enum { + DIRTY_BITMAP = 1 << 0, + DIRTY_ALPHA = 1 << 1, + DIRTY_POSITION = 1 << 2, + DIRTY_TRANSFORMATION_MATRIX = 1 << 3, + DIRTY_LAYER = 1 << 4, + DIRTY_VISIBILITY = 1 << 5, + DIRTY_HOTSPOT = 1 << 6, + }; + + /* Describes the state of a sprite. + * This structure is designed so that it can be copied during updates so that + * surfaces can be resized and redrawn without blocking the client by holding a lock + * on the sprites for a long time. + * Note that the SkBitmap holds a reference to a shared (and immutable) pixel ref. */ + struct SpriteState { + inline SpriteState() : + dirty(0), hotSpotX(0), hotSpotY(0), visible(false), + positionX(0), positionY(0), layer(0), alpha(1.0f), + surfaceWidth(0), surfaceHeight(0), surfaceDrawn(false), surfaceVisible(false) { + } + + uint32_t dirty; + + SkBitmap bitmap; + float hotSpotX; + float hotSpotY; + bool visible; + float positionX; + float positionY; + int32_t layer; + float alpha; + SpriteTransformationMatrix transformationMatrix; + + sp<SurfaceControl> surfaceControl; + int32_t surfaceWidth; + int32_t surfaceHeight; + bool surfaceDrawn; + bool surfaceVisible; + + inline bool wantSurfaceVisible() const { + return visible && alpha > 0.0f && !bitmap.isNull() && !bitmap.empty(); + } + }; + + /* Client interface for a sprite. + * Requests acquire a lock on the controller, update local state and request the + * controller to invalidate the sprite. + * The real heavy lifting of creating, resizing and redrawing surfaces happens + * asynchronously with no locks held except in short critical section to copy + * the sprite state before the work and update the sprite surface control afterwards. + */ + class SpriteImpl : public Sprite { + protected: + virtual ~SpriteImpl(); + + public: + SpriteImpl(const sp<SpriteController> controller); + + virtual void setBitmap(const SkBitmap* bitmap, float hotSpotX, float hotSpotY); + virtual void setVisible(bool visible); + virtual void setPosition(float x, float y); + virtual void setLayer(int32_t layer); + virtual void setAlpha(float alpha); + virtual void setTransformationMatrix(const SpriteTransformationMatrix& matrix); + virtual void openTransaction(); + virtual void closeTransaction(); + + inline const SpriteState& getStateLocked() const { + return mState; + } + + inline void resetDirtyLocked() { + mState.dirty = 0; + } + + inline void setSurfaceLocked(const sp<SurfaceControl>& surfaceControl, + int32_t width, int32_t height, bool drawn, bool visible) { + mState.surfaceControl = surfaceControl; + mState.surfaceWidth = width; + mState.surfaceHeight = height; + mState.surfaceDrawn = drawn; + mState.surfaceVisible = visible; + } + + private: + sp<SpriteController> mController; + + SpriteState mState; // guarded by mController->mLock + uint32_t mTransactionNestingCount; // guarded by mController->mLock + + void invalidateLocked(uint32_t dirty); + }; + + /* Stores temporary information collected during the sprite update cycle. */ + struct SpriteUpdate { + inline SpriteUpdate() : surfaceChanged(false) { } + inline SpriteUpdate(const sp<SpriteImpl> sprite, const SpriteState& state) : + sprite(sprite), state(state), surfaceChanged(false) { + } + + sp<SpriteImpl> sprite; + SpriteState state; + bool surfaceChanged; + }; + + mutable Mutex mLock; + + sp<Looper> mLooper; + const int32_t mOverlayLayer; + sp<WeakMessageHandler> mHandler; + + sp<SurfaceComposerClient> mSurfaceComposerClient; + + Vector<sp<SpriteImpl> > mInvalidatedSprites; // guarded by mLock + Vector<sp<SurfaceControl> > mDisposedSurfaces; // guarded by mLock + + void invalidateSpriteLocked(const sp<SpriteImpl>& sprite); + void disposeSurfaceLocked(const sp<SurfaceControl>& surfaceControl); + + void handleMessage(const Message& message); + void doUpdateSprites(); + void doDisposeSurfaces(); + + void ensureSurfaceComposerClient(); + sp<SurfaceControl> obtainSurface(int32_t width, int32_t height); +}; + +} // namespace android + +#endif // _UI_SPRITES_H diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index 4c5f239..ba8ca9c 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -192,6 +192,10 @@ private: virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId) { return mPointerControllers.valueFor(deviceId); } + + virtual sp<SpotControllerInterface> obtainSpotController(int32_t device) { + return NULL; + } }; diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index 1455764..7028772 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -1399,6 +1399,24 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } + public InputMethodSubtype getLastInputMethodSubtype() { + synchronized (mMethodMap) { + final Pair<String, String> lastIme = mSettings.getLastInputMethodAndSubtypeLocked(); + // TODO: Handle the case of the last IME with no subtypes + if (lastIme == null || TextUtils.isEmpty(lastIme.first) + || TextUtils.isEmpty(lastIme.second)) return null; + final InputMethodInfo lastImi = mMethodMap.get(lastIme.first); + if (lastImi == null) return null; + try { + final int lastSubtypeHash = Integer.valueOf(lastIme.second); + return lastImi.getSubtypeAt(getSubtypeIdFromHashCode( + lastImi, lastSubtypeHash)); + } catch (NumberFormatException e) { + return null; + } + } + } + private void setInputMethodWithSubtypeId(IBinder token, String id, int subtypeId) { synchronized (mMethodMap) { if (token == null) { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index f44b889..31b7f86 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -441,8 +441,7 @@ public final class ActivityManagerService extends ActivityManagerNative /** * List of intents that were used to start the most recent tasks. */ - final ArrayList<TaskRecord> mRecentTasks - = new ArrayList<TaskRecord>(); + final ArrayList<TaskRecord> mRecentTasks = new ArrayList<TaskRecord>(); /** * All of the applications we currently have running organized by name. @@ -450,8 +449,7 @@ public final class ActivityManagerService extends ActivityManagerNative * returned by the package manager), and the keys are ApplicationRecord * objects. */ - final ProcessMap<ProcessRecord> mProcessNames - = new ProcessMap<ProcessRecord>(); + final ProcessMap<ProcessRecord> mProcessNames = new ProcessMap<ProcessRecord>(); /** * The currently running heavy-weight process, if any. @@ -480,8 +478,7 @@ public final class ActivityManagerService extends ActivityManagerNative * <p>NOTE: This object is protected by its own lock, NOT the global * activity manager lock! */ - final SparseArray<ProcessRecord> mPidsSelfLocked - = new SparseArray<ProcessRecord>(); + final SparseArray<ProcessRecord> mPidsSelfLocked = new SparseArray<ProcessRecord>(); /** * All of the processes that have been forced to be foreground. The key @@ -2981,7 +2978,10 @@ public final class ActivityManagerService extends ActivityManagerNative synchronized (this) { if (!showBackground && !app.isInterestingToUserLocked() && app.pid != MY_PID) { - Process.killProcess(app.pid); + Slog.w(TAG, "Killing " + app + ": background ANR"); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "background ANR"); + Process.killProcessQuiet(app.pid); return; } @@ -3446,7 +3446,9 @@ public final class ActivityManagerService extends ActivityManagerNative bringDownServiceLocked(sr, true); } } - Process.killProcess(pid); + EventLog.writeEvent(EventLogTags.AM_KILL, pid, + app.processName, app.setAdj, "start timeout"); + Process.killProcessQuiet(pid); if (mBackupTarget != null && mBackupTarget.app.pid == pid) { Slog.w(TAG, "Unattached app died before backup, skipping"); try { @@ -3492,7 +3494,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " (IApplicationThread " + thread + "); dropping process"); EventLog.writeEvent(EventLogTags.AM_DROP_PROCESS, pid); if (pid > 0 && pid != MY_PID) { - Process.killProcess(pid); + Process.killProcessQuiet(pid); } else { try { thread.scheduleExit(); @@ -3890,18 +3892,17 @@ public final class ActivityManagerService extends ActivityManagerNative } for (int i=0; i<intents.length; i++) { Intent intent = intents[i]; - if (intent == null) { - throw new IllegalArgumentException("Null intent at index " + i); - } - if (intent.hasFileDescriptors()) { - throw new IllegalArgumentException("File descriptors passed in Intent"); - } - if (type == INTENT_SENDER_BROADCAST && - (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { - throw new IllegalArgumentException( - "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); + if (intent != null) { + if (intent.hasFileDescriptors()) { + throw new IllegalArgumentException("File descriptors passed in Intent"); + } + if (type == INTENT_SENDER_BROADCAST && + (intent.getFlags()&Intent.FLAG_RECEIVER_BOOT_UPGRADE) != 0) { + throw new IllegalArgumentException( + "Can't use FLAG_RECEIVER_BOOT_UPGRADE here"); + } + intents[i] = new Intent(intent); } - intents[i] = new Intent(intent); } if (resolvedTypes != null && resolvedTypes.length != intents.length) { throw new IllegalArgumentException( @@ -4832,11 +4833,6 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } - final boolean canReadFb = (flags&ActivityManager.TASKS_GET_THUMBNAILS) != 0 - && checkCallingPermission( - android.Manifest.permission.READ_FRAME_BUFFER) - == PackageManager.PERMISSION_GRANTED; - int pos = mMainStack.mHistory.size()-1; ActivityRecord next = pos >= 0 ? (ActivityRecord)mMainStack.mHistory.get(pos) : null; @@ -4876,13 +4872,6 @@ public final class ActivityManagerService extends ActivityManagerNative ci.id = curTask.taskId; ci.baseActivity = r.intent.getComponent(); ci.topActivity = top.intent.getComponent(); - if (canReadFb) { - if (top.state == ActivityState.RESUMED) { - ci.thumbnail = top.stack.screenshotActivities(top); - } else if (top.thumbHolder != null) { - ci.thumbnail = top.thumbHolder.lastThumbnail; - } - } if (top.thumbHolder != null) { ci.description = top.thumbHolder.lastDescription; } @@ -4955,8 +4944,6 @@ public final class ActivityManagerService extends ActivityManagerNative IPackageManager pm = AppGlobals.getPackageManager(); - ActivityRecord resumed = mMainStack.mResumedActivity; - final int N = mRecentTasks.size(); ArrayList<ActivityManager.RecentTaskInfo> res = new ArrayList<ActivityManager.RecentTaskInfo>( @@ -5002,74 +4989,121 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private TaskRecord taskForIdLocked(int id) { + final int N = mRecentTasks.size(); + for (int i=0; i<N; i++) { + TaskRecord tr = mRecentTasks.get(i); + if (tr.taskId == id) { + return tr; + } + } + return null; + } + public ActivityManager.TaskThumbnails getTaskThumbnails(int id) { synchronized (this) { enforceCallingPermission(android.Manifest.permission.READ_FRAME_BUFFER, - "getTaskThumbnail()"); - ActivityRecord resumed = mMainStack.mResumedActivity; - final int N = mRecentTasks.size(); - for (int i=0; i<N; i++) { - TaskRecord tr = mRecentTasks.get(i); - if (tr.taskId == id) { - final ActivityManager.TaskThumbnails thumbs - = new ActivityManager.TaskThumbnails(); - if (resumed != null && resumed.thumbHolder == tr) { - thumbs.mainThumbnail = resumed.stack.screenshotActivities(resumed); - } else { - thumbs.mainThumbnail = tr.lastThumbnail; - } - // How many different sub-thumbnails? - final int NA = mMainStack.mHistory.size(); - int j = 0; - ThumbnailHolder holder = null; - while (j < NA) { - ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j); - j++; - if (ar.task == tr) { - holder = ar.thumbHolder; - break; - } - } - ArrayList<Bitmap> bitmaps = new ArrayList<Bitmap>(); - thumbs.otherThumbnails = bitmaps; - ActivityRecord lastActivity = null; - while (j < NA) { - ActivityRecord ar = (ActivityRecord)mMainStack.mHistory.get(j); - j++; - if (ar.task != tr) { - break; - } - lastActivity = ar; - if (ar.thumbHolder != holder && holder != null) { - thumbs.numSubThumbbails++; - holder = ar.thumbHolder; - bitmaps.add(holder.lastThumbnail); - } - } - if (lastActivity != null && bitmaps.size() > 0) { - if (resumed == lastActivity) { - Bitmap bm = lastActivity.stack.screenshotActivities(lastActivity); - if (bm != null) { - bitmaps.remove(bitmaps.size()-1); - bitmaps.add(bm); - } - } + "getTaskThumbnails()"); + TaskRecord tr = taskForIdLocked(id); + if (tr != null) { + return mMainStack.getTaskThumbnailsLocked(tr); + } + } + return null; + } + + public boolean removeSubTask(int taskId, int subTaskIndex) { + synchronized (this) { + enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, + "removeSubTask()"); + long ident = Binder.clearCallingIdentity(); + try { + return mMainStack.removeTaskActivitiesLocked(taskId, subTaskIndex) != null; + } finally { + Binder.restoreCallingIdentity(ident); + } + } + } + + private void removeTaskProcessesLocked(ActivityRecord root) { + TaskRecord tr = root.task; + Intent baseIntent = new Intent( + tr.intent != null ? tr.intent : tr.affinityIntent); + ComponentName component = baseIntent.getComponent(); + if (component == null) { + Slog.w(TAG, "Now component for base intent of task: " + tr); + return; + } + + // Find any running services associated with this app. + ArrayList<ServiceRecord> services = new ArrayList<ServiceRecord>(); + for (ServiceRecord sr : mServices.values()) { + if (sr.packageName.equals(component.getPackageName())) { + services.add(sr); + } + } + + // Take care of any running services associated with the app. + for (int i=0; i<services.size(); i++) { + ServiceRecord sr = services.get(i); + if (sr.startRequested) { + if ((sr.serviceInfo.flags&ServiceInfo.FLAG_STOP_WITH_TASK) != 0) { + stopServiceLocked(sr); + } else { + sr.pendingStarts.add(new ServiceRecord.StartItem(sr, true, + sr.makeNextStartId(), baseIntent, -1)); + if (sr.app != null && sr.app.thread != null) { + sendServiceArgsLocked(sr, false); } - if (thumbs.numSubThumbbails > 0) { - thumbs.retriever = new IThumbnailRetriever.Stub() { - public Bitmap getThumbnail(int index) { - if (index < 0 || index >= thumbs.otherThumbnails.size()) { - return null; - } - return thumbs.otherThumbnails.get(index); - } - }; + } + } + } + + // Find any running processes associated with this app. + ArrayList<ProcessRecord> procs = new ArrayList<ProcessRecord>(); + SparseArray<ProcessRecord> appProcs + = mProcessNames.getMap().get(component.getPackageName()); + if (appProcs != null) { + for (int i=0; i<appProcs.size(); i++) { + procs.add(appProcs.valueAt(i)); + } + } + + // Kill the running processes. + for (int i=0; i<procs.size(); i++) { + ProcessRecord pr = procs.get(i); + if (pr.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { + Slog.i(TAG, "Killing " + pr + ": remove task"); + EventLog.writeEvent(EventLogTags.AM_KILL, pr.pid, + pr.processName, pr.setAdj, "remove task"); + Process.killProcessQuiet(pr.pid); + } else { + pr.waitingToKill = "remove task"; + } + } + } + + public boolean removeTask(int taskId, int flags) { + synchronized (this) { + enforceCallingPermission(android.Manifest.permission.REMOVE_TASKS, + "removeTask()"); + long ident = Binder.clearCallingIdentity(); + try { + ActivityRecord r = mMainStack.removeTaskActivitiesLocked(taskId, -1); + if (r != null) { + mRecentTasks.remove(r.task); + + if ((flags&ActivityManager.REMOVE_TASK_KILL_PROCESS) != 0) { + removeTaskProcessesLocked(r); } - return thumbs; + + return true; } + } finally { + Binder.restoreCallingIdentity(ident); } } - return null; + return false; } private final int findAffinityTaskTopLocked(int startIndex, String affinity) { @@ -5123,21 +5157,18 @@ public final class ActivityManagerService extends ActivityManagerNative } final long origId = Binder.clearCallingIdentity(); try { - int N = mRecentTasks.size(); - for (int i=0; i<N; i++) { - TaskRecord tr = mRecentTasks.get(i); - if (tr.taskId == task) { - if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { - mMainStack.mUserLeaving = true; - } - if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { - // Caller wants the home activity moved with it. To accomplish this, - // we'll just move the home task to the top first. - mMainStack.moveHomeToFrontLocked(); - } - mMainStack.moveTaskToFrontLocked(tr, null); - return; + TaskRecord tr = taskForIdLocked(task); + if (tr != null) { + if ((flags&ActivityManager.MOVE_TASK_NO_USER_ACTION) == 0) { + mMainStack.mUserLeaving = true; + } + if ((flags&ActivityManager.MOVE_TASK_WITH_HOME) != 0) { + // Caller wants the home activity moved with it. To accomplish this, + // we'll just move the home task to the top first. + mMainStack.moveHomeToFrontLocked(); } + mMainStack.moveTaskToFrontLocked(tr, null); + return; } for (int i=mMainStack.mHistory.size()-1; i>=0; i--) { ActivityRecord hr = (ActivityRecord)mMainStack.mHistory.get(i); @@ -6661,11 +6692,10 @@ public final class ActivityManagerService extends ActivityManagerNative } if (app.pid > 0 && app.pid != MY_PID) { handleAppCrashLocked(app); - Slog.i(ActivityManagerService.TAG, "Killing " - + app.processName + " (pid=" + app.pid + "): user's request"); + Slog.i(ActivityManagerService.TAG, "Killing " + app + ": user's request"); EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, app.processName, app.setAdj, "user's request after error"); - Process.killProcess(app.pid); + Process.killProcessQuiet(app.pid); } } } @@ -8946,7 +8976,7 @@ public final class ActivityManagerService extends ActivityManagerNative + " in dying process " + proc.processName); EventLog.writeEvent(EventLogTags.AM_KILL, capp.pid, capp.processName, capp.setAdj, "dying provider " + proc.processName); - Process.killProcess(capp.pid); + Process.killProcessQuiet(capp.pid); } } @@ -9453,7 +9483,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (si.doneExecutingCount > 0) { flags |= Service.START_FLAG_REDELIVERY; } - r.app.thread.scheduleServiceArgs(r, si.id, flags, si.intent); + r.app.thread.scheduleServiceArgs(r, si.taskRemoved, si.id, flags, si.intent); } catch (RemoteException e) { // Remote process gone... we'll let the normal cleanup take // care of this. @@ -9539,11 +9569,8 @@ public final class ActivityManagerService extends ActivityManagerNative // pending arguments, then fake up one so its onStartCommand() will // be called. if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) { - r.lastStartId++; - if (r.lastStartId < 1) { - r.lastStartId = 1; - } - r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, null, -1)); + r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), + null, -1)); } sendServiceArgsLocked(r, true); @@ -9897,11 +9924,7 @@ public final class ActivityManagerService extends ActivityManagerNative } r.startRequested = true; r.callStart = false; - r.lastStartId++; - if (r.lastStartId < 1) { - r.lastStartId = 1; - } - r.pendingStarts.add(new ServiceRecord.StartItem(r, r.lastStartId, + r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(), service, targetPermissionUid)); r.lastActivity = SystemClock.uptimeMillis(); synchronized (r.stats.getBatteryStats()) { @@ -9943,6 +9966,15 @@ public final class ActivityManagerService extends ActivityManagerNative } } + private void stopServiceLocked(ServiceRecord service) { + synchronized (service.stats.getBatteryStats()) { + service.stats.stopRunningLocked(); + } + service.startRequested = false; + service.callStart = false; + bringDownServiceLocked(service, false); + } + public int stopService(IApplicationThread caller, Intent service, String resolvedType) { // Refuse possible leaked file descriptors @@ -9966,14 +9998,12 @@ public final class ActivityManagerService extends ActivityManagerNative ServiceLookupResult r = findServiceLocked(service, resolvedType); if (r != null) { if (r.record != null) { - synchronized (r.record.stats.getBatteryStats()) { - r.record.stats.stopRunningLocked(); - } - r.record.startRequested = false; - r.record.callStart = false; final long origId = Binder.clearCallingIdentity(); - bringDownServiceLocked(r.record, false); - Binder.restoreCallingIdentity(origId); + try { + stopServiceLocked(r.record); + } finally { + Binder.restoreCallingIdentity(origId); + } return 1; } return -1; @@ -10035,7 +10065,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } - if (r.lastStartId != startId) { + if (r.getLastStartId() != startId) { return false; } @@ -10476,7 +10506,7 @@ public final class ActivityManagerService extends ActivityManagerNative case Service.START_NOT_STICKY: { // We are done with the associated start arguments. r.findDeliveredStart(startId, true); - if (r.lastStartId == startId) { + if (r.getLastStartId() == startId) { // There is no more work, and this service // doesn't want to hang around if killed. r.stopIfKilled = true; @@ -10496,6 +10526,12 @@ public final class ActivityManagerService extends ActivityManagerNative } break; } + case Service.START_TASK_REMOVED_COMPLETE: { + // Special processing for onTaskRemoved(). Don't + // impact normal onStartCommand() processing. + r.findDeliveredStart(startId, true); + break; + } default: throw new IllegalArgumentException( "Unknown service start result: " + res); @@ -12885,23 +12921,31 @@ public final class ActivityManagerService extends ActivityManagerNative if (DEBUG_SWITCH || DEBUG_OOM_ADJ) Slog.v(TAG, "Setting process group of " + app.processName + " to " + app.curSchedGroup); - if (true) { - long oldId = Binder.clearCallingIdentity(); - try { - Process.setProcessGroup(app.pid, app.curSchedGroup); - } catch (Exception e) { - Slog.w(TAG, "Failed setting process group of " + app.pid - + " to " + app.curSchedGroup); - e.printStackTrace(); - } finally { - Binder.restoreCallingIdentity(oldId); - } - } - if (false) { - if (app.thread != null) { + if (app.waitingToKill != null && + app.setSchedGroup == Process.THREAD_GROUP_BG_NONINTERACTIVE) { + Slog.i(TAG, "Killing " + app + ": " + app.waitingToKill); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, app.waitingToKill); + Process.killProcessQuiet(app.pid); + } else { + if (true) { + long oldId = Binder.clearCallingIdentity(); try { - app.thread.setSchedulingGroup(app.curSchedGroup); - } catch (RemoteException e) { + Process.setProcessGroup(app.pid, app.curSchedGroup); + } catch (Exception e) { + Slog.w(TAG, "Failed setting process group of " + app.pid + + " to " + app.curSchedGroup); + e.printStackTrace(); + } finally { + Binder.restoreCallingIdentity(oldId); + } + } + if (false) { + if (app.thread != null) { + try { + app.thread.setSchedulingGroup(app.curSchedGroup); + } catch (RemoteException e) { + } } } } @@ -13024,7 +13068,9 @@ public final class ActivityManagerService extends ActivityManagerNative + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); if (app.pid > 0 && app.pid != MY_PID) { - Process.killProcess(app.pid); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "empty"); + Process.killProcessQuiet(app.pid); } else { try { app.thread.scheduleExit(); @@ -13090,7 +13136,9 @@ public final class ActivityManagerService extends ActivityManagerNative + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); if (app.pid > 0 && app.pid != MY_PID) { - Process.killProcess(app.pid); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "empty"); + Process.killProcessQuiet(app.pid); } else { try { app.thread.scheduleExit(); @@ -13147,7 +13195,9 @@ public final class ActivityManagerService extends ActivityManagerNative + (app.thread != null ? app.thread.asBinder() : null) + ")\n"); if (app.pid > 0 && app.pid != MY_PID) { - Process.killProcess(app.pid); + EventLog.writeEvent(EventLogTags.AM_KILL, app.pid, + app.processName, app.setAdj, "old background"); + Process.killProcessQuiet(app.pid); } else { try { app.thread.scheduleExit(); @@ -13364,4 +13414,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + + // Multi-user methods + + public boolean switchUser(int userid) { + // TODO + return true; + } } diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index e1d380b..9558895 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -21,8 +21,10 @@ import com.android.internal.os.BatteryStatsImpl; import com.android.server.am.ActivityManagerService.PendingActivityLaunch; import android.app.Activity; +import android.app.ActivityManager; import android.app.AppGlobals; import android.app.IActivityManager; +import android.app.IThumbnailRetriever; import static android.app.IActivityManager.START_CLASS_NOT_FOUND; import static android.app.IActivityManager.START_DELIVERED_TO_TOP; import static android.app.IActivityManager.START_FORWARD_AND_REQUEST_CONFLICT; @@ -69,7 +71,7 @@ import java.util.List; /** * State and management of a single stack of activities. */ -final public class ActivityStack { +final class ActivityStack { static final String TAG = ActivityManagerService.TAG; static final boolean localLOGV = ActivityManagerService.localLOGV; static final boolean DEBUG_SWITCH = ActivityManagerService.DEBUG_SWITCH; @@ -135,14 +137,14 @@ final public class ActivityStack { * The back history of all previous (and possibly still * running) activities. It contains HistoryRecord objects. */ - final ArrayList mHistory = new ArrayList(); + final ArrayList<ActivityRecord> mHistory = new ArrayList<ActivityRecord>(); /** * List of running activities, sorted by recent usage. * The first entry in the list is the least recently used. * It contains HistoryRecord objects. */ - final ArrayList mLRUActivities = new ArrayList(); + final ArrayList<ActivityRecord> mLRUActivities = new ArrayList<ActivityRecord>(); /** * List of activities that are waiting for a new activity @@ -346,7 +348,7 @@ final public class ActivityStack { final ActivityRecord topRunningActivityLocked(ActivityRecord notTop) { int i = mHistory.size()-1; while (i >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (!r.finishing && r != notTop) { return r; } @@ -358,7 +360,7 @@ final public class ActivityStack { final ActivityRecord topRunningNonDelayedActivityLocked(ActivityRecord notTop) { int i = mHistory.size()-1; while (i >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (!r.finishing && !r.delayedResume && r != notTop) { return r; } @@ -379,7 +381,7 @@ final public class ActivityStack { final ActivityRecord topRunningActivityLocked(IBinder token, int taskId) { int i = mHistory.size()-1; while (i >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); // Note: the taskId check depends on real taskId fields being non-zero if (!r.finishing && (token != r) && (taskId != r.task.taskId)) { return r; @@ -425,7 +427,7 @@ final public class ActivityStack { final int N = mHistory.size(); for (int i=(N-1); i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (!r.finishing && r.task != cp && r.launchMode != ActivityInfo.LAUNCH_SINGLE_INSTANCE) { cp = r.task; @@ -469,7 +471,7 @@ final public class ActivityStack { final int N = mHistory.size(); for (int i=(N-1); i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (!r.finishing) { if (r.intent.getComponent().equals(cls)) { //Slog.i(TAG, "Found matching class!"); @@ -504,6 +506,7 @@ final public class ActivityStack { } r.app = app; + app.waitingToKill = null; if (localLOGV) Slog.v(TAG, "Launching: " + r); @@ -678,7 +681,7 @@ final public class ActivityStack { } // Ensure activities are no longer sleeping. for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); r.setSleeping(false); } mGoingToSleepActivities.clear(); @@ -724,7 +727,7 @@ final public class ActivityStack { // Make sure any stopped but visible activities are now sleeping. // This ensures that the activity's onStop() is called. for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (r.state == ActivityState.STOPPING || r.state == ActivityState.STOPPED) { r.setSleeping(true); } @@ -862,7 +865,7 @@ final public class ActivityStack { synchronized (mService) { int index = indexOfTokenLocked(token); if (index >= 0) { - r = (ActivityRecord)mHistory.get(index); + r = mHistory.get(index); mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); if (mPausingActivity == r) { r.state = ActivityState.PAUSED; @@ -1024,7 +1027,7 @@ final public class ActivityStack { ActivityRecord r; boolean behindFullscreen = false; for (; i>=0; i--) { - r = (ActivityRecord)mHistory.get(i); + r = mHistory.get(i); if (DEBUG_VISBILITY) Slog.v( TAG, "Make visible? " + r + " finishing=" + r.finishing + " state=" + r.state); @@ -1108,7 +1111,7 @@ final public class ActivityStack { // Now for any activities that aren't visible to the user, make // sure they no longer are keeping the screen frozen. while (i >= 0) { - r = (ActivityRecord)mHistory.get(i); + r = mHistory.get(i); if (DEBUG_VISBILITY) Slog.v( TAG, "Make invisible? " + r + " finishing=" + r.finishing + " state=" + r.state @@ -1499,7 +1502,7 @@ final public class ActivityStack { // If starting in an existing task, find where that is... boolean startIt = true; for (int i = NH-1; i >= 0; i--) { - ActivityRecord p = (ActivityRecord)mHistory.get(i); + ActivityRecord p = mHistory.get(i); if (p.finishing) { continue; } @@ -1646,7 +1649,7 @@ final public class ActivityStack { int replyChainEnd = -1; int lastReparentPos = -1; for (int i=mHistory.size()-1; i>=-1; i--) { - ActivityRecord below = i >= 0 ? (ActivityRecord)mHistory.get(i) : null; + ActivityRecord below = i >= 0 ? mHistory.get(i) : null; if (below != null && below.finishing) { continue; @@ -1700,7 +1703,7 @@ final public class ActivityStack { // bottom of the activity stack. This also keeps it // correctly ordered with any activities we previously // moved. - ActivityRecord p = (ActivityRecord)mHistory.get(0); + ActivityRecord p = mHistory.get(0); if (target.taskAffinity != null && target.taskAffinity.equals(p.task.affinity)) { // If the activity currently at the bottom has the @@ -1727,7 +1730,7 @@ final public class ActivityStack { int dstPos = 0; ThumbnailHolder curThumbHolder = target.thumbHolder; for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = (ActivityRecord)mHistory.get(srcPos); + p = mHistory.get(srcPos); if (p.finishing) { continue; } @@ -1764,7 +1767,7 @@ final public class ActivityStack { // like these are all in the reply chain. replyChainEnd = targetI+1; while (replyChainEnd < mHistory.size() && - ((ActivityRecord)mHistory.get( + (mHistory.get( replyChainEnd)).task == task) { replyChainEnd++; } @@ -1774,7 +1777,7 @@ final public class ActivityStack { } ActivityRecord p = null; for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = (ActivityRecord)mHistory.get(srcPos); + p = mHistory.get(srcPos); if (p.finishing) { continue; } @@ -1834,7 +1837,7 @@ final public class ActivityStack { } ActivityRecord p = null; for (int srcPos=targetI; srcPos<=replyChainEnd; srcPos++) { - p = (ActivityRecord)mHistory.get(srcPos); + p = mHistory.get(srcPos); if (p.finishing) { continue; } @@ -1852,7 +1855,7 @@ final public class ActivityStack { replyChainEnd = targetI; } for (int srcPos=replyChainEnd; srcPos>=targetI; srcPos--) { - ActivityRecord p = (ActivityRecord)mHistory.get(srcPos); + ActivityRecord p = mHistory.get(srcPos); if (p.finishing) { continue; } @@ -1881,7 +1884,7 @@ final public class ActivityStack { // below so it remains singleTop. if (target.info.launchMode == ActivityInfo.LAUNCH_SINGLE_TOP) { for (int j=lastReparentPos-1; j>=0; j--) { - ActivityRecord p = (ActivityRecord)mHistory.get(j); + ActivityRecord p = mHistory.get(j); if (p.finishing) { continue; } @@ -1922,7 +1925,7 @@ final public class ActivityStack { // First find the requested task. while (i > 0) { i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (r.task.taskId == taskId) { i++; break; @@ -1932,7 +1935,7 @@ final public class ActivityStack { // Now clear it. while (i > 0) { i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (r.finishing) { continue; } @@ -1944,7 +1947,7 @@ final public class ActivityStack { ActivityRecord ret = r; while (i < (mHistory.size()-1)) { i++; - r = (ActivityRecord)mHistory.get(i); + r = mHistory.get(i); if (r.task.taskId != taskId) { break; } @@ -1980,6 +1983,28 @@ final public class ActivityStack { } /** + * Completely remove all activities associated with an existing + * task starting at a specified index. + */ + private final void performClearTaskAtIndexLocked(int taskId, int i) { + while (i < (mHistory.size()-1)) { + ActivityRecord r = mHistory.get(i); + if (r.task.taskId != taskId) { + // Whoops hit the end. + return; + } + if (r.finishing) { + i++; + continue; + } + if (!finishActivityLocked(r, i, Activity.RESULT_CANCELED, + null, "clear")) { + i++; + } + } + } + + /** * Completely remove all activities associated with an existing task. */ private final void performClearTaskLocked(int taskId) { @@ -1988,37 +2013,23 @@ final public class ActivityStack { // First find the requested task. while (i > 0) { i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (r.task.taskId == taskId) { i++; break; } } - // Now clear it. + // Now find the start and clear it. while (i > 0) { i--; - ActivityRecord r = (ActivityRecord)mHistory.get(i); + ActivityRecord r = mHistory.get(i); if (r.finishing) { continue; } if (r.task.taskId != taskId) { // We hit the bottom. Now finish it all... - while (i < (mHistory.size()-1)) { - i++; - r = (ActivityRecord)mHistory.get(i); - if (r.task.taskId != taskId) { - // Whoops hit the end. - return; - } - if (r.finishing) { - continue; - } - if (finishActivityLocked(r, i, Activity.RESULT_CANCELED, - null, "clear")) { - i--; - } - } + performClearTaskAtIndexLocked(taskId, i+1); return; } } @@ -2032,7 +2043,7 @@ final public class ActivityStack { int i = mHistory.size(); while (i > 0) { i--; - ActivityRecord candidate = (ActivityRecord)mHistory.get(i); + ActivityRecord candidate = mHistory.get(i); if (candidate.task.taskId != task) { break; } @@ -2049,9 +2060,9 @@ final public class ActivityStack { * brought to the front. */ private final ActivityRecord moveActivityToFrontLocked(int where) { - ActivityRecord newTop = (ActivityRecord)mHistory.remove(where); + ActivityRecord newTop = mHistory.remove(where); int top = mHistory.size(); - ActivityRecord oldTop = (ActivityRecord)mHistory.get(top-1); + ActivityRecord oldTop = mHistory.get(top-1); mHistory.add(top, newTop); oldTop.frontOfTask = false; newTop.frontOfTask = true; @@ -2094,7 +2105,7 @@ final public class ActivityStack { if (DEBUG_RESULTS) Slog.v( TAG, "Sending result to " + resultTo + " (index " + index + ")"); if (index >= 0) { - sourceRecord = (ActivityRecord)mHistory.get(index); + sourceRecord = mHistory.get(index); if (requestCode >= 0 && !sourceRecord.finishing) { resultRecord = sourceRecord; } @@ -2576,7 +2587,7 @@ final public class ActivityStack { // this case should never happen. final int N = mHistory.size(); ActivityRecord prev = - N > 0 ? (ActivityRecord)mHistory.get(N-1) : null; + N > 0 ? mHistory.get(N-1) : null; r.setTask(prev != null ? prev.task : new TaskRecord(mService.mCurTask, r.info, intent), null, true); @@ -3021,7 +3032,7 @@ final public class ActivityStack { // Get the activity record. int index = indexOfTokenLocked(token); if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = mHistory.get(index); if (fromTimeout) { reportActivityLaunchedLocked(fromTimeout, r, -1, -1); @@ -3153,12 +3164,12 @@ final public class ActivityStack { if (index < 0) { return false; } - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = mHistory.get(index); // Is this the last activity left? boolean lastActivity = true; for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord p = (ActivityRecord)mHistory.get(i); + ActivityRecord p = mHistory.get(i); if (!p.finishing && p != r) { lastActivity = false; break; @@ -3193,7 +3204,7 @@ final public class ActivityStack { System.identityHashCode(r), r.task.taskId, r.shortComponentName, reason); if (index < (mHistory.size()-1)) { - ActivityRecord next = (ActivityRecord)mHistory.get(index+1); + ActivityRecord next = mHistory.get(index+1); if (next.task == r.task) { if (r.frontOfTask) { // The next activity is now the front of the task. @@ -3249,7 +3260,7 @@ final public class ActivityStack { if (mResumedActivity == r) { boolean endTask = index <= 0 - || ((ActivityRecord)mHistory.get(index-1)).task != r.task; + || (mHistory.get(index-1)).task != r.task; if (DEBUG_TRANSITION) Slog.v(TAG, "Prepare close transition: finishing " + r); mService.mWindowManager.prepareAppTransition(endTask @@ -3391,6 +3402,7 @@ final public class ActivityStack { // Get rid of any pending idle timeouts. mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); mHandler.removeMessages(IDLE_TIMEOUT_MSG, r); + mHandler.removeMessages(DESTROY_TIMEOUT_MSG, r); } private final void removeActivityFromHistoryLocked(ActivityRecord r) { @@ -3516,7 +3528,7 @@ final public class ActivityStack { int index = indexOfTokenLocked(token); if (index >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(index); + ActivityRecord r = mHistory.get(index); if (r.state == ActivityState.DESTROYING) { final long origId = Binder.clearCallingIdentity(); removeActivityFromHistoryLocked(r); @@ -3558,7 +3570,7 @@ final public class ActivityStack { final void moveHomeToFrontLocked() { TaskRecord homeTask = null; for (int i=mHistory.size()-1; i>=0; i--) { - ActivityRecord hr = (ActivityRecord)mHistory.get(i); + ActivityRecord hr = mHistory.get(i); if (hr.isHomeActivity) { homeTask = hr.task; break; @@ -3576,7 +3588,7 @@ final public class ActivityStack { final int task = tr.taskId; int top = mHistory.size()-1; - if (top < 0 || ((ActivityRecord)mHistory.get(top)).task.taskId == task) { + if (top < 0 || (mHistory.get(top)).task.taskId == task) { // nothing to do! return; } @@ -3591,7 +3603,7 @@ final public class ActivityStack { // Shift all activities with this task up to the top // of the stack, keeping them in the same internal order. while (pos >= 0) { - ActivityRecord r = (ActivityRecord)mHistory.get(pos); + ActivityRecord r = mHistory.get(pos); if (localLOGV) Slog.v( TAG, "At " + pos + " ckp " + r.task + ": " + r); if (r.task.taskId == task) { @@ -3680,7 +3692,7 @@ final public class ActivityStack { // Shift all activities with this task down to the bottom // of the stack, keeping them in the same internal order. while (pos < N) { - ActivityRecord r = (ActivityRecord)mHistory.get(pos); + ActivityRecord r = mHistory.get(pos); if (localLOGV) Slog.v( TAG, "At " + pos + " ckp " + r.task + ": " + r); if (r.task.taskId == task) { @@ -3714,6 +3726,106 @@ final public class ActivityStack { return true; } + public ActivityManager.TaskThumbnails getTaskThumbnailsLocked(TaskRecord tr) { + TaskAccessInfo info = getTaskAccessInfoLocked(tr.taskId, true); + ActivityRecord resumed = mResumedActivity; + if (resumed != null && resumed.thumbHolder == tr) { + info.mainThumbnail = resumed.stack.screenshotActivities(resumed); + } else { + info.mainThumbnail = tr.lastThumbnail; + } + return info; + } + + public ActivityRecord removeTaskActivitiesLocked(int taskId, int subTaskIndex) { + TaskAccessInfo info = getTaskAccessInfoLocked(taskId, false); + if (info.root == null) { + Slog.w(TAG, "removeTaskLocked: unknown taskId " + taskId); + return null; + } + + if (subTaskIndex < 0) { + // Just remove the entire task. + performClearTaskAtIndexLocked(taskId, info.rootIndex); + return info.root; + } + + if (subTaskIndex >= info.subtasks.size()) { + Slog.w(TAG, "removeTaskLocked: unknown subTaskIndex " + subTaskIndex); + return null; + } + + // Remove all of this task's activies starting at the sub task. + TaskAccessInfo.SubTask subtask = info.subtasks.get(subTaskIndex); + performClearTaskAtIndexLocked(taskId, subtask.index); + return subtask.activity; + } + + public TaskAccessInfo getTaskAccessInfoLocked(int taskId, boolean inclThumbs) { + ActivityRecord resumed = mResumedActivity; + final TaskAccessInfo thumbs = new TaskAccessInfo(); + // How many different sub-thumbnails? + final int NA = mHistory.size(); + int j = 0; + ThumbnailHolder holder = null; + while (j < NA) { + ActivityRecord ar = mHistory.get(j); + if (!ar.finishing && ar.task.taskId == taskId) { + holder = ar.thumbHolder; + break; + } + j++; + } + + if (j >= NA) { + return thumbs; + } + + thumbs.root = mHistory.get(j); + thumbs.rootIndex = j; + + ArrayList<TaskAccessInfo.SubTask> subtasks = new ArrayList<TaskAccessInfo.SubTask>(); + thumbs.subtasks = subtasks; + ActivityRecord lastActivity = null; + while (j < NA) { + ActivityRecord ar = mHistory.get(j); + j++; + if (ar.finishing) { + continue; + } + if (ar.task.taskId != taskId) { + break; + } + lastActivity = ar; + if (ar.thumbHolder != holder && holder != null) { + thumbs.numSubThumbbails++; + holder = ar.thumbHolder; + TaskAccessInfo.SubTask sub = new TaskAccessInfo.SubTask(); + sub.thumbnail = holder.lastThumbnail; + sub.activity = ar; + sub.index = j-1; + subtasks.add(sub); + } + } + if (lastActivity != null && subtasks.size() > 0) { + if (resumed == lastActivity) { + TaskAccessInfo.SubTask sub = subtasks.get(subtasks.size()-1); + sub.thumbnail = lastActivity.stack.screenshotActivities(lastActivity); + } + } + if (thumbs.numSubThumbbails > 0) { + thumbs.retriever = new IThumbnailRetriever.Stub() { + public Bitmap getThumbnail(int index) { + if (index < 0 || index >= thumbs.subtasks.size()) { + return null; + } + return thumbs.subtasks.get(index).thumbnail; + } + }; + } + return thumbs; + } + private final void logStartActivity(int tag, ActivityRecord r, TaskRecord task) { EventLog.writeEvent(tag, diff --git a/services/java/com/android/server/am/BatteryStatsService.java b/services/java/com/android/server/am/BatteryStatsService.java index 963a691..b4fdc9f 100644 --- a/services/java/com/android/server/am/BatteryStatsService.java +++ b/services/java/com/android/server/am/BatteryStatsService.java @@ -449,6 +449,7 @@ public final class BatteryStatsService extends IBatteryStats.Stub { @Override protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { boolean isCheckin = false; + boolean noOutput = false; if (args != null) { for (String arg : args) { if ("--checkin".equals(arg)) { @@ -457,10 +458,22 @@ public final class BatteryStatsService extends IBatteryStats.Stub { synchronized (mStats) { mStats.resetAllStatsLocked(); pw.println("Battery stats reset."); + noOutput = true; } + } else if ("--write".equals(arg)) { + synchronized (mStats) { + mStats.writeSyncLocked(); + pw.println("Battery stats written."); + noOutput = true; + } + } else { + pw.println("Unknown option: " + arg); } } } + if (noOutput) { + return; + } if (isCheckin) { List<ApplicationInfo> apps = mContext.getPackageManager().getInstalledApplications(0); synchronized (mStats) { diff --git a/services/java/com/android/server/am/ProcessRecord.java b/services/java/com/android/server/am/ProcessRecord.java index 353ff6d..e39c239 100644 --- a/services/java/com/android/server/am/ProcessRecord.java +++ b/services/java/com/android/server/am/ProcessRecord.java @@ -65,6 +65,7 @@ class ProcessRecord { boolean foregroundServices; // Running any services that are foreground? boolean bad; // True if disabled in the bad process list boolean killedBackground; // True when proc has been killed due to too many bg + String waitingToKill; // Process is waiting to be killed when in the bg; reason IBinder forcingToForeground;// Token that is forcing this process to be foreground int adjSeq; // Sequence id for identifying oom_adj assignment cycles int lruSeq; // Sequence id for identifying LRU update cycles @@ -202,8 +203,9 @@ class ProcessRecord { pw.print(" lastLowMemory="); TimeUtils.formatDuration(lastLowMemory, now, pw); pw.print(" reportLowMemory="); pw.println(reportLowMemory); - if (killedBackground) { - pw.print(prefix); pw.print("killedBackground="); pw.println(killedBackground); + if (killedBackground || waitingToKill != null) { + pw.print(prefix); pw.print("killedBackground="); pw.print(killedBackground); + pw.print(" waitingToKill="); pw.println(waitingToKill); } if (debugging || crashing || crashDialog != null || notResponding || anrDialog != null || bad) { diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java index 1a617dd..da00c0c 100644 --- a/services/java/com/android/server/am/ServiceRecord.java +++ b/services/java/com/android/server/am/ServiceRecord.java @@ -84,7 +84,6 @@ class ServiceRecord extends Binder { boolean startRequested; // someone explicitly called start? boolean stopIfKilled; // last onStart() said to stop if service killed? boolean callStart; // last onStart() has asked to alway be called on restart. - int lastStartId; // identifier of most recent start request. int executeNesting; // number of outstanding operations keeping foreground. long executingStart; // start time of last execute request. int crashCount; // number of times proc has crashed with service running @@ -96,8 +95,11 @@ class ServiceRecord extends Binder { String stringName; // caching of toString + private int lastStartId; // identifier of most recent start request. + static class StartItem { final ServiceRecord sr; + final boolean taskRemoved; final int id; final Intent intent; final int targetPermissionUid; @@ -108,8 +110,10 @@ class ServiceRecord extends Binder { String stringName; // caching of toString - StartItem(ServiceRecord _sr, int _id, Intent _intent, int _targetPermissionUid) { + StartItem(ServiceRecord _sr, boolean _taskRemoved, int _id, Intent _intent, + int _targetPermissionUid) { sr = _sr; + taskRemoved = _taskRemoved; id = _id; intent = _intent; targetPermissionUid = _targetPermissionUid; @@ -321,6 +325,18 @@ class ServiceRecord extends Binder { return null; } + public int getLastStartId() { + return lastStartId; + } + + public int makeNextStartId() { + lastStartId++; + if (lastStartId < 1) { + lastStartId = 1; + } + return lastStartId; + } + public void postNotification() { final int appUid = appInfo.uid; final int appPid = app.pid; diff --git a/services/java/com/android/server/am/TaskAccessInfo.java b/services/java/com/android/server/am/TaskAccessInfo.java new file mode 100644 index 0000000..5618c1a --- /dev/null +++ b/services/java/com/android/server/am/TaskAccessInfo.java @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2011 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.am; + +import java.util.ArrayList; + +import android.app.ActivityManager.TaskThumbnails; +import android.graphics.Bitmap; + +final class TaskAccessInfo extends TaskThumbnails { + final static class SubTask { + Bitmap thumbnail; + ActivityRecord activity; + int index; + } + + public ActivityRecord root; + public int rootIndex; + + public ArrayList<SubTask> subtasks; +} diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index 11dde75..16b55c3 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -1336,6 +1336,19 @@ final class Settings { } mPendingPackages.clear(); + /* + * Make sure all the updated system packages have their shared users + * associated with them. + */ + final Iterator<PackageSetting> disabledIt = mDisabledSysPackages.values().iterator(); + while (disabledIt.hasNext()) { + final PackageSetting disabledPs = disabledIt.next(); + final Object id = getUserIdLPr(disabledPs.userId); + if (id != null && id instanceof SharedUserSetting) { + disabledPs.sharedUser = (SharedUserSetting) id; + } + } + readStoppedLPw(); mReadMessages.append("Read completed successfully: " + mPackages.size() + " packages, " diff --git a/services/java/com/android/server/pm/UserDetails.java b/services/java/com/android/server/pm/UserDetails.java new file mode 100644 index 0000000..2aeed7c --- /dev/null +++ b/services/java/com/android/server/pm/UserDetails.java @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2011 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.pm; + +import com.android.internal.util.FastXmlSerializer; + +import android.content.pm.UserInfo; +import android.os.Environment; +import android.os.FileUtils; +import android.util.Slog; +import android.util.SparseArray; +import android.util.Xml; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlSerializer; + +public class UserDetails { + private static final String TAG_NAME = "name"; + + private static final String ATTR_FLAGS = "flags"; + + private static final String ATTR_ID = "id"; + + private static final String TAG_USERS = "users"; + + private static final String TAG_USER = "user"; + + private static final String TAG = "UserDetails"; + + private static final String USER_INFO_DIR = "system/users"; + private static final String USER_LIST_FILENAME = "userlist.xml"; + + private SparseArray<UserInfo> mUsers; + + private final File mUsersDir; + private final File mUserListFile; + + /** + * Available for testing purposes. + */ + UserDetails(File dataDir) { + mUsersDir = new File(dataDir, USER_INFO_DIR); + mUsersDir.mkdirs(); + FileUtils.setPermissions(mUsersDir.toString(), + FileUtils.S_IRWXU|FileUtils.S_IRWXG + |FileUtils.S_IROTH|FileUtils.S_IXOTH, + -1, -1); + mUserListFile = new File(mUsersDir, USER_LIST_FILENAME); + readUserList(); + } + + public UserDetails() { + this(Environment.getDataDirectory()); + } + + public List<UserInfo> getUsers() { + ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size()); + for (int i = 0; i < mUsers.size(); i++) { + users.add(mUsers.valueAt(i)); + } + return users; + } + + private void readUserList() { + mUsers = new SparseArray<UserInfo>(); + if (!mUserListFile.exists()) { + fallbackToSingleUser(); + return; + } + FileInputStream fis = null; + try { + fis = new FileInputStream(mUserListFile); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + Slog.e(TAG, "Unable to read user list"); + fallbackToSingleUser(); + return; + } + + while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { + String id = parser.getAttributeValue(null, ATTR_ID); + UserInfo user = readUser(Integer.parseInt(id)); + if (user != null) { + mUsers.put(user.id, user); + } + } + } + } catch (IOException ioe) { + fallbackToSingleUser(); + } catch (XmlPullParserException pe) { + fallbackToSingleUser(); + } + } + + private void fallbackToSingleUser() { + // Create the primary user + UserInfo primary = new UserInfo(0, "Primary", + UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); + mUsers.put(0, primary); + + writeUserList(); + writeUser(primary); + } + + /* + * Writes the user file in this format: + * + * <user flags="20039023" id="0"> + * <name>Primary</name> + * </user> + */ + private void writeUser(UserInfo userInfo) { + try { + final File mUserFile = new File(mUsersDir, userInfo.id + ".xml"); + final FileOutputStream fos = new FileOutputStream(mUserFile); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(bos, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); + serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); + + serializer.startTag(null, TAG_NAME); + serializer.text(userInfo.name); + serializer.endTag(null, TAG_NAME); + + serializer.endTag(null, TAG_USER); + + serializer.endDocument(); + } catch (IOException ioe) { + Slog.e(TAG, "Error writing user info " + userInfo.id + "\n" + ioe); + } + } + + /* + * Writes the user list file in this format: + * + * <users> + * <user id="0"></user> + * <user id="2"></user> + * </users> + */ + private void writeUserList() { + try { + final FileOutputStream fos = new FileOutputStream(mUserListFile); + final BufferedOutputStream bos = new BufferedOutputStream(fos); + + // XmlSerializer serializer = XmlUtils.serializerInstance(); + final XmlSerializer serializer = new FastXmlSerializer(); + serializer.setOutput(bos, "utf-8"); + serializer.startDocument(null, true); + serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + serializer.startTag(null, TAG_USERS); + + for (int i = 0; i < mUsers.size(); i++) { + UserInfo user = mUsers.valueAt(i); + serializer.startTag(null, TAG_USER); + serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); + serializer.endTag(null, TAG_USER); + Slog.e(TAG, "Wrote user " + user.id + " to userlist.xml"); + } + + serializer.endTag(null, TAG_USERS); + + serializer.endDocument(); + } catch (IOException ioe) { + Slog.e(TAG, "Error writing user list"); + } + } + + private UserInfo readUser(int id) { + int flags = 0; + String name = null; + + FileInputStream fis = null; + try { + File userFile = new File(mUsersDir, Integer.toString(id) + ".xml"); + fis = new FileInputStream(userFile); + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(fis, null); + int type; + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + ; + } + + if (type != XmlPullParser.START_TAG) { + Slog.e(TAG, "Unable to read user " + id); + return null; + } + + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { + String storedId = parser.getAttributeValue(null, ATTR_ID); + if (Integer.parseInt(storedId) != id) { + Slog.e(TAG, "User id does not match the file name"); + return null; + } + String flagString = parser.getAttributeValue(null, ATTR_FLAGS); + flags = Integer.parseInt(flagString); + + while ((type = parser.next()) != XmlPullParser.START_TAG + && type != XmlPullParser.END_DOCUMENT) { + } + if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) { + type = parser.next(); + if (type == XmlPullParser.TEXT) { + name = parser.getText(); + } + } + } + fis.close(); + + UserInfo userInfo = new UserInfo(id, name, flags); + return userInfo; + + } catch (IOException ioe) { + } catch (XmlPullParserException pe) { + } + return null; + } + + public UserInfo createUser(String name, int flags) { + int id = getNextAvailableId(); + UserInfo userInfo = new UserInfo(id, name, flags); + if (!createPackageFolders(id)) { + return null; + } + mUsers.put(id, userInfo); + writeUserList(); + writeUser(userInfo); + return userInfo; + } + + public void removeUser(int id) { + // Remove from the list + UserInfo userInfo = mUsers.get(id); + if (userInfo != null) { + // Remove this user from the list + mUsers.remove(id); + // Remove user file + File userFile = new File(mUsersDir, id + ".xml"); + userFile.delete(); + writeUserList(); + removePackageFolders(id); + } + } + + private int getNextAvailableId() { + int i = 0; + while (i < Integer.MAX_VALUE) { + if (mUsers.indexOfKey(i) < 0) { + break; + } + i++; + } + return i; + } + + private boolean createPackageFolders(int id) { + // TODO: Create data directories for all the packages for a new user, w/ specified user id. + return true; + } + + private boolean removePackageFolders(int id) { + // TODO: Remove all the data directories for the specified user. + return true; + } +} diff --git a/services/jni/com_android_server_AlarmManagerService.cpp b/services/jni/com_android_server_AlarmManagerService.cpp index aa8c9b3..c9a702a 100644 --- a/services/jni/com_android_server_AlarmManagerService.cpp +++ b/services/jni/com_android_server_AlarmManagerService.cpp @@ -2,16 +2,16 @@ ** ** Copyright 2006, 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 +** 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 +** 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 +** 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. */ @@ -84,7 +84,7 @@ static void android_server_AlarmManagerService_set(JNIEnv* env, jobject obj, jin struct timespec ts; ts.tv_sec = seconds; ts.tv_nsec = nanoseconds; - + int result = ioctl(fd, ANDROID_ALARM_SET(type), &ts); if (result < 0) { @@ -97,18 +97,18 @@ static jint android_server_AlarmManagerService_waitForAlarm(JNIEnv* env, jobject { #ifdef HAVE_ANDROID_OS int result = 0; - + do { result = ioctl(fd, ANDROID_ALARM_WAIT); } while (result < 0 && errno == EINTR); - + if (result < 0) { LOGE("Unable to wait on alarm: %s\n", strerror(errno)); return 0; } - + return result; #endif } @@ -124,14 +124,6 @@ static JNINativeMethod sMethods[] = { int register_android_server_AlarmManagerService(JNIEnv* env) { - jclass clazz = env->FindClass("com/android/server/AlarmManagerService"); - - if (clazz == NULL) - { - LOGE("Can't find com/android/server/AlarmManagerService"); - return -1; - } - return jniRegisterNativeMethods(env, "com/android/server/AlarmManagerService", sMethods, NELEM(sMethods)); } diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index ab2c125..aaa305e 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -36,6 +36,8 @@ #include <input/InputManager.h> #include <input/PointerController.h> +#include <input/SpotController.h> +#include <input/SpriteController.h> #include <android_os_MessageQueue.h> #include <android_view_KeyEvent.h> @@ -163,6 +165,7 @@ public: virtual nsecs_t getVirtualKeyQuietTime(); virtual void getExcludedDeviceNames(Vector<String8>& outExcludedDeviceNames); virtual sp<PointerControllerInterface> obtainPointerController(int32_t deviceId); + virtual sp<SpotControllerInterface> obtainSpotController(int32_t deviceId); /* --- InputDispatcherPolicyInterface implementation --- */ @@ -213,12 +216,16 @@ private: // System UI visibility. int32_t systemUiVisibility; + // Sprite controller singleton, created on first use. + sp<SpriteController> spriteController; + // Pointer controller singleton, created and destroyed as needed. wp<PointerController> pointerController; } mLocked; void updateInactivityFadeDelayLocked(const sp<PointerController>& controller); void handleInterceptActions(jint wmActions, nsecs_t when, uint32_t& policyFlags); + void ensureSpriteControllerLocked(); // Power manager interactions. bool isScreenOn(); @@ -419,18 +426,15 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 sp<PointerController> controller = mLocked.pointerController.promote(); if (controller == NULL) { - JNIEnv* env = jniEnv(); - jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer); - if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) { - layer = -1; - } + ensureSpriteControllerLocked(); - controller = new PointerController(mLooper, layer); + controller = new PointerController(mLooper, mLocked.spriteController); mLocked.pointerController = controller; controller->setDisplaySize(mLocked.displayWidth, mLocked.displayHeight); controller->setDisplayOrientation(mLocked.displayOrientation); + JNIEnv* env = jniEnv(); jobject iconObj = env->CallObjectMethod(mCallbacksObj, gCallbacksClassInfo.getPointerIcon); if (!checkAndClearExceptionFromCallback(env, "getPointerIcon") && iconObj) { jfloat iconHotSpotX = env->GetFloatField(iconObj, gPointerIconClassInfo.hotSpotX); @@ -451,6 +455,24 @@ sp<PointerControllerInterface> NativeInputManager::obtainPointerController(int32 return controller; } +sp<SpotControllerInterface> NativeInputManager::obtainSpotController(int32_t deviceId) { + AutoMutex _l(mLock); + + ensureSpriteControllerLocked(); + return new SpotController(mLooper, mLocked.spriteController); +} + +void NativeInputManager::ensureSpriteControllerLocked() { + if (mLocked.spriteController == NULL) { + JNIEnv* env = jniEnv(); + jint layer = env->CallIntMethod(mCallbacksObj, gCallbacksClassInfo.getPointerLayer); + if (checkAndClearExceptionFromCallback(env, "getPointerLayer")) { + layer = -1; + } + mLocked.spriteController = new SpriteController(mLooper, layer); + } +} + void NativeInputManager::notifySwitch(nsecs_t when, int32_t switchCode, int32_t switchValue, uint32_t policyFlags) { #if DEBUG_INPUT_DISPATCHER_POLICY diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp index 00ee7e3..9cd04f6 100644 --- a/services/jni/com_android_server_UsbService.cpp +++ b/services/jni/com_android_server_UsbService.cpp @@ -37,13 +37,6 @@ namespace android { -static struct file_descriptor_offsets_t -{ - jclass mClass; - jmethodID mConstructor; - jfieldID mDescriptor; -} gFileDescriptorOffsets; - static struct parcel_file_descriptor_offsets_t { jclass mClass; @@ -167,11 +160,8 @@ static jobject android_server_UsbService_openDevice(JNIEnv *env, jobject thiz, j int newFD = dup(fd); usb_device_close(device); - jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, - gFileDescriptorOffsets.mConstructor); - if (fileDescriptor != NULL) { - env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, newFD); - } else { + jobject fileDescriptor = jniCreateFileDescriptor(env, newFD); + if (fileDescriptor == NULL) { return NULL; } return env->NewObject(gParcelFileDescriptorOffsets.mClass, @@ -221,11 +211,8 @@ static jobject android_server_UsbService_openAccessory(JNIEnv *env, jobject thiz LOGE("could not open %s", DRIVER_NAME); return NULL; } - jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass, - gFileDescriptorOffsets.mConstructor); - if (fileDescriptor != NULL) { - env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd); - } else { + jobject fileDescriptor = jniCreateFileDescriptor(env, fd); + if (fileDescriptor == NULL) { return NULL; } return env->NewObject(gParcelFileDescriptorOffsets.mClass, @@ -260,14 +247,6 @@ int register_android_server_UsbService(JNIEnv *env) return -1; } - clazz = env->FindClass("java/io/FileDescriptor"); - LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor"); - gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); - gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V"); - gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I"); - LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL, - "Unable to find descriptor field in java.io.FileDescriptor"); - clazz = env->FindClass("android/os/ParcelFileDescriptor"); LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor"); gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz); diff --git a/services/tests/servicestests/AndroidManifest.xml b/services/tests/servicestests/AndroidManifest.xml index f115f42..2fcce78 100644 --- a/services/tests/servicestests/AndroidManifest.xml +++ b/services/tests/servicestests/AndroidManifest.xml @@ -15,7 +15,7 @@ --> <manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.frameworks.servicestests"> + package="com.android.frameworks.servicestests"> <uses-permission android:name="android.permission.READ_LOGS" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> diff --git a/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java b/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java new file mode 100644 index 0000000..7b77aac --- /dev/null +++ b/services/tests/servicestests/src/com/android/server/pm/UserDetailsTest.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2011 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.pm; + +import com.android.server.pm.UserDetails; + +import android.content.pm.UserInfo; +import android.os.Debug; +import android.os.Environment; +import android.test.AndroidTestCase; + +import java.util.List; + +/** Test {@link UserDetails} functionality. */ +public class UserDetailsTest extends AndroidTestCase { + + UserDetails mDetails = null; + + @Override + public void setUp() throws Exception { + mDetails = new UserDetails(Environment.getExternalStorageDirectory()); + } + + @Override + public void tearDown() throws Exception { + List<UserInfo> users = mDetails.getUsers(); + // Remove all except the primary user + for (UserInfo user : users) { + if (!user.isPrimary()) { + mDetails.removeUser(user.id); + } + } + } + + public void testHasPrimary() throws Exception { + assertTrue(findUser(0)); + } + + public void testAddUser() throws Exception { + final UserDetails details = mDetails; + + UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST); + assertTrue(userInfo != null); + + List<UserInfo> list = details.getUsers(); + boolean found = false; + for (UserInfo user : list) { + if (user.id == userInfo.id && user.name.equals("Guest 1") + && user.isGuest() + && !user.isAdmin() + && !user.isPrimary()) { + found = true; + } + } + assertTrue(found); + } + + public void testAdd2Users() throws Exception { + final UserDetails details = mDetails; + + UserInfo user1 = details.createUser("Guest 1", UserInfo.FLAG_GUEST); + UserInfo user2 = details.createUser("User 2", UserInfo.FLAG_ADMIN); + + assertTrue(user1 != null); + assertTrue(user2 != null); + + assertTrue(findUser(0)); + assertTrue(findUser(user1.id)); + assertTrue(findUser(user2.id)); + } + + public void testRemoveUser() throws Exception { + final UserDetails details = mDetails; + + UserInfo userInfo = details.createUser("Guest 1", UserInfo.FLAG_GUEST); + + details.removeUser(userInfo.id); + + assertFalse(findUser(userInfo.id)); + } + + private boolean findUser(int id) { + List<UserInfo> list = mDetails.getUsers(); + + for (UserInfo user : list) { + if (user.id == id) { + return true; + } + } + return false; + } +} |