diff options
Diffstat (limited to 'libs')
| -rw-r--r-- | libs/hwui/FontRenderer.cpp | 24 | ||||
| -rw-r--r-- | libs/hwui/FontRenderer.h | 3 | ||||
| -rw-r--r-- | libs/hwui/Layer.h | 13 | ||||
| -rw-r--r-- | libs/hwui/LayerCache.cpp | 24 | ||||
| -rw-r--r-- | libs/hwui/LayerCache.h | 4 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.cpp | 226 | ||||
| -rw-r--r-- | libs/hwui/OpenGLRenderer.h | 26 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.cpp | 5 | ||||
| -rw-r--r-- | libs/hwui/ProgramCache.h | 13 | ||||
| -rw-r--r-- | libs/hwui/Snapshot.h | 74 | ||||
| -rw-r--r-- | libs/ui/InputDispatcher.cpp | 2162 | ||||
| -rw-r--r-- | libs/ui/InputManager.cpp | 48 | ||||
| -rw-r--r-- | libs/ui/InputReader.cpp | 165 | ||||
| -rw-r--r-- | libs/utils/PollLoop.cpp | 8 |
14 files changed, 2115 insertions, 680 deletions
diff --git a/libs/hwui/FontRenderer.cpp b/libs/hwui/FontRenderer.cpp index 5d7f8bf..2959814 100644 --- a/libs/hwui/FontRenderer.cpp +++ b/libs/hwui/FontRenderer.cpp @@ -166,6 +166,8 @@ void Font::measureUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t renderUTF(paint, text, start, len, numGlyphs, 0, 0, MEASURE, NULL, 0, 0, bounds); } +#define SkAutoKern_AdjustF(prev, next) (((next) - (prev) + 32) >> 6 << 16) + void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t len, int numGlyphs, int x, int y, RenderMode mode, uint8_t *bitmap, uint32_t bitmapW, uint32_t bitmapH,Rect *bounds) { @@ -173,12 +175,16 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t return; } - int penX = x, penY = y; + SkFixed penX = SkIntToFixed(x); + int penY = y; int glyphsLeft = 1; if (numGlyphs > 0) { glyphsLeft = numGlyphs; } + SkFixed prevRsbDelta = 0; + penX += SK_Fixed1 / 2; + text += start; while (glyphsLeft > 0) { @@ -190,23 +196,25 @@ void Font::renderUTF(SkPaint* paint, const char* text, uint32_t start, uint32_t } CachedGlyphInfo* cachedGlyph = getCachedUTFChar(paint, utfChar); + penX += SkAutoKern_AdjustF(prevRsbDelta, cachedGlyph->mLsbDelta); + prevRsbDelta = cachedGlyph->mRsbDelta; // If it's still not valid, we couldn't cache it, so we shouldn't draw garbage if (cachedGlyph->mIsValid) { switch(mode) { case FRAMEBUFFER: - drawCachedGlyph(cachedGlyph, penX, penY); + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY); break; case BITMAP: - drawCachedGlyph(cachedGlyph, penX, penY, bitmap, bitmapW, bitmapH); + drawCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bitmap, bitmapW, bitmapH); break; case MEASURE: - measureCachedGlyph(cachedGlyph, penX, penY, bounds); + measureCachedGlyph(cachedGlyph, SkFixedFloor(penX), penY, bounds); break; } } - penX += SkFixedFloor(cachedGlyph->mAdvanceX); + penX += cachedGlyph->mAdvanceX; // If we were given a specific number of glyphs, decrement if (numGlyphs > 0) { @@ -220,6 +228,8 @@ void Font::updateGlyphCache(SkPaint* paint, const SkGlyph& skiaGlyph, CachedGlyp glyph->mAdvanceY = skiaGlyph.fAdvanceY; glyph->mBitmapLeft = skiaGlyph.fLeft; glyph->mBitmapTop = skiaGlyph.fTop; + glyph->mLsbDelta = skiaGlyph.fLsbDelta; + glyph->mRsbDelta = skiaGlyph.fRsbDelta; uint32_t startX = 0; uint32_t startY = 0; @@ -352,7 +362,7 @@ void FontRenderer::flushAllAndInvalidate() { bool FontRenderer::cacheBitmap(const SkGlyph& glyph, uint32_t* retOriginX, uint32_t* retOriginY) { // If the glyph is too tall, don't cache it - if (glyph.fWidth > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { + if (glyph.fHeight > mCacheLines[mCacheLines.size() - 1]->mMaxHeight) { LOGE("Font size to large to fit in cache. width, height = %i, %i", (int) glyph.fWidth, (int) glyph.fHeight); return false; @@ -616,7 +626,7 @@ void FontRenderer::setFont(SkPaint* paint, uint32_t fontId, float fontSize) { const float maxPrecacheFontSize = 40.0f; bool isNewFont = currentNumFonts != mActiveFonts.size(); - if(isNewFont && fontSize <= maxPrecacheFontSize ){ + if (isNewFont && fontSize <= maxPrecacheFontSize) { precacheLatin(paint); } } diff --git a/libs/hwui/FontRenderer.h b/libs/hwui/FontRenderer.h index a03ea92..de5c019 100644 --- a/libs/hwui/FontRenderer.h +++ b/libs/hwui/FontRenderer.h @@ -94,6 +94,9 @@ protected: // Values below contain a glyph's origin in the bitmap int32_t mBitmapLeft; int32_t mBitmapTop; + // Auto-kerning + SkFixed mLsbDelta; + SkFixed mRsbDelta; }; Font(FontRenderer* state, uint32_t fontId, float fontSize); diff --git a/libs/hwui/Layer.h b/libs/hwui/Layer.h index d4db782..c527038 100644 --- a/libs/hwui/Layer.h +++ b/libs/hwui/Layer.h @@ -63,28 +63,19 @@ struct LayerSize { */ struct Layer { /** - * Coordinates of the layer corresponding to this snapshot. - * Only set when the flag kFlagIsLayer is set. + * Coordinates of the layer. */ Rect layer; /** * Name of the texture used to render the layer. - * Only set when the flag kFlagIsLayer is set. */ GLuint texture; /** - * Name of the FBO used to render the layer. - * Only set when the flag kFlagIsLayer is set. - */ - GLuint fbo; - /** * Opacity of the layer. - * Only set when the flag kFlagIsLayer is set. */ - float alpha; + int alpha; /** * Blending mode of the layer. - * Only set when the flag kFlagIsLayer is set. */ SkXfermode::Mode mode; /** diff --git a/libs/hwui/LayerCache.cpp b/libs/hwui/LayerCache.cpp index a204778..1a18766 100644 --- a/libs/hwui/LayerCache.cpp +++ b/libs/hwui/LayerCache.cpp @@ -87,7 +87,6 @@ void LayerCache::deleteLayer(Layer* layer) { if (layer) { mSize -= layer->layer.getWidth() * layer->layer.getHeight() * 4; - glDeleteFramebuffers(1, &layer->fbo); glDeleteTextures(1, &layer->texture); delete layer; } @@ -99,7 +98,7 @@ void LayerCache::clear() { mCache.setOnEntryRemovedListener(NULL); } -Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) { +Layer* LayerCache::get(LayerSize& size) { Layer* layer = mCache.remove(size); if (layer) { LAYER_LOGD("Reusing layer"); @@ -111,10 +110,6 @@ Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) { layer = new Layer; layer->blend = true; - // Generate the FBO and attach the texture - glGenFramebuffers(1, &layer->fbo); - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - // Generate the texture in which the FBO will draw glGenTextures(1, &layer->texture); glBindTexture(GL_TEXTURE_2D, layer->texture); @@ -128,23 +123,6 @@ Layer* LayerCache::get(LayerSize& size, GLuint previousFbo) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL); - - // Bind texture to FBO - glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, - layer->texture, 0); - - GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); - if (status != GL_FRAMEBUFFER_COMPLETE) { - LOGE("Framebuffer incomplete (GL error code 0x%x)", status); - - glBindFramebuffer(GL_FRAMEBUFFER, previousFbo); - - glDeleteFramebuffers(1, &layer->fbo); - glDeleteTextures(1, &layer->texture); - delete layer; - - return NULL; - } } return layer; diff --git a/libs/hwui/LayerCache.h b/libs/hwui/LayerCache.h index 9860994..c0c7542 100644 --- a/libs/hwui/LayerCache.h +++ b/libs/hwui/LayerCache.h @@ -62,10 +62,8 @@ public: * size of the cache goes down. * * @param size The dimensions of the desired layer - * @param previousFbo The name of the FBO to bind to if creating a new - * layer fails */ - Layer* get(LayerSize& size, GLuint previousFbo); + Layer* get(LayerSize& size); /** * Adds the layer to the cache. The layer will not be added if there is * not enough space available. diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 6c90704..b1f5f6b 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -80,6 +80,24 @@ static const Blender gBlends[] = { { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } }; +// This array contains the swapped version of each SkXfermode. For instance +// this array's SrcOver blending mode is actually DstOver. You can refer to +// createLayer() for more information on the purpose of this array. +static const Blender gBlendsSwap[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA } +}; + static const GLenum gTextureUnits[] = { GL_TEXTURE0, GL_TEXTURE1, @@ -122,8 +140,6 @@ void OpenGLRenderer::setViewport(int width, int height) { mWidth = width; mHeight = height; - mFirstSnapshot->height = height; - mFirstSnapshot->viewport.set(0, 0, width, height); } void OpenGLRenderer::prepare() { @@ -155,14 +171,19 @@ void OpenGLRenderer::acquireContext() { } void OpenGLRenderer::releaseContext() { - glViewport(0, 0, mSnapshot->viewport.getWidth(), mSnapshot->viewport.getHeight()); + glViewport(0, 0, mWidth, mHeight); glEnable(GL_SCISSOR_TEST); setScissorFromClip(); + glDisable(GL_DITHER); + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + if (mCaches.blend) { glEnable(GL_BLEND); glBlendFunc(mCaches.lastSrcMode, mCaches.lastDstMode); + glBlendEquation(GL_FUNC_ADD); } else { glDisable(GL_BLEND); } @@ -202,17 +223,10 @@ int OpenGLRenderer::saveSnapshot(int flags) { bool OpenGLRenderer::restoreSnapshot() { bool restoreClip = mSnapshot->flags & Snapshot::kFlagClipSet; bool restoreLayer = mSnapshot->flags & Snapshot::kFlagIsLayer; - bool restoreOrtho = mSnapshot->flags & Snapshot::kFlagDirtyOrtho; sp<Snapshot> current = mSnapshot; sp<Snapshot> previous = mSnapshot->previous; - if (restoreOrtho) { - Rect& r = previous->viewport; - glViewport(r.left, r.top, r.right, r.bottom); - mOrthoMatrix.load(current->orthoMatrix); - } - mSaveCount--; mSnapshot = previous; @@ -253,11 +267,7 @@ int OpenGLRenderer::saveLayer(float left, float top, float right, float bottom, mode = SkXfermode::kSrcOver_Mode; } - if (alpha > 0 && !mSnapshot->invisible) { - createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags); - } else { - mSnapshot->invisible = true; - } + createLayer(mSnapshot, left, top, right, bottom, alpha, mode, flags); return count; } @@ -273,79 +283,123 @@ int OpenGLRenderer::saveLayerAlpha(float left, float top, float right, float bot } } +/** + * Layers are viewed by Skia are slightly different than layers in image editing + * programs (for instance.) When a layer is created, previously created layers + * and the frame buffer still receive every drawing command. For instance, if a + * layer is created and a shape intersecting the bounds of the layers and the + * framebuffer is draw, the shape will be drawn on both (unless the layer was + * created with the SkCanvas::kClipToLayer_SaveFlag flag.) + * + * A way to implement layers is to create an FBO for each layer, backed by an RGBA + * texture. Unfortunately, this is inefficient as it requires every primitive to + * be drawn n + 1 times, where n is the number of active layers. In practice this + * means, for every primitive: + * - Switch active frame buffer + * - Change viewport, clip and projection matrix + * - Issue the drawing + * + * Switching rendering target n + 1 times per drawn primitive is extremely costly. + * To avoid this, layers are implemented in a different way here. + * + * This implementation relies on the frame buffer being at least RGBA 8888. When + * a layer is created, only a texture is created, not an FBO. The content of the + * frame buffer contained within the layer's bounds is copied into this texture + * using glCopyTexImage2D(). The layer's region is then cleared(1) in the frame + * buffer and drawing continues as normal. This technique therefore treats the + * frame buffer as a scratch buffer for the layers. + * + * To compose the layers back onto the frame buffer, each layer texture + * (containing the original frame buffer data) is drawn as a simple quad over + * the frame buffer. The trick is that the quad is set as the composition + * destination in the blending equation, and the frame buffer becomes the source + * of the composition. + * + * Drawing layers with an alpha value requires an extra step before composition. + * An empty quad is drawn over the layer's region in the frame buffer. This quad + * is drawn with the rgba color (0,0,0,alpha). The alpha value offered by the + * quad is used to multiply the colors in the frame buffer. This is achieved by + * changing the GL blend functions for the GL_FUNC_ADD blend equation to + * GL_ZERO, GL_SRC_ALPHA. + * + * Because glCopyTexImage2D() can be slow, an alternative implementation might + * be use to draw a single clipped layer. The implementation described above + * is correct in every case. + * + * (1) The frame buffer is actually not cleared right away. To allow the GPU + * to potentially optimize series of calls to glCopyTexImage2D, the frame + * buffer is left untouched until the first drawing operation. Only when + * something actually gets drawn are the layers regions cleared. + */ bool OpenGLRenderer::createLayer(sp<Snapshot> snapshot, float left, float top, float right, float bottom, int alpha, SkXfermode::Mode mode,int flags) { LAYER_LOGD("Requesting layer %fx%f", right - left, bottom - top); LAYER_LOGD("Layer cache size = %d", mCaches.layerCache.getSize()); + // Window coordinates of the layer Rect bounds(left, top, right, bottom); - // TODO: Apply transformations and treat layers in screen coordinates - // mSnapshot->transform->mapRect(bounds); + mSnapshot->transform->mapRect(bounds); - GLuint previousFbo = snapshot->previous.get() ? snapshot->previous->fbo : 0; - LayerSize size(bounds.getWidth(), bounds.getHeight()); + // Layers only make sense if they are in the framebuffer's bounds + bounds.intersect(*mSnapshot->clipRect); + if (bounds.isEmpty()) return false; - Layer* layer = mCaches.layerCache.get(size, previousFbo); + LayerSize size(bounds.getWidth(), bounds.getHeight()); + Layer* layer = mCaches.layerCache.get(size); if (!layer) { return false; } - glBindFramebuffer(GL_FRAMEBUFFER, layer->fbo); - - // Clear the FBO - glDisable(GL_SCISSOR_TEST); - glClearColor(0.0f, 0.0f, 0.0f, 0.0f); - glClear(GL_COLOR_BUFFER_BIT); - glEnable(GL_SCISSOR_TEST); - layer->mode = mode; - layer->alpha = alpha / 255.0f; + layer->alpha = alpha; layer->layer.set(bounds); // Save the layer in the snapshot snapshot->flags |= Snapshot::kFlagIsLayer; snapshot->layer = layer; - snapshot->fbo = layer->fbo; - // TODO: Temporary until real layer support is implemented - snapshot->resetTransform(-bounds.left, -bounds.top, 0.0f); - // TODO: Temporary until real layer support is implemented - snapshot->resetClip(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->viewport.set(0.0f, 0.0f, bounds.getWidth(), bounds.getHeight()); - snapshot->height = bounds.getHeight(); - snapshot->flags |= Snapshot::kFlagDirtyOrtho; - snapshot->orthoMatrix.load(mOrthoMatrix); - setScissorFromClip(); + // Copy the framebuffer into the layer + glBindTexture(GL_TEXTURE_2D, layer->texture); + glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, bounds.left, mHeight - bounds.bottom, + bounds.getWidth(), bounds.getHeight(), 0); + + if (flags & SkCanvas::kClipToLayer_SaveFlag) { + if (mSnapshot->clipTransformed(bounds)) setScissorFromClip(); + } - // Change the ortho projection - glViewport(0, 0, bounds.getWidth(), bounds.getHeight()); - mOrthoMatrix.loadOrtho(0.0f, bounds.getWidth(), bounds.getHeight(), 0.0f, -1.0f, 1.0f); + // Enqueue the buffer coordinates to clear the corresponding region later + mLayers.push(new Rect(bounds)); return true; } +/** + * Read the documentation of createLayer() before doing anything in this method. + */ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { if (!current->layer) { LOGE("Attempting to compose a layer that does not exist"); return; } - // Unbind current FBO and restore previous one - // Most of the time, previous->fbo will be 0 to bind the default buffer - glBindFramebuffer(GL_FRAMEBUFFER, previous->fbo); - // Restore the clip from the previous snapshot const Rect& clip = *previous->clipRect; - glScissor(clip.left, previous->height - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); Layer* layer = current->layer; const Rect& rect = layer->layer; - // FBOs are already drawn with a top-left origin, don't flip the texture + if (layer->alpha < 255) { + drawColorRect(rect.left, rect.top, rect.right, rect.bottom, + layer->alpha << 24, SkXfermode::kDstIn_Mode, true); + } + + // Layers are already drawn with a top-left origin, don't flip the texture resetDrawTextureTexCoords(0.0f, 1.0f, 1.0f, 0.0f); - drawTextureRect(rect.left, rect.top, rect.right, rect.bottom, - layer->texture, layer->alpha, layer->mode, layer->blend); + drawTextureMesh(rect.left, rect.top, rect.right, rect.bottom, layer->texture, + 1.0f, layer->mode, layer->blend, &mMeshVertices[0].position[0], + &mMeshVertices[0].texture[0], NULL, 0, true, true); resetDrawTextureTexCoords(0.0f, 0.0f, 1.0f, 1.0f); @@ -355,13 +409,32 @@ void OpenGLRenderer::composeLayer(sp<Snapshot> current, sp<Snapshot> previous) { if (!mCaches.layerCache.put(size, layer)) { LAYER_LOGD("Deleting layer"); - glDeleteFramebuffers(1, &layer->fbo); glDeleteTextures(1, &layer->texture); delete layer; } } +void OpenGLRenderer::clearLayerRegions() { + if (mLayers.size() == 0) return; + + for (uint32_t i = 0; i < mLayers.size(); i++) { + Rect* bounds = mLayers.itemAt(i); + + // Clear the framebuffer where the layer will draw + glScissor(bounds->left, mHeight - bounds->bottom, + bounds->getWidth(), bounds->getHeight()); + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + glClear(GL_COLOR_BUFFER_BIT); + + delete bounds; + } + mLayers.clear(); + + // Restore the clip + setScissorFromClip(); +} + /////////////////////////////////////////////////////////////////////////////// // Transforms /////////////////////////////////////////////////////////////////////////////// @@ -397,7 +470,7 @@ void OpenGLRenderer::concatMatrix(SkMatrix* matrix) { void OpenGLRenderer::setScissorFromClip() { const Rect& clip = *mSnapshot->clipRect; - glScissor(clip.left, mSnapshot->height - clip.bottom, clip.getWidth(), clip.getHeight()); + glScissor(clip.left, mHeight - clip.bottom, clip.getWidth(), clip.getHeight()); } const Rect& OpenGLRenderer::getClipBounds() { @@ -405,8 +478,6 @@ const Rect& OpenGLRenderer::getClipBounds() { } bool OpenGLRenderer::quickReject(float left, float top, float right, float bottom) { - if (mSnapshot->invisible) return true; - Rect r(left, top, right, bottom); mSnapshot->transform->mapRect(r); return !mSnapshot->clipRect->intersects(r); @@ -503,6 +574,7 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, Patch* mesh = mCaches.patchCache.get(patch); mesh->updateVertices(bitmap, left, top, right, bottom, &patch->xDivs[0], &patch->yDivs[0], patch->numXDivs, patch->numYDivs); + mesh->dump(); // Specify right and bottom as +1.0f from left/top to prevent scaling since the // patch mesh already defines the final size @@ -512,7 +584,6 @@ void OpenGLRenderer::drawPatch(SkBitmap* bitmap, Res_png_9patch* patch, } void OpenGLRenderer::drawColor(int color, SkXfermode::Mode mode) { - if (mSnapshot->invisible) return; const Rect& clip = *mSnapshot->clipRect; drawColorRect(clip.left, clip.top, clip.right, clip.bottom, color, mode, true); } @@ -545,18 +616,10 @@ void OpenGLRenderer::drawRect(float left, float top, float right, float bottom, void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint) { - if (mSnapshot->invisible || text == NULL || count == 0 || - (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { + if (text == NULL || count == 0 || (paint->getAlpha() == 0 && paint->getXfermode() == NULL)) { return; } - - float scaleX = paint->getTextScaleX(); - bool applyScaleX = scaleX < 0.9999f || scaleX > 1.0001f; - if (applyScaleX) { - save(SkCanvas::kMatrix_SaveFlag); - translate(x - (x * scaleX), 0.0f); - scale(scaleX, 1.0f); - } + paint->setAntiAlias(true); float length = -1.0f; switch (paint->getTextAlign()) { @@ -606,21 +669,16 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, mode, false, true); const Rect& clip = mSnapshot->getLocalClip(); + clearLayerRegions(); fontRenderer.renderText(paint, &clip, text, 0, bytesCount, count, x, y); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); drawTextDecorations(text, bytesCount, length, x, y, paint); - - if (applyScaleX) { - restore(); - } } void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { - if (mSnapshot->invisible) return; - GLuint textureUnit = 0; glActiveTexture(gTextureUnits[textureUnit]); @@ -647,6 +705,8 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { setupTextureAlpha8(texture, textureUnit, x, y, r, g, b, a, mode, true, true); + clearLayerRegions(); + // Draw the mesh glDrawArrays(GL_TRIANGLE_STRIP, 0, gMeshCount); glDisableVertexAttribArray(mCaches.currentProgram->getAttrib("texCoords")); @@ -778,6 +838,7 @@ void OpenGLRenderer::setupTextureAlpha8(GLuint texture, uint32_t width, uint32_t } } +// Same values used by Skia #define kStdStrikeThru_Offset (-6.0f / 21.0f) #define kStdUnderline_Offset (1.0f / 9.0f) #define kStdUnderline_Thickness (1.0f / 18.0f) @@ -831,6 +892,8 @@ void OpenGLRenderer::drawTextDecorations(const char* text, int bytesCount, float void OpenGLRenderer::drawColorRect(float left, float top, float right, float bottom, int color, SkXfermode::Mode mode, bool ignoreTransform) { + clearLayerRegions(); + // If a shader is set, preserve only the alpha if (mShader) { color |= 0x00ffffff; @@ -905,7 +968,10 @@ void OpenGLRenderer::drawTextureRect(float left, float top, float right, float b void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount) { + GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount, + bool swapSrcDst, bool ignoreTransform) { + clearLayerRegions(); + ProgramDescription description; description.hasTexture = true; if (mColorFilter) { @@ -915,10 +981,15 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b mModelView.loadTranslate(left, top, 0.0f); mModelView.scale(right - left, bottom - top, 1.0f); - chooseBlending(blend || alpha < 1.0f, mode, description); + chooseBlending(blend || alpha < 1.0f, mode, description, swapSrcDst); useProgram(mCaches.programCache.get(description)); - mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + if (!ignoreTransform) { + mCaches.currentProgram->set(mOrthoMatrix, mModelView, *mSnapshot->transform); + } else { + mat4 m; + mCaches.currentProgram->set(mOrthoMatrix, mModelView, m); + } // Texture bindTexture(texture, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, 0); @@ -948,7 +1019,7 @@ void OpenGLRenderer::drawTextureMesh(float left, float top, float right, float b } void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, - ProgramDescription& description) { + ProgramDescription& description, bool swapSrcDst) { blend = blend || mode != SkXfermode::kSrcOver_Mode; if (blend) { if (mode < SkXfermode::kPlus_Mode) { @@ -956,8 +1027,8 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, glEnable(GL_BLEND); } - GLenum sourceMode = gBlends[mode].src; - GLenum destMode = gBlends[mode].dst; + GLenum sourceMode = swapSrcDst ? gBlendsSwap[mode].src : gBlends[mode].src; + GLenum destMode = swapSrcDst ? gBlendsSwap[mode].dst : gBlends[mode].dst; if (sourceMode != mCaches.lastSrcMode || destMode != mCaches.lastDstMode) { glBlendFunc(sourceMode, destMode); @@ -970,6 +1041,7 @@ void OpenGLRenderer::chooseBlending(bool blend, SkXfermode::Mode mode, // the blending, turn blending off here if (mExtensions.hasFramebufferFetch()) { description.framebufferMode = mode; + description.swapSrcDst = swapSrcDst; } if (mCaches.blend) { diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 50f42c2..3126754 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -29,6 +29,7 @@ #include <utils/RefBase.h> #include <utils/ResourceTypes.h> +#include <utils/Vector.h> #include "Extensions.h" #include "Matrix.h" @@ -156,6 +157,12 @@ private: int alpha, SkXfermode::Mode mode, int flags); /** + * Clears all the regions corresponding to the current list of layers. + * This method MUST be invoked before any drawing operation. + */ + void clearLayerRegions(); + + /** * Draws a colored rectangle with the specified color. The specified coordinates * are transformed by the current snapshot's transform matrix. * @@ -166,9 +173,10 @@ private: * @param color The rectangle's ARGB color, defined as a packed 32 bits word * @param mode The Skia xfermode to use * @param ignoreTransform True if the current transform should be ignored + * @paran ignoreBlending True if the blending is set by the caller */ void drawColorRect(float left, float top, float right, float bottom, - int color, SkXfermode::Mode mode, bool ignoreTransform = false); + int color, SkXfermode::Mode mode, bool ignoreTransform = false); /** * Draws a textured rectangle with the specified texture. The specified coordinates @@ -216,10 +224,13 @@ private: * @param texCoords The texture coordinates of each vertex * @param indices The indices of the vertices, can be NULL * @param elementsCount The number of elements in the mesh, required by indices + * @param swapSrcDst Whether or not the src and dst blending operations should be swapped + * @param ignoreTransform True if the current transform should be ignored */ void drawTextureMesh(float left, float top, float right, float bottom, GLuint texture, float alpha, SkXfermode::Mode mode, bool blend, - GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0); + GLvoid* vertices, GLvoid* texCoords, GLvoid* indices, GLsizei elementsCount = 0, + bool swapSrcDst = false, bool ignoreTransform = false); /** * Prepares the renderer to draw the specified shadow. @@ -322,8 +333,13 @@ private: * Enable or disable blending as necessary. This function sets the appropriate * blend function based on the specified xfermode. */ - inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description); + inline void chooseBlending(bool blend, SkXfermode::Mode mode, ProgramDescription& description, + bool swapSrcDst = false); + /** + * Safely retrieves the mode from the specified xfermode. If the specified + * xfermode is null, the mode is assumed to be SkXfermode::kSrcOver_Mode. + */ inline SkXfermode::Mode getXfermode(SkXfermode* mode); /** @@ -375,6 +391,10 @@ private: // Various caches Caches& mCaches; + + // List of rectangles to clear due to calls to saveLayer() + Vector<Rect*> mLayers; + }; // class OpenGLRenderer }; // namespace uirenderer diff --git a/libs/hwui/ProgramCache.cpp b/libs/hwui/ProgramCache.cpp index ff65c1b..becbc22 100644 --- a/libs/hwui/ProgramCache.cpp +++ b/libs/hwui/ProgramCache.cpp @@ -119,6 +119,8 @@ const char* gFS_Main_FragColor = " gl_FragColor = fragColor;\n"; const char* gFS_Main_FragColor_Blend = " gl_FragColor = blendFramebuffer(fragColor, gl_LastFragColor);\n"; +const char* gFS_Main_FragColor_Blend_Swap = + " gl_FragColor = blendFramebuffer(gl_LastFragColor, fragColor);\n"; const char* gFS_Main_ApplyColorOp[4] = { // None "", @@ -376,7 +378,8 @@ String8 ProgramCache::generateFragmentShader(const ProgramDescription& descripti if (!blendFramebuffer) { shader.append(gFS_Main_FragColor); } else { - shader.append(gFS_Main_FragColor_Blend); + shader.append(!description.swapSrcDst ? + gFS_Main_FragColor_Blend : gFS_Main_FragColor_Blend_Swap); } } // End the shader diff --git a/libs/hwui/ProgramCache.h b/libs/hwui/ProgramCache.h index 8f5304d..0a17052 100644 --- a/libs/hwui/ProgramCache.h +++ b/libs/hwui/ProgramCache.h @@ -35,7 +35,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// // Debug -#define DEBUG_PROGRAM_CACHE 1 +#define DEBUG_PROGRAM_CACHE 0 // Debug #if DEBUG_PROGRAM_CACHE @@ -44,6 +44,9 @@ namespace uirenderer { #define PROGRAM_LOGD(...) #endif +/* + * IMPORTANT: All 32 bits are used, switch to a long. + */ #define PROGRAM_KEY_TEXTURE 0x1 #define PROGRAM_KEY_A8_TEXTURE 0x2 #define PROGRAM_KEY_BITMAP 0x4 @@ -53,6 +56,7 @@ namespace uirenderer { #define PROGRAM_KEY_COLOR_LIGHTING 0x40 #define PROGRAM_KEY_COLOR_BLEND 0x80 #define PROGRAM_KEY_BITMAP_NPOT 0x100 +#define PROGRAM_KEY_SWAP_SRC_DST 0x2000 #define PROGRAM_KEY_BITMAP_WRAPS_MASK 0x600 #define PROGRAM_KEY_BITMAP_WRAPT_MASK 0x1800 @@ -70,6 +74,9 @@ namespace uirenderer { // Types /////////////////////////////////////////////////////////////////////////////// +/* + * IMPORTANT: All 32 bits are used, switch to a long. + */ typedef uint32_t programid; /////////////////////////////////////////////////////////////////////////////// @@ -95,7 +102,7 @@ struct ProgramDescription { shadersMode(SkXfermode::kClear_Mode), isBitmapFirst(false), bitmapWrapS(GL_CLAMP_TO_EDGE), bitmapWrapT(GL_CLAMP_TO_EDGE), colorOp(kColorNone), colorMode(SkXfermode::kClear_Mode), - framebufferMode(SkXfermode::kClear_Mode) { + framebufferMode(SkXfermode::kClear_Mode), swapSrcDst(false) { } // Texturing @@ -118,6 +125,7 @@ struct ProgramDescription { // Framebuffer blending (requires Extensions.hasFramebufferFetch()) // Ignored for all values < SkXfermode::kPlus_Mode SkXfermode::Mode framebufferMode; + bool swapSrcDst; inline uint32_t getEnumForWrap(GLenum wrap) const { switch (wrap) { @@ -163,6 +171,7 @@ struct ProgramDescription { break; } key |= (framebufferMode & PROGRAM_MAX_XFERMODE) << PROGRAM_XFERMODE_FRAMEBUFFER_SHIFT; + if (swapSrcDst) key |= PROGRAM_KEY_SWAP_SRC_DST; return key; } }; // struct ProgramDescription diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h index da48243..062c986 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -43,7 +43,7 @@ namespace uirenderer { */ class Snapshot: public LightRefBase<Snapshot> { public: - Snapshot(): invisible(false), flags(0), previous(NULL), layer(NULL), fbo(0) { + Snapshot(): flags(0), previous(NULL), layer(NULL) { transform = &mTransformRoot; clipRect = &mClipRectRoot; } @@ -53,13 +53,7 @@ public: * the previous snapshot. */ Snapshot(const sp<Snapshot>& s, int saveFlags): - height(s->height), - invisible(s->invisible), - flags(0), - previous(s), - layer(NULL), - fbo(s->fbo), - viewport(s->viewport) { + flags(0), previous(s), layer(NULL) { if (saveFlags & SkCanvas::kMatrix_SaveFlag) { mTransformRoot.load(*s->transform); transform = &mTransformRoot; @@ -97,24 +91,36 @@ public: */ kFlagIsLayer = 0x2, /** - * Indicates that this snapshot has changed the ortho matrix. - */ - kFlagDirtyOrtho = 0x4, - /** * Indicates that the local clip should be recomputed. */ - kFlagDirtyLocalClip = 0x8, + kFlagDirtyLocalClip = 0x4, }; /** - * Intersects the current clip with the new clip rectangle. + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is transformed + * by this snapshot's trasnformation. */ - bool clip(float left, float top, float right, float bottom, SkRegion::Op op) { - bool clipped = false; - + bool clip(float left, float top, float right, float bottom, + SkRegion::Op op = SkRegion::kIntersect_Op) { Rect r(left, top, right, bottom); transform->mapRect(r); + return clipTransformed(r, op); + } + /** + * Modifies the current clip with the new clip rectangle and + * the specified operation. The specified rectangle is considered + * already transformed. + */ + bool clipTransformed(const Rect& r, SkRegion::Op op = SkRegion::kIntersect_Op) { + bool clipped = false; + + // NOTE: The unimplemented operations require support for regions + // Supporting regions would require using a stencil buffer instead + // of the scissor. The stencil buffer itself is not too expensive + // (memory cost excluded) but on fillrate limited devices, managing + // the stencil might have a negative impact on the framerate. switch (op) { case SkRegion::kDifference_Op: break; @@ -162,29 +168,6 @@ public: return mLocalClip; } - // TODO: Temporary - void resetTransform(float x, float y, float z) { - transform = &mTransformRoot; - transform->loadTranslate(x, y, z); - } - - // TODO: Temporary - void resetClip(float left, float top, float right, float bottom) { - clipRect = &mClipRectRoot; - clipRect->set(left, top, right, bottom); - flags |= Snapshot::kFlagClipSet | Snapshot::kFlagDirtyLocalClip; - } - - /** - * Height of the framebuffer the snapshot is rendering into. - */ - int height; - - /** - * If true, the layer won't be rendered. - */ - bool invisible; - /** * Dirty flags. */ @@ -199,17 +182,6 @@ public: * Only set when the flag kFlagIsLayer is set. */ Layer* layer; - GLuint fbo; - - /** - * Current viewport. - */ - Rect viewport; - - /** - * Contains the previous ortho matrix. - */ - mat4 orthoMatrix; /** * Local transformation. Holds the current translation, scale and diff --git a/libs/ui/InputDispatcher.cpp b/libs/ui/InputDispatcher.cpp index df232d4..b8a26b0 100644 --- a/libs/ui/InputDispatcher.cpp +++ b/libs/ui/InputDispatcher.cpp @@ -31,8 +31,15 @@ // Log debug messages about input event throttling. #define DEBUG_THROTTLING 0 +// Log debug messages about input focus tracking. +#define DEBUG_FOCUS 0 + +// Log debug messages about the app switch latency optimization. +#define DEBUG_APP_SWITCH 0 + #include <cutils/log.h> #include <ui/InputDispatcher.h> +#include <ui/PowerManager.h> #include <stddef.h> #include <unistd.h> @@ -41,31 +48,62 @@ namespace android { -// TODO, this needs to be somewhere else, perhaps in the policy -static inline bool isMovementKey(int32_t keyCode) { - return keyCode == AKEYCODE_DPAD_UP - || keyCode == AKEYCODE_DPAD_DOWN - || keyCode == AKEYCODE_DPAD_LEFT - || keyCode == AKEYCODE_DPAD_RIGHT; -} +// Delay between reporting long touch events to the power manager. +const nsecs_t EVENT_IGNORE_DURATION = 300 * 1000000LL; // 300 ms + +// Default input dispatching timeout if there is no focused application or paused window +// from which to determine an appropriate dispatching timeout. +const nsecs_t DEFAULT_INPUT_DISPATCHING_TIMEOUT = 5000 * 1000000LL; // 5 sec + +// Amount of time to allow for all pending events to be processed when an app switch +// key is on the way. This is used to preempt input dispatch and drop input events +// when an application takes too long to respond and the user has pressed an app switch key. +const nsecs_t APP_SWITCH_TIMEOUT = 500 * 1000000LL; // 0.5sec + static inline nsecs_t now() { return systemTime(SYSTEM_TIME_MONOTONIC); } +static inline const char* toString(bool value) { + return value ? "true" : "false"; +} + + +// --- InputWindow --- + +bool InputWindow::visibleFrameIntersects(const InputWindow* other) const { + return visibleFrameRight > other->visibleFrameLeft + && visibleFrameLeft < other->visibleFrameRight + && visibleFrameBottom > other->visibleFrameTop + && visibleFrameTop < other->visibleFrameBottom; +} + +bool InputWindow::touchableAreaContainsPoint(int32_t x, int32_t y) const { + return x >= touchableAreaLeft && x <= touchableAreaRight + && y >= touchableAreaTop && y <= touchableAreaBottom; +} + + // --- InputDispatcher --- InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& policy) : - mPolicy(policy) { + mPolicy(policy), + mPendingEvent(NULL), mAppSwitchDueTime(LONG_LONG_MAX), + mDispatchEnabled(true), mDispatchFrozen(false), + mFocusedWindow(NULL), mTouchDown(false), mTouchedWindow(NULL), + mFocusedApplication(NULL), + mCurrentInputTargetsValid(false), + mInputTargetWaitCause(INPUT_TARGET_WAIT_CAUSE_NONE) { mPollLoop = new PollLoop(false); - mInboundQueue.head.refCount = -1; - mInboundQueue.head.type = EventEntry::TYPE_SENTINEL; - mInboundQueue.head.eventTime = LONG_LONG_MIN; + mInboundQueue.headSentinel.refCount = -1; + mInboundQueue.headSentinel.type = EventEntry::TYPE_SENTINEL; + mInboundQueue.headSentinel.eventTime = LONG_LONG_MIN; - mInboundQueue.tail.refCount = -1; - mInboundQueue.tail.type = EventEntry::TYPE_SENTINEL; - mInboundQueue.tail.eventTime = LONG_LONG_MAX; + mInboundQueue.tailSentinel.refCount = -1; + mInboundQueue.tailSentinel.type = EventEntry::TYPE_SENTINEL; + mInboundQueue.tailSentinel.eventTime = LONG_LONG_MAX; mKeyRepeatState.lastKeyEntry = NULL; @@ -77,21 +115,19 @@ InputDispatcher::InputDispatcher(const sp<InputDispatcherPolicyInterface>& polic mThrottleState.originalSampleCount = 0; LOGD("Throttling - Max events per second = %d", maxEventsPerSecond); #endif - - mCurrentInputTargetsValid = false; } InputDispatcher::~InputDispatcher() { - resetKeyRepeatLocked(); + { // acquire lock + AutoMutex _l(mLock); - while (mConnectionsByReceiveFd.size() != 0) { - unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); + resetKeyRepeatLocked(); + releasePendingEventLocked(true); + drainInboundQueueLocked(); } - for (EventEntry* entry = mInboundQueue.head.next; entry != & mInboundQueue.tail; ) { - EventEntry* next = entry->next; - mAllocator.releaseEventEntry(next); - entry = next; + while (mConnectionsByReceiveFd.size() != 0) { + unregisterInputChannel(mConnectionsByReceiveFd.valueAt(0)->inputChannel); } } @@ -99,167 +135,282 @@ void InputDispatcher::dispatchOnce() { nsecs_t keyRepeatTimeout = mPolicy->getKeyRepeatTimeout(); nsecs_t keyRepeatDelay = mPolicy->getKeyRepeatDelay(); - bool skipPoll = false; - nsecs_t currentTime; nsecs_t nextWakeupTime = LONG_LONG_MAX; { // acquire lock AutoMutex _l(mLock); - currentTime = now(); + dispatchOnceInnerLocked(keyRepeatTimeout, keyRepeatDelay, & nextWakeupTime); - // Reset the key repeat timer whenever we disallow key events, even if the next event - // is not a key. This is to ensure that we abort a key repeat if the device is just coming - // out of sleep. - // XXX we should handle resetting input state coming out of sleep more generally elsewhere - if (keyRepeatTimeout < 0) { - resetKeyRepeatLocked(); + if (runCommandsLockedInterruptible()) { + nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately } + } // release lock - // Detect and process timeouts for all connections and determine if there are any - // synchronous event dispatches pending. This step is entirely non-interruptible. - bool hasPendingSyncTarget = false; - size_t activeConnectionCount = mActiveConnections.size(); - for (size_t i = 0; i < activeConnectionCount; i++) { - Connection* connection = mActiveConnections.itemAt(i); + // Wait for callback or timeout or wake. (make sure we round up, not down) + nsecs_t currentTime = now(); + int32_t timeoutMillis; + if (nextWakeupTime > currentTime) { + uint64_t timeout = uint64_t(nextWakeupTime - currentTime); + timeout = (timeout + 999999LL) / 1000000LL; + timeoutMillis = timeout > INT_MAX ? -1 : int32_t(timeout); + } else { + timeoutMillis = 0; + } - if (connection->hasPendingSyncTarget()) { - hasPendingSyncTarget = true; - } + mPollLoop->pollOnce(timeoutMillis); +} + +void InputDispatcher::dispatchOnceInnerLocked(nsecs_t keyRepeatTimeout, + nsecs_t keyRepeatDelay, nsecs_t* nextWakeupTime) { + nsecs_t currentTime = now(); + + // Reset the key repeat timer whenever we disallow key events, even if the next event + // is not a key. This is to ensure that we abort a key repeat if the device is just coming + // out of sleep. + if (keyRepeatTimeout < 0) { + resetKeyRepeatLocked(); + } + + // If dispatching is disabled, drop all events in the queue. + if (! mDispatchEnabled) { + if (mPendingEvent || ! mInboundQueue.isEmpty()) { + LOGI("Dropping pending events because input dispatch is disabled."); + releasePendingEventLocked(true); + drainInboundQueueLocked(); + } + return; + } + + // If dispatching is frozen, do not process timeouts or try to deliver any new events. + if (mDispatchFrozen) { +#if DEBUG_FOCUS + LOGD("Dispatch frozen. Waiting some more."); +#endif + return; + } - nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; - if (connectionTimeoutTime <= currentTime) { - mTimedOutConnections.add(connection); - } else if (connectionTimeoutTime < nextWakeupTime) { - nextWakeupTime = connectionTimeoutTime; + // Optimize latency of app switches. + // Essentially we start a short timeout when an app switch key (HOME / ENDCALL) has + // been pressed. When it expires, we preempt dispatch and drop all other pending events. + bool isAppSwitchDue = mAppSwitchDueTime <= currentTime; + if (mAppSwitchDueTime < *nextWakeupTime) { + *nextWakeupTime = mAppSwitchDueTime; + } + + // Detect and process timeouts for all connections and determine if there are any + // synchronous event dispatches pending. This step is entirely non-interruptible. + bool havePendingSyncTarget = false; + size_t activeConnectionCount = mActiveConnections.size(); + for (size_t i = 0; i < activeConnectionCount; i++) { + Connection* connection = mActiveConnections.itemAt(i); + + if (connection->hasPendingSyncTarget()) { + if (isAppSwitchDue) { + connection->preemptSyncTarget(); + } else { + havePendingSyncTarget = true; } } - size_t timedOutConnectionCount = mTimedOutConnections.size(); - for (size_t i = 0; i < timedOutConnectionCount; i++) { - Connection* connection = mTimedOutConnections.itemAt(i); - timeoutDispatchCycleLocked(currentTime, connection); - skipPoll = true; - } - mTimedOutConnections.clear(); - - // If we don't have a pending sync target, then we can begin delivering a new event. - // (Otherwise we wait for dispatch to complete for that target.) - if (! hasPendingSyncTarget) { - if (mInboundQueue.isEmpty()) { - if (mKeyRepeatState.lastKeyEntry) { - if (currentTime >= mKeyRepeatState.nextRepeatTime) { - processKeyRepeatLockedInterruptible(currentTime, keyRepeatDelay); - skipPoll = true; - } else { - if (mKeyRepeatState.nextRepeatTime < nextWakeupTime) { - nextWakeupTime = mKeyRepeatState.nextRepeatTime; - } + nsecs_t connectionTimeoutTime = connection->nextTimeoutTime; + if (connectionTimeoutTime <= currentTime) { + mTimedOutConnections.add(connection); + } else if (connectionTimeoutTime < *nextWakeupTime) { + *nextWakeupTime = connectionTimeoutTime; + } + } + + size_t timedOutConnectionCount = mTimedOutConnections.size(); + for (size_t i = 0; i < timedOutConnectionCount; i++) { + Connection* connection = mTimedOutConnections.itemAt(i); + timeoutDispatchCycleLocked(currentTime, connection); + *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + } + mTimedOutConnections.clear(); + + // If we have a pending synchronous target, skip dispatch. + if (havePendingSyncTarget) { + return; + } + + // Ready to start a new event. + // If we don't already have a pending event, go grab one. + if (! mPendingEvent) { + if (mInboundQueue.isEmpty()) { + if (isAppSwitchDue) { + // The inbound queue is empty so the app switch key we were waiting + // for will never arrive. Stop waiting for it. + resetPendingAppSwitchLocked(false); + isAppSwitchDue = false; + } + + // Synthesize a key repeat if appropriate. + if (mKeyRepeatState.lastKeyEntry) { + if (currentTime >= mKeyRepeatState.nextRepeatTime) { + mPendingEvent = synthesizeKeyRepeatLocked(currentTime, keyRepeatDelay); + } else { + if (mKeyRepeatState.nextRepeatTime < *nextWakeupTime) { + *nextWakeupTime = mKeyRepeatState.nextRepeatTime; } } - } else { - // Inbound queue has at least one entry. - EventEntry* entry = mInboundQueue.head.next; - - // Consider throttling the entry if it is a move event and there are no - // other events behind it in the queue. Due to movement batching, additional - // samples may be appended to this event by the time the throttling timeout - // expires. - // TODO Make this smarter and consider throttling per device independently. - if (entry->type == EventEntry::TYPE_MOTION) { - MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); - int32_t deviceId = motionEntry->deviceId; - uint32_t source = motionEntry->source; - if (motionEntry->next == & mInboundQueue.tail - && motionEntry->action == AMOTION_EVENT_ACTION_MOVE - && deviceId == mThrottleState.lastDeviceId - && source == mThrottleState.lastSource) { - nsecs_t nextTime = mThrottleState.lastEventTime - + mThrottleState.minTimeBetweenEvents; - if (currentTime < nextTime) { - // Throttle it! + } + if (! mPendingEvent) { + return; + } + } else { + // Inbound queue has at least one entry. + EventEntry* entry = mInboundQueue.headSentinel.next; + + // Throttle the entry if it is a move event and there are no + // other events behind it in the queue. Due to movement batching, additional + // samples may be appended to this event by the time the throttling timeout + // expires. + // TODO Make this smarter and consider throttling per device independently. + if (entry->type == EventEntry::TYPE_MOTION) { + MotionEntry* motionEntry = static_cast<MotionEntry*>(entry); + int32_t deviceId = motionEntry->deviceId; + uint32_t source = motionEntry->source; + if (! isAppSwitchDue + && motionEntry->next == & mInboundQueue.tailSentinel // exactly one event + && motionEntry->action == AMOTION_EVENT_ACTION_MOVE + && deviceId == mThrottleState.lastDeviceId + && source == mThrottleState.lastSource) { + nsecs_t nextTime = mThrottleState.lastEventTime + + mThrottleState.minTimeBetweenEvents; + if (currentTime < nextTime) { + // Throttle it! #if DEBUG_THROTTLING - LOGD("Throttling - Delaying motion event for " - "device 0x%x, source 0x%08x by up to %0.3fms.", - deviceId, source, (nextTime - currentTime) * 0.000001); + LOGD("Throttling - Delaying motion event for " + "device 0x%x, source 0x%08x by up to %0.3fms.", + deviceId, source, (nextTime - currentTime) * 0.000001); #endif - if (nextTime < nextWakeupTime) { - nextWakeupTime = nextTime; - } - if (mThrottleState.originalSampleCount == 0) { - mThrottleState.originalSampleCount = - motionEntry->countSamples(); - } - goto Throttle; + if (nextTime < *nextWakeupTime) { + *nextWakeupTime = nextTime; + } + if (mThrottleState.originalSampleCount == 0) { + mThrottleState.originalSampleCount = + motionEntry->countSamples(); } + return; } + } #if DEBUG_THROTTLING - if (mThrottleState.originalSampleCount != 0) { - uint32_t count = motionEntry->countSamples(); - LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", - count - mThrottleState.originalSampleCount, - mThrottleState.originalSampleCount, count); - mThrottleState.originalSampleCount = 0; - } + if (mThrottleState.originalSampleCount != 0) { + uint32_t count = motionEntry->countSamples(); + LOGD("Throttling - Motion event sample count grew by %d from %d to %d.", + count - mThrottleState.originalSampleCount, + mThrottleState.originalSampleCount, count); + mThrottleState.originalSampleCount = 0; + } #endif - mThrottleState.lastEventTime = entry->eventTime < currentTime - ? entry->eventTime : currentTime; - mThrottleState.lastDeviceId = deviceId; - mThrottleState.lastSource = source; - } + mThrottleState.lastEventTime = entry->eventTime < currentTime + ? entry->eventTime : currentTime; + mThrottleState.lastDeviceId = deviceId; + mThrottleState.lastSource = source; + } - // Start processing the entry but leave it on the queue until later so that the - // input reader can keep appending samples onto a motion event between the - // time we started processing it and the time we finally enqueue dispatch - // entries for it. - switch (entry->type) { - case EventEntry::TYPE_CONFIGURATION_CHANGED: { - ConfigurationChangedEntry* typedEntry = - static_cast<ConfigurationChangedEntry*>(entry); - processConfigurationChangedLockedInterruptible(currentTime, typedEntry); - break; - } + mInboundQueue.dequeue(entry); + mPendingEvent = entry; + } + } - case EventEntry::TYPE_KEY: { - KeyEntry* typedEntry = static_cast<KeyEntry*>(entry); - processKeyLockedInterruptible(currentTime, typedEntry, keyRepeatTimeout); - break; - } + // Now we have an event to dispatch. + assert(mPendingEvent != NULL); + bool wasDispatched = false; + bool wasDropped = false; + switch (mPendingEvent->type) { + case EventEntry::TYPE_CONFIGURATION_CHANGED: { + ConfigurationChangedEntry* typedEntry = + static_cast<ConfigurationChangedEntry*>(mPendingEvent); + wasDispatched = dispatchConfigurationChangedLocked(currentTime, typedEntry); + break; + } - case EventEntry::TYPE_MOTION: { - MotionEntry* typedEntry = static_cast<MotionEntry*>(entry); - processMotionLockedInterruptible(currentTime, typedEntry); - break; - } + case EventEntry::TYPE_KEY: { + KeyEntry* typedEntry = static_cast<KeyEntry*>(mPendingEvent); + if (isAppSwitchPendingLocked()) { + if (isAppSwitchKey(typedEntry->keyCode)) { + resetPendingAppSwitchLocked(true); + } else if (isAppSwitchDue) { + LOGI("Dropping key because of pending overdue app switch."); + wasDropped = true; + break; + } + } + wasDispatched = dispatchKeyLocked(currentTime, typedEntry, keyRepeatTimeout, + nextWakeupTime); + break; + } - default: - assert(false); - break; - } + case EventEntry::TYPE_MOTION: { + MotionEntry* typedEntry = static_cast<MotionEntry*>(mPendingEvent); + if (isAppSwitchDue) { + LOGI("Dropping motion because of pending overdue app switch."); + wasDropped = true; + break; + } + wasDispatched = dispatchMotionLocked(currentTime, typedEntry, nextWakeupTime); + break; + } - // Dequeue and release the event entry that we just processed. - mInboundQueue.dequeue(entry); - mAllocator.releaseEventEntry(entry); - skipPoll = true; + default: + assert(false); + wasDropped = true; + break; + } - Throttle: ; - } - } + if (wasDispatched || wasDropped) { + releasePendingEventLocked(wasDropped); + *nextWakeupTime = LONG_LONG_MIN; // force next poll to wake up immediately + } +} - // Run any deferred commands. - skipPoll |= runCommandsLockedInterruptible(); - } // release lock +bool InputDispatcher::enqueueInboundEventLocked(EventEntry* entry) { + bool needWake = mInboundQueue.isEmpty(); + mInboundQueue.enqueueAtTail(entry); - // If we dispatched anything, don't poll just now. Wait for the next iteration. - // Contents may have shifted during flight. - if (skipPoll) { - return; + switch (entry->type) { + case EventEntry::TYPE_KEY: + needWake |= detectPendingAppSwitchLocked(static_cast<KeyEntry*>(entry)); + break; } - // Wait for callback or timeout or wake. (make sure we round up, not down) - nsecs_t timeout = (nextWakeupTime - currentTime + 999999LL) / 1000000LL; - int32_t timeoutMillis = timeout > INT_MAX ? -1 : timeout > 0 ? int32_t(timeout) : 0; - mPollLoop->pollOnce(timeoutMillis); + return needWake; +} + +bool InputDispatcher::isAppSwitchKey(int32_t keyCode) { + return keyCode == AKEYCODE_HOME || keyCode == AKEYCODE_ENDCALL; +} + +bool InputDispatcher::isAppSwitchPendingLocked() { + return mAppSwitchDueTime != LONG_LONG_MAX; +} + +bool InputDispatcher::detectPendingAppSwitchLocked(KeyEntry* inboundKeyEntry) { + if (inboundKeyEntry->action == AKEY_EVENT_ACTION_UP + && ! (inboundKeyEntry->flags & AKEY_EVENT_FLAG_CANCELED) + && isAppSwitchKey(inboundKeyEntry->keyCode) + && isEventFromReliableSourceLocked(inboundKeyEntry)) { +#if DEBUG_APP_SWITCH + LOGD("App switch is pending!"); +#endif + mAppSwitchDueTime = inboundKeyEntry->eventTime + APP_SWITCH_TIMEOUT; + return true; // need wake + } + return false; +} + +void InputDispatcher::resetPendingAppSwitchLocked(bool handled) { + mAppSwitchDueTime = LONG_LONG_MAX; + +#if DEBUG_APP_SWITCH + if (handled) { + LOGD("App switch has arrived."); + } else { + LOGD("App switch was abandoned."); + } +#endif } bool InputDispatcher::runCommandsLockedInterruptible() { @@ -285,78 +436,52 @@ InputDispatcher::CommandEntry* InputDispatcher::postCommandLocked(Command comman return commandEntry; } -void InputDispatcher::processConfigurationChangedLockedInterruptible( - nsecs_t currentTime, ConfigurationChangedEntry* entry) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processConfigurationChanged - eventTime=%lld", entry->eventTime); -#endif - - // Reset key repeating in case a keyboard device was added or removed or something. - resetKeyRepeatLocked(); - - mLock.unlock(); - - mPolicy->notifyConfigurationChanged(entry->eventTime); +void InputDispatcher::drainInboundQueueLocked() { + while (! mInboundQueue.isEmpty()) { + EventEntry* entry = mInboundQueue.dequeueAtHead(); + releaseInboundEventLocked(entry, true /*wasDropped*/); + } +} - mLock.lock(); +void InputDispatcher::releasePendingEventLocked(bool wasDropped) { + if (mPendingEvent) { + releaseInboundEventLocked(mPendingEvent, wasDropped); + mPendingEvent = NULL; + } } -void InputDispatcher::processKeyLockedInterruptible( - nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout) { -#if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processKey - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, action=0x%x, " - "flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, downTime=%lld", - entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, - entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->downTime); +void InputDispatcher::releaseInboundEventLocked(EventEntry* entry, bool wasDropped) { + if (wasDropped) { +#if DEBUG_DISPATCH_CYCLE + LOGD("Pending event was dropped."); #endif - - if (entry->action == AKEY_EVENT_ACTION_DOWN && ! entry->isInjected()) { - if (mKeyRepeatState.lastKeyEntry - && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { - // We have seen two identical key downs in a row which indicates that the device - // driver is automatically generating key repeats itself. We take note of the - // repeat here, but we disable our own next key repeat timer since it is clear that - // we will not need to synthesize key repeats ourselves. - entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves - } else { - // Not a repeat. Save key down state in case we do see a repeat later. - resetKeyRepeatLocked(); - mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; - } - mKeyRepeatState.lastKeyEntry = entry; - entry->refCount += 1; - } else { - resetKeyRepeatLocked(); + setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); } + mAllocator.releaseEventEntry(entry); +} - identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); +bool InputDispatcher::isEventFromReliableSourceLocked(EventEntry* entry) { + return ! entry->isInjected() + || entry->injectorUid == 0 + || mPolicy->checkInjectEventsPermissionNonReentrant( + entry->injectorPid, entry->injectorUid); } -void InputDispatcher::processKeyRepeatLockedInterruptible( +void InputDispatcher::resetKeyRepeatLocked() { + if (mKeyRepeatState.lastKeyEntry) { + mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); + mKeyRepeatState.lastKeyEntry = NULL; + } +} + +InputDispatcher::KeyEntry* InputDispatcher::synthesizeKeyRepeatLocked( nsecs_t currentTime, nsecs_t keyRepeatDelay) { KeyEntry* entry = mKeyRepeatState.lastKeyEntry; - // Search the inbound queue for a key up corresponding to this device. - // It doesn't make sense to generate a key repeat event if the key is already up. - for (EventEntry* queuedEntry = mInboundQueue.head.next; - queuedEntry != & mInboundQueue.tail; queuedEntry = entry->next) { - if (queuedEntry->type == EventEntry::TYPE_KEY) { - KeyEntry* queuedKeyEntry = static_cast<KeyEntry*>(queuedEntry); - if (queuedKeyEntry->deviceId == entry->deviceId - && entry->action == AKEY_EVENT_ACTION_UP) { - resetKeyRepeatLocked(); - return; - } - } - } - - // Synthesize a key repeat. // Reuse the repeated key entry if it is otherwise unreferenced. uint32_t policyFlags = entry->policyFlags & POLICY_FLAG_RAW_MASK; if (entry->refCount == 1) { + entry->recycle(); entry->eventTime = currentTime; entry->policyFlags = policyFlags; entry->repeatCount += 1; @@ -371,31 +496,194 @@ void InputDispatcher::processKeyRepeatLockedInterruptible( entry = newEntry; } + entry->syntheticRepeat = true; + + // Increment reference count since we keep a reference to the event in + // mKeyRepeatState.lastKeyEntry in addition to the one we return. + entry->refCount += 1; if (entry->repeatCount == 1) { entry->flags |= AKEY_EVENT_FLAG_LONG_PRESS; } mKeyRepeatState.nextRepeatTime = currentTime + keyRepeatDelay; + return entry; +} + +bool InputDispatcher::dispatchConfigurationChangedLocked( + nsecs_t currentTime, ConfigurationChangedEntry* entry) { +#if DEBUG_OUTBOUND_EVENT_DETAILS + LOGD("dispatchConfigurationChanged - eventTime=%lld", entry->eventTime); +#endif + + // Reset key repeating in case a keyboard device was added or removed or something. + resetKeyRepeatLocked(); + + // Enqueue a command to run outside the lock to tell the policy that the configuration changed. + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doNotifyConfigurationChangedInterruptible); + commandEntry->eventTime = entry->eventTime; + return true; +} +bool InputDispatcher::dispatchKeyLocked( + nsecs_t currentTime, KeyEntry* entry, nsecs_t keyRepeatTimeout, + nsecs_t* nextWakeupTime) { + // Preprocessing. + if (! entry->dispatchInProgress) { + logOutboundKeyDetailsLocked("dispatchKey - ", entry); + + if (entry->repeatCount == 0 + && entry->action == AKEY_EVENT_ACTION_DOWN + && ! entry->isInjected()) { + if (mKeyRepeatState.lastKeyEntry + && mKeyRepeatState.lastKeyEntry->keyCode == entry->keyCode) { + // We have seen two identical key downs in a row which indicates that the device + // driver is automatically generating key repeats itself. We take note of the + // repeat here, but we disable our own next key repeat timer since it is clear that + // we will not need to synthesize key repeats ourselves. + entry->repeatCount = mKeyRepeatState.lastKeyEntry->repeatCount + 1; + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = LONG_LONG_MAX; // don't generate repeats ourselves + } else { + // Not a repeat. Save key down state in case we do see a repeat later. + resetKeyRepeatLocked(); + mKeyRepeatState.nextRepeatTime = entry->eventTime + keyRepeatTimeout; + } + mKeyRepeatState.lastKeyEntry = entry; + entry->refCount += 1; + } else if (! entry->syntheticRepeat) { + resetKeyRepeatLocked(); + } + + entry->dispatchInProgress = true; + startFindingTargetsLocked(); + } + + // Identify targets. + if (! mCurrentInputTargetsValid) { + InputWindow* window = NULL; + int32_t injectionResult = findFocusedWindowLocked(currentTime, + entry, nextWakeupTime, & window); + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } + + setInjectionResultLocked(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } + + addMonitoringTargetsLocked(); + finishFindingTargetsLocked(window); + } + + // Give the policy a chance to intercept the key. + if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN) { + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible); + commandEntry->inputChannel = mCurrentInputChannel; + commandEntry->keyEntry = entry; + entry->refCount += 1; + return false; // wait for the command to run + } + if (entry->interceptKeyResult == KeyEntry::INTERCEPT_KEY_RESULT_SKIP) { + return true; + } + + // Dispatch the key. + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + + // Poke user activity. + pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, POWER_MANAGER_BUTTON_EVENT); + return true; +} + +void InputDispatcher::logOutboundKeyDetailsLocked(const char* prefix, const KeyEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processKeyRepeat - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, keyCode=0x%x, scanCode=0x%x, metaState=0x%x, " - "repeatCount=%d, downTime=%lld", + "downTime=%lld", + prefix, entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, entry->flags, entry->keyCode, entry->scanCode, entry->metaState, - entry->repeatCount, entry->downTime); + entry->downTime); #endif +} + +bool InputDispatcher::dispatchMotionLocked( + nsecs_t currentTime, MotionEntry* entry, nsecs_t* nextWakeupTime) { + // Preprocessing. + if (! entry->dispatchInProgress) { + logOutboundMotionDetailsLocked("dispatchMotion - ", entry); + + entry->dispatchInProgress = true; + startFindingTargetsLocked(); + } + + bool isPointerEvent = entry->source & AINPUT_SOURCE_CLASS_POINTER; + + // Identify targets. + if (! mCurrentInputTargetsValid) { + InputWindow* window = NULL; + int32_t injectionResult; + if (isPointerEvent) { + // Pointer event. (eg. touchscreen) + injectionResult = findTouchedWindowLocked(currentTime, + entry, nextWakeupTime, & window); + } else { + // Non touch event. (eg. trackball) + injectionResult = findFocusedWindowLocked(currentTime, + entry, nextWakeupTime, & window); + } + if (injectionResult == INPUT_EVENT_INJECTION_PENDING) { + return false; + } + + setInjectionResultLocked(entry, injectionResult); + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + return true; + } - identifyInputTargetsAndDispatchKeyLockedInterruptible(currentTime, entry); + addMonitoringTargetsLocked(); + finishFindingTargetsLocked(window); + } + + // Dispatch the motion. + dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + + // Poke user activity. + int32_t eventType; + if (isPointerEvent) { + switch (entry->action) { + case AMOTION_EVENT_ACTION_DOWN: + eventType = POWER_MANAGER_TOUCH_EVENT; + break; + case AMOTION_EVENT_ACTION_UP: + eventType = POWER_MANAGER_TOUCH_UP_EVENT; + break; + default: + if (entry->eventTime - entry->downTime >= EVENT_IGNORE_DURATION) { + eventType = POWER_MANAGER_TOUCH_EVENT; + } else { + eventType = POWER_MANAGER_LONG_TOUCH_EVENT; + } + break; + } + } else { + eventType = POWER_MANAGER_BUTTON_EVENT; + } + pokeUserActivityLocked(entry->eventTime, mCurrentInputWindowType, eventType); + return true; } -void InputDispatcher::processMotionLockedInterruptible( - nsecs_t currentTime, MotionEntry* entry) { + +void InputDispatcher::logOutboundMotionDetailsLocked(const char* prefix, const MotionEntry* entry) { #if DEBUG_OUTBOUND_EVENT_DETAILS - LOGD("processMotion - eventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " + LOGD("%seventTime=%lld, deviceId=0x%x, source=0x%x, policyFlags=0x%x, " "action=0x%x, flags=0x%x, " "metaState=0x%x, edgeFlags=0x%x, xPrecision=%f, yPrecision=%f, downTime=%lld", + prefix, entry->eventTime, entry->deviceId, entry->source, entry->policyFlags, entry->action, entry->flags, entry->metaState, entry->edgeFlags, entry->xPrecision, entry->yPrecision, @@ -403,7 +691,7 @@ void InputDispatcher::processMotionLockedInterruptible( // Print the most recent sample that we have available, this may change due to batching. size_t sampleCount = 1; - MotionSample* sample = & entry->firstSample; + const MotionSample* sample = & entry->firstSample; for (; sample->next != NULL; sample = sample->next) { sampleCount += 1; } @@ -425,94 +713,541 @@ void InputDispatcher::processMotionLockedInterruptible( LOGD(" ... Total movement samples currently batched %d ...", sampleCount); } #endif - - identifyInputTargetsAndDispatchMotionLockedInterruptible(currentTime, entry); } -void InputDispatcher::identifyInputTargetsAndDispatchKeyLockedInterruptible( - nsecs_t currentTime, KeyEntry* entry) { +void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, + EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { #if DEBUG_DISPATCH_CYCLE - LOGD("identifyInputTargetsAndDispatchKey"); + LOGD("dispatchEventToCurrentInputTargets - " + "resumeWithAppendedMotionSample=%s", + toString(resumeWithAppendedMotionSample)); #endif - entry->dispatchInProgress = true; - mCurrentInputTargetsValid = false; - mLock.unlock(); + assert(eventEntry->dispatchInProgress); // should already have been set to true - mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, - entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, - entry->downTime, entry->eventTime); + for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { + const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); + ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel); + if (connectionIndex >= 0) { + sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); + prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, + resumeWithAppendedMotionSample); + } else { + LOGW("Framework requested delivery of an input event to channel '%s' but it " + "is not registered with the input dispatcher.", + inputTarget.inputChannel->getName().string()); + } + } +} + +void InputDispatcher::startFindingTargetsLocked() { + mCurrentInputTargetsValid = false; mCurrentInputTargets.clear(); - int32_t injectionResult = mPolicy->waitForKeyEventTargets(& mReusableKeyEvent, - entry->policyFlags, entry->injectorPid, entry->injectorUid, - mCurrentInputTargets); + mCurrentInputChannel.clear(); + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; +} - mLock.lock(); +void InputDispatcher::finishFindingTargetsLocked(const InputWindow* window) { + mCurrentInputWindowType = window->layoutParamsType; + mCurrentInputChannel = window->inputChannel; mCurrentInputTargetsValid = true; +} + +int32_t InputDispatcher::handleTargetsNotReadyLocked(nsecs_t currentTime, + const EventEntry* entry, const InputApplication* application, const InputWindow* window, + nsecs_t* nextWakeupTime) { + if (application == NULL && window == NULL) { + if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY) { +#if DEBUG_FOCUS + LOGD("Waiting for system to become ready for input."); +#endif + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_SYSTEM_NOT_READY; + mInputTargetWaitStartTime = currentTime; + mInputTargetWaitTimeoutTime = LONG_LONG_MAX; + mInputTargetWaitTimeoutExpired = false; + } + } else { + if (mInputTargetWaitCause != INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { +#if DEBUG_FOCUS + LOGD("Waiting for application to become ready for input: name=%s, window=%s", + application ? application->name.string() : "<unknown>", + window ? window->inputChannel->getName().string() : "<unknown>"); +#endif + nsecs_t timeout = window ? window->dispatchingTimeout : + application ? application->dispatchingTimeout : DEFAULT_INPUT_DISPATCHING_TIMEOUT; + + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY; + mInputTargetWaitStartTime = currentTime; + mInputTargetWaitTimeoutTime = currentTime + timeout; + mInputTargetWaitTimeoutExpired = false; + } + } + + if (mInputTargetWaitTimeoutExpired) { + return INPUT_EVENT_INJECTION_TIMED_OUT; + } + + if (currentTime >= mInputTargetWaitTimeoutTime) { + LOGI("Application is not ready for input: name=%s, window=%s," + "%01.1fms since event, %01.1fms since wait started", + application ? application->name.string() : "<unknown>", + window ? window->inputChannel->getName().string() : "<unknown>", + (currentTime - entry->eventTime) / 1000000.0, + (currentTime - mInputTargetWaitStartTime) / 1000000.0); + + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible); + if (application) { + commandEntry->inputApplicationHandle = application->handle; + } + if (window) { + commandEntry->inputChannel = window->inputChannel; + } + + // Force poll loop to wake up immediately on next iteration once we get the + // ANR response back from the policy. + *nextWakeupTime = LONG_LONG_MIN; + return INPUT_EVENT_INJECTION_PENDING; + } else { + // Force poll loop to wake up when timeout is due. + if (mInputTargetWaitTimeoutTime < *nextWakeupTime) { + *nextWakeupTime = mInputTargetWaitTimeoutTime; + } + return INPUT_EVENT_INJECTION_PENDING; + } +} - setInjectionResultLocked(entry, injectionResult); +void InputDispatcher::resumeAfterTargetsNotReadyTimeoutLocked(nsecs_t newTimeout) { + if (newTimeout > 0) { + // Extend the timeout. + mInputTargetWaitTimeoutTime = now() + newTimeout; + } else { + // Give up. + mInputTargetWaitTimeoutExpired = true; + } +} - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); +nsecs_t InputDispatcher::getTimeSpentWaitingForApplicationWhileFindingTargetsLocked( + nsecs_t currentTime) { + if (mInputTargetWaitCause == INPUT_TARGET_WAIT_CAUSE_APPLICATION_NOT_READY) { + return currentTime - mInputTargetWaitStartTime; } + return 0; } -void InputDispatcher::identifyInputTargetsAndDispatchMotionLockedInterruptible( - nsecs_t currentTime, MotionEntry* entry) { -#if DEBUG_DISPATCH_CYCLE - LOGD("identifyInputTargetsAndDispatchMotion"); +void InputDispatcher::resetANRTimeoutsLocked() { +#if DEBUG_FOCUS + LOGD("Resetting ANR timeouts."); #endif - entry->dispatchInProgress = true; - mCurrentInputTargetsValid = false; - mLock.unlock(); + // Reset timeouts for all active connections. + nsecs_t currentTime = now(); + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections[i]; + connection->resetTimeout(currentTime); + } - mReusableMotionEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, - entry->edgeFlags, entry->metaState, - 0, 0, entry->xPrecision, entry->yPrecision, - entry->downTime, entry->eventTime, entry->pointerCount, entry->pointerIds, - entry->firstSample.pointerCoords); + // Reset input target wait timeout. + mInputTargetWaitCause = INPUT_TARGET_WAIT_CAUSE_NONE; +} +int32_t InputDispatcher::findFocusedWindowLocked(nsecs_t currentTime, const EventEntry* entry, + nsecs_t* nextWakeupTime, InputWindow** outWindow) { + *outWindow = NULL; mCurrentInputTargets.clear(); - int32_t injectionResult = mPolicy->waitForMotionEventTargets(& mReusableMotionEvent, - entry->policyFlags, entry->injectorPid, entry->injectorUid, - mCurrentInputTargets); - mLock.lock(); - mCurrentInputTargetsValid = true; + int32_t injectionResult; - setInjectionResultLocked(entry, injectionResult); + // If there is no currently focused window and no focused application + // then drop the event. + if (! mFocusedWindow) { + if (mFocusedApplication) { +#if DEBUG_FOCUS + LOGD("Waiting because there is no focused window but there is a " + "focused application that may eventually add a window: '%s'.", + mFocusedApplication->name.string()); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, NULL, nextWakeupTime); + goto Unresponsive; + } - if (injectionResult == INPUT_EVENT_INJECTION_SUCCEEDED) { - dispatchEventToCurrentInputTargetsLocked(currentTime, entry, false); + LOGI("Dropping event because there is no focused window or focused application."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + goto Failed; } + + // Check permissions. + if (! checkInjectionPermission(mFocusedWindow, entry->injectorPid, entry->injectorUid)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + goto Failed; + } + + // If the currently focused window is paused then keep waiting. + if (mFocusedWindow->paused) { +#if DEBUG_FOCUS + LOGD("Waiting because focused window is paused."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, mFocusedWindow, nextWakeupTime); + goto Unresponsive; + } + + // Success! Output targets. + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + *outWindow = mFocusedWindow; + addWindowTargetLocked(mFocusedWindow, InputTarget::FLAG_SYNC, + getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime)); + + // Done. +Failed: +Unresponsive: +#if DEBUG_FOCUS + LOGD("findFocusedWindow finished: injectionResult=%d", + injectionResult); + logDispatchStateLocked(); +#endif + return injectionResult; } -void InputDispatcher::dispatchEventToCurrentInputTargetsLocked(nsecs_t currentTime, - EventEntry* eventEntry, bool resumeWithAppendedMotionSample) { -#if DEBUG_DISPATCH_CYCLE - LOGD("dispatchEventToCurrentInputTargets - " - "resumeWithAppendedMotionSample=%s", - resumeWithAppendedMotionSample ? "true" : "false"); +int32_t InputDispatcher::findTouchedWindowLocked(nsecs_t currentTime, const MotionEntry* entry, + nsecs_t* nextWakeupTime, InputWindow** outWindow) { + enum InjectionPermission { + INJECTION_PERMISSION_UNKNOWN, + INJECTION_PERMISSION_GRANTED, + INJECTION_PERMISSION_DENIED + }; + + *outWindow = NULL; + mCurrentInputTargets.clear(); + + nsecs_t startTime = now(); + + // For security reasons, we defer updating the touch state until we are sure that + // event injection will be allowed. + // + // FIXME In the original code, screenWasOff could never be set to true. + // The reason is that the POLICY_FLAG_WOKE_HERE + // and POLICY_FLAG_BRIGHT_HERE flags were set only when preprocessing raw + // EV_KEY, EV_REL and EV_ABS events. As it happens, the touch event was + // actually enqueued using the policyFlags that appeared in the final EV_SYN + // events upon which no preprocessing took place. So policyFlags was always 0. + // In the new native input dispatcher we're a bit more careful about event + // preprocessing so the touches we receive can actually have non-zero policyFlags. + // Unfortunately we obtain undesirable behavior. + // + // Here's what happens: + // + // When the device dims in anticipation of going to sleep, touches + // in windows which have FLAG_TOUCHABLE_WHEN_WAKING cause + // the device to brighten and reset the user activity timer. + // Touches on other windows (such as the launcher window) + // are dropped. Then after a moment, the device goes to sleep. Oops. + // + // Also notice how screenWasOff was being initialized using POLICY_FLAG_BRIGHT_HERE + // instead of POLICY_FLAG_WOKE_HERE... + // + bool screenWasOff = false; // original policy: policyFlags & POLICY_FLAG_BRIGHT_HERE; + + int32_t action = entry->action; + + // Update the touch state as needed based on the properties of the touch event. + int32_t injectionResult; + InjectionPermission injectionPermission; + if (action == AMOTION_EVENT_ACTION_DOWN) { + /* Case 1: ACTION_DOWN */ + + InputWindow* newTouchedWindow = NULL; + mTempTouchedOutsideTargets.clear(); + + int32_t x = int32_t(entry->firstSample.pointerCoords[0].x); + int32_t y = int32_t(entry->firstSample.pointerCoords[0].y); + InputWindow* topErrorWindow = NULL; + bool obscured = false; + + // Traverse windows from front to back to find touched window and outside targets. + size_t numWindows = mWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + InputWindow* window = & mWindows.editItemAt(i); + int32_t flags = window->layoutParamsFlags; + + if (flags & InputWindow::FLAG_SYSTEM_ERROR) { + if (! topErrorWindow) { + topErrorWindow = window; + } + } + + if (window->visible) { + if (! (flags & InputWindow::FLAG_NOT_TOUCHABLE)) { + bool isTouchModal = (flags & (InputWindow::FLAG_NOT_FOCUSABLE + | InputWindow::FLAG_NOT_TOUCH_MODAL)) == 0; + if (isTouchModal || window->touchableAreaContainsPoint(x, y)) { + if (! screenWasOff || flags & InputWindow::FLAG_TOUCHABLE_WHEN_WAKING) { + newTouchedWindow = window; + obscured = isWindowObscuredLocked(window); + } + break; // found touched window, exit window loop + } + } + + if (flags & InputWindow::FLAG_WATCH_OUTSIDE_TOUCH) { + OutsideTarget outsideTarget; + outsideTarget.window = window; + outsideTarget.obscured = isWindowObscuredLocked(window); + mTempTouchedOutsideTargets.push(outsideTarget); + } + } + } + + // If there is an error window but it is not taking focus (typically because + // it is invisible) then wait for it. Any other focused window may in + // fact be in ANR state. + if (topErrorWindow && newTouchedWindow != topErrorWindow) { +#if DEBUG_FOCUS + LOGD("Waiting because system error window is pending."); #endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, NULL, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_UNKNOWN; + goto Unresponsive; + } - assert(eventEntry->dispatchInProgress); // should already have been set to true + // If we did not find a touched window then fail. + if (! newTouchedWindow) { + if (mFocusedApplication) { +#if DEBUG_FOCUS + LOGD("Waiting because there is no touched window but there is a " + "focused application that may eventually add a new window: '%s'.", + mFocusedApplication->name.string()); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + mFocusedApplication, NULL, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_UNKNOWN; + goto Unresponsive; + } - for (size_t i = 0; i < mCurrentInputTargets.size(); i++) { - const InputTarget& inputTarget = mCurrentInputTargets.itemAt(i); + LOGI("Dropping event because there is no touched window or focused application."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionPermission = INJECTION_PERMISSION_UNKNOWN; + goto Failed; + } - ssize_t connectionIndex = getConnectionIndex(inputTarget.inputChannel); - if (connectionIndex >= 0) { - sp<Connection> connection = mConnectionsByReceiveFd.valueAt(connectionIndex); - prepareDispatchCycleLocked(currentTime, connection, eventEntry, & inputTarget, - resumeWithAppendedMotionSample); + // Check permissions. + if (! checkInjectionPermission(newTouchedWindow, entry->injectorPid, entry->injectorUid)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionPermission = INJECTION_PERMISSION_DENIED; + goto Failed; + } + + // If the touched window is paused then keep waiting. + if (newTouchedWindow->paused) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("Waiting because touched window is paused."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, newTouchedWindow, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Unresponsive; + } + + // Success! Update the touch dispatch state for real. + releaseTouchedWindowLocked(); + + mTouchedWindow = newTouchedWindow; + mTouchedWindowIsObscured = obscured; + + if (newTouchedWindow->hasWallpaper) { + mTouchedWallpaperWindows.appendVector(mWallpaperWindows); + } + } else { + /* Case 2: Everything but ACTION_DOWN */ + + // Check permissions. + if (! checkInjectionPermission(mTouchedWindow, entry->injectorPid, entry->injectorUid)) { + injectionResult = INPUT_EVENT_INJECTION_PERMISSION_DENIED; + injectionPermission = INJECTION_PERMISSION_DENIED; + goto Failed; + } + + // If the pointer is not currently down, then ignore the event. + if (! mTouchDown) { + LOGI("Dropping event because the pointer is not down."); + injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Failed; + } + + // If there is no currently touched window then fail. + if (! mTouchedWindow) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("Dropping event because there is no touched window to receive it."); +#endif + injectionResult = INPUT_EVENT_INJECTION_FAILED; + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Failed; + } + + // If the touched window is paused then keep waiting. + if (mTouchedWindow->paused) { +#if DEBUG_INPUT_DISPATCHER_POLICY + LOGD("Waiting because touched window is paused."); +#endif + injectionResult = handleTargetsNotReadyLocked(currentTime, entry, + NULL, mTouchedWindow, nextWakeupTime); + injectionPermission = INJECTION_PERMISSION_GRANTED; + goto Unresponsive; + } + } + + // Success! Output targets. + injectionResult = INPUT_EVENT_INJECTION_SUCCEEDED; + injectionPermission = INJECTION_PERMISSION_GRANTED; + + { + size_t numWallpaperWindows = mTouchedWallpaperWindows.size(); + for (size_t i = 0; i < numWallpaperWindows; i++) { + addWindowTargetLocked(mTouchedWallpaperWindows[i], + InputTarget::FLAG_WINDOW_IS_OBSCURED, 0); + } + + size_t numOutsideTargets = mTempTouchedOutsideTargets.size(); + for (size_t i = 0; i < numOutsideTargets; i++) { + const OutsideTarget& outsideTarget = mTempTouchedOutsideTargets[i]; + int32_t outsideTargetFlags = InputTarget::FLAG_OUTSIDE; + if (outsideTarget.obscured) { + outsideTargetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + } + addWindowTargetLocked(outsideTarget.window, outsideTargetFlags, 0); + } + mTempTouchedOutsideTargets.clear(); + + int32_t targetFlags = InputTarget::FLAG_SYNC; + if (mTouchedWindowIsObscured) { + targetFlags |= InputTarget::FLAG_WINDOW_IS_OBSCURED; + } + addWindowTargetLocked(mTouchedWindow, targetFlags, + getTimeSpentWaitingForApplicationWhileFindingTargetsLocked(currentTime)); + *outWindow = mTouchedWindow; + } + +Failed: + // Check injection permission once and for all. + if (injectionPermission == INJECTION_PERMISSION_UNKNOWN) { + if (checkInjectionPermission(action == AMOTION_EVENT_ACTION_DOWN ? NULL : mTouchedWindow, + entry->injectorPid, entry->injectorUid)) { + injectionPermission = INJECTION_PERMISSION_GRANTED; } else { - LOGW("Framework requested delivery of an input event to channel '%s' but it " - "is not registered with the input dispatcher.", - inputTarget.inputChannel->getName().string()); + injectionPermission = INJECTION_PERMISSION_DENIED; } } + + // Update final pieces of touch state if the injector had permission. + if (injectionPermission == INJECTION_PERMISSION_GRANTED) { + if (action == AMOTION_EVENT_ACTION_DOWN) { + if (mTouchDown) { + // This is weird. We got a down but we thought it was already down! + LOGW("Pointer down received while already down."); + } else { + mTouchDown = true; + } + + if (injectionResult != INPUT_EVENT_INJECTION_SUCCEEDED) { + // Since we failed to identify a target for this touch down, we may still + // be holding on to an earlier target from a previous touch down. Release it. + releaseTouchedWindowLocked(); + } + } else if (action == AMOTION_EVENT_ACTION_UP) { + mTouchDown = false; + releaseTouchedWindowLocked(); + } + } else { + LOGW("Not updating touch focus because injection was denied."); + } + +Unresponsive: +#if DEBUG_FOCUS + LOGD("findTouchedWindow finished: injectionResult=%d, injectionPermission=%d", + injectionResult, injectionPermission); + logDispatchStateLocked(); +#endif + return injectionResult; +} + +void InputDispatcher::releaseTouchedWindowLocked() { + mTouchedWindow = NULL; + mTouchedWindowIsObscured = false; + mTouchedWallpaperWindows.clear(); +} + +void InputDispatcher::addWindowTargetLocked(const InputWindow* window, int32_t targetFlags, + nsecs_t timeSpentWaitingForApplication) { + mCurrentInputTargets.push(); + + InputTarget& target = mCurrentInputTargets.editTop(); + target.inputChannel = window->inputChannel; + target.flags = targetFlags; + target.timeout = window->dispatchingTimeout; + target.timeSpentWaitingForApplication = timeSpentWaitingForApplication; + target.xOffset = - window->frameLeft; + target.yOffset = - window->frameTop; +} + +void InputDispatcher::addMonitoringTargetsLocked() { + for (size_t i = 0; i < mMonitoringChannels.size(); i++) { + mCurrentInputTargets.push(); + + InputTarget& target = mCurrentInputTargets.editTop(); + target.inputChannel = mMonitoringChannels[i]; + target.flags = 0; + target.timeout = -1; + target.timeSpentWaitingForApplication = 0; + target.xOffset = 0; + target.yOffset = 0; + } +} + +bool InputDispatcher::checkInjectionPermission(const InputWindow* window, + int32_t injectorPid, int32_t injectorUid) { + if (injectorUid > 0 && (window == NULL || window->ownerUid != injectorUid)) { + bool result = mPolicy->checkInjectEventsPermissionNonReentrant(injectorPid, injectorUid); + if (! result) { + if (window) { + LOGW("Permission denied: injecting event from pid %d uid %d to window " + "with input channel %s owned by uid %d", + injectorPid, injectorUid, window->inputChannel->getName().string(), + window->ownerUid); + } else { + LOGW("Permission denied: injecting event from pid %d uid %d", + injectorPid, injectorUid); + } + return false; + } + } + return true; +} + +bool InputDispatcher::isWindowObscuredLocked(const InputWindow* window) { + size_t numWindows = mWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + const InputWindow* other = & mWindows.itemAt(i); + if (other == window) { + break; + } + if (other->visible && window->visibleFrameIntersects(other)) { + return true; + } + } + return false; +} + +void InputDispatcher::pokeUserActivityLocked(nsecs_t eventTime, + int32_t windowType, int32_t eventType) { + CommandEntry* commandEntry = postCommandLocked( + & InputDispatcher::doPokeUserActivityLockedInterruptible); + commandEntry->eventTime = eventTime; + commandEntry->windowType = windowType; + commandEntry->userActivityEventType = eventType; } void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, @@ -523,15 +1258,21 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, "xOffset=%f, yOffset=%f, resumeWithAppendedMotionSample=%s", connection->getInputChannelName(), inputTarget->flags, inputTarget->timeout, inputTarget->xOffset, inputTarget->yOffset, - resumeWithAppendedMotionSample ? "true" : "false"); + toString(resumeWithAppendedMotionSample)); #endif // Skip this event if the connection status is not normal. - // We don't want to queue outbound events at all if the connection is broken or + // We don't want to enqueue additional outbound events if the connection is broken or // not responding. if (connection->status != Connection::STATUS_NORMAL) { - LOGV("channel '%s' ~ Dropping event because the channel status is %s", - connection->getStatusLabel()); + LOGW("channel '%s' ~ Dropping event because the channel status is %s", + connection->getInputChannelName(), connection->getStatusLabel()); + + // If the connection is not responding but the user is poking the application anyways, + // retrigger the original timeout. + if (connection->status == Connection::STATUS_NOT_RESPONDING) { + timeoutDispatchCycleLocked(currentTime, connection); + } return; } @@ -612,17 +1353,45 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, } } + // Bring the input state back in line with reality in case it drifted off during an ANR. + if (connection->inputState.isOutOfSync()) { + mTempCancelationEvents.clear(); + connection->inputState.synthesizeCancelationEvents(& mAllocator, mTempCancelationEvents); + connection->inputState.resetOutOfSync(); + + if (! mTempCancelationEvents.isEmpty()) { + LOGI("channel '%s' ~ Generated %d cancelation events to bring channel back in sync " + "with reality.", + connection->getInputChannelName(), mTempCancelationEvents.size()); + + for (size_t i = 0; i < mTempCancelationEvents.size(); i++) { + EventEntry* cancelationEventEntry = mTempCancelationEvents.itemAt(i); + switch (cancelationEventEntry->type) { + case EventEntry::TYPE_KEY: + logOutboundKeyDetailsLocked(" ", + static_cast<KeyEntry*>(cancelationEventEntry)); + break; + case EventEntry::TYPE_MOTION: + logOutboundMotionDetailsLocked(" ", + static_cast<MotionEntry*>(cancelationEventEntry)); + break; + } + + DispatchEntry* cancelationDispatchEntry = + mAllocator.obtainDispatchEntry(cancelationEventEntry, + 0, inputTarget->xOffset, inputTarget->yOffset, inputTarget->timeout); + connection->outboundQueue.enqueueAtTail(cancelationDispatchEntry); + + mAllocator.releaseEventEntry(cancelationEventEntry); + } + } + } + // This is a new event. // Enqueue a new dispatch entry onto the outbound queue for this connection. - DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry); // increments ref - dispatchEntry->targetFlags = inputTarget->flags; - dispatchEntry->xOffset = inputTarget->xOffset; - dispatchEntry->yOffset = inputTarget->yOffset; - dispatchEntry->timeout = inputTarget->timeout; - dispatchEntry->inProgress = false; - dispatchEntry->headMotionSample = NULL; - dispatchEntry->tailMotionSample = NULL; - + DispatchEntry* dispatchEntry = mAllocator.obtainDispatchEntry(eventEntry, // increments ref + inputTarget->flags, inputTarget->xOffset, inputTarget->yOffset, + inputTarget->timeout); if (dispatchEntry->isSyncTarget()) { eventEntry->pendingSyncDispatches += 1; } @@ -647,12 +1416,13 @@ void InputDispatcher::prepareDispatchCycleLocked(nsecs_t currentTime, // If the outbound queue was previously empty, start the dispatch cycle going. if (wasEmpty) { activateConnectionLocked(connection.get()); - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(currentTime, connection, + inputTarget->timeSpentWaitingForApplication); } } void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, - const sp<Connection>& connection) { + const sp<Connection>& connection, nsecs_t timeSpentWaitingForApplication) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ startDispatchCycle", connection->getInputChannelName()); @@ -661,12 +1431,37 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, assert(connection->status == Connection::STATUS_NORMAL); assert(! connection->outboundQueue.isEmpty()); - DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; + DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; assert(! dispatchEntry->inProgress); - // TODO throttle successive ACTION_MOVE motion events for the same device - // possible implementation could set a brief poll timeout here and resume starting the - // dispatch cycle when elapsed + // Mark the dispatch entry as in progress. + dispatchEntry->inProgress = true; + + // Update the connection's input state. + InputState::Consistency consistency = connection->inputState.trackEvent( + dispatchEntry->eventEntry); + +#if FILTER_INPUT_EVENTS + // Filter out inconsistent sequences of input events. + // The input system may drop or inject events in a way that could violate implicit + // invariants on input state and potentially cause an application to crash + // or think that a key or pointer is stuck down. Technically we make no guarantees + // of consistency but it would be nice to improve on this where possible. + // XXX: This code is a proof of concept only. Not ready for prime time. + if (consistency == InputState::TOLERABLE) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Sending an event that is inconsistent with the connection's " + "current input state but that is likely to be tolerated by the application.", + connection->getInputChannelName()); +#endif + } else if (consistency == InputState::BROKEN) { + LOGI("channel '%s' ~ Dropping an event that is inconsistent with the connection's " + "current input state and that is likely to cause the application to crash.", + connection->getInputChannelName()); + startNextDispatchCycleLocked(currentTime, connection); + return; + } +#endif // Publish the event. status_t status; @@ -790,12 +1585,10 @@ void InputDispatcher::startDispatchCycleLocked(nsecs_t currentTime, } // Record information about the newly started dispatch cycle. - dispatchEntry->inProgress = true; - connection->lastEventTime = dispatchEntry->eventEntry->eventTime; connection->lastDispatchTime = currentTime; - nsecs_t timeout = dispatchEntry->timeout; + nsecs_t timeout = dispatchEntry->timeout - timeSpentWaitingForApplication; connection->setNextTimeoutTime(currentTime, timeout); // Notify other system components. @@ -844,9 +1637,14 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, return; } + startNextDispatchCycleLocked(currentTime, connection); +} + +void InputDispatcher::startNextDispatchCycleLocked(nsecs_t currentTime, + const sp<Connection>& connection) { // Start the next dispatch cycle for this connection. while (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.head.next; + DispatchEntry* dispatchEntry = connection->outboundQueue.headSentinel.next; if (dispatchEntry->inProgress) { // Finish or resume current event in progress. if (dispatchEntry->tailMotionSample) { @@ -855,7 +1653,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, dispatchEntry->inProgress = false; dispatchEntry->headMotionSample = dispatchEntry->tailMotionSample; dispatchEntry->tailMotionSample = NULL; - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(currentTime, connection, 0); return; } // Finished. @@ -868,7 +1666,7 @@ void InputDispatcher::finishDispatchCycleLocked(nsecs_t currentTime, // If the head is not in progress, then we must have already dequeued the in // progress event, which means we actually aborted it (due to ANR). // So just start the next event for this connection. - startDispatchCycleLocked(currentTime, connection); + startDispatchCycleLocked(currentTime, connection, 0); return; } } @@ -884,58 +1682,74 @@ void InputDispatcher::timeoutDispatchCycleLocked(nsecs_t currentTime, connection->getInputChannelName()); #endif - if (connection->status != Connection::STATUS_NORMAL) { + if (connection->status == Connection::STATUS_NORMAL) { + // Enter the not responding state. + connection->status = Connection::STATUS_NOT_RESPONDING; + connection->lastANRTime = currentTime; + } else if (connection->status != Connection::STATUS_NOT_RESPONDING) { + // Connection is broken or dead. return; } - // Enter the not responding state. - connection->status = Connection::STATUS_NOT_RESPONDING; - connection->lastANRTime = currentTime; - // Notify other system components. - // This enqueues a command which will eventually either call - // resumeAfterTimeoutDispatchCycleLocked or abortDispatchCycleLocked. + // This enqueues a command which will eventually call resumeAfterTimeoutDispatchCycleLocked. onDispatchCycleANRLocked(currentTime, connection); } void InputDispatcher::resumeAfterTimeoutDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, nsecs_t newTimeout) { #if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked", - connection->getInputChannelName()); + LOGD("channel '%s' ~ resumeAfterTimeoutDispatchCycleLocked - newTimeout=%lld", + connection->getInputChannelName(), newTimeout); #endif if (connection->status != Connection::STATUS_NOT_RESPONDING) { return; } - // Resume normal dispatch. - connection->status = Connection::STATUS_NORMAL; - connection->setNextTimeoutTime(currentTime, newTimeout); + if (newTimeout > 0) { + // The system has decided to give the application some more time. + // Keep waiting synchronously and resume normal dispatch. + connection->status = Connection::STATUS_NORMAL; + connection->setNextTimeoutTime(currentTime, newTimeout); + } else { + // The system is about to throw up an ANR dialog and has requested that we abort dispatch. + // Reset the timeout. + connection->nextTimeoutTime = LONG_LONG_MAX; + + // Input state will no longer be realistic. + connection->inputState.setOutOfSync(); + + if (! connection->outboundQueue.isEmpty()) { + // Make the current pending dispatch asynchronous (if it isn't already) so that + // subsequent events can be delivered to the ANR dialog or to another application. + DispatchEntry* currentDispatchEntry = connection->outboundQueue.headSentinel.next; + currentDispatchEntry->preemptSyncTarget(); + + // Drain all but the first entry in the outbound queue. We keep the first entry + // since that is the one that dispatch is stuck on. We throw away the others + // so that we don't spam the application with stale messages if it eventually + // wakes up and recovers from the ANR. + drainOutboundQueueLocked(connection.get(), currentDispatchEntry->next); + } + } } void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, const sp<Connection>& connection, bool broken) { #if DEBUG_DISPATCH_CYCLE LOGD("channel '%s' ~ abortDispatchCycle - broken=%s", - connection->getInputChannelName(), broken ? "true" : "false"); + connection->getInputChannelName(), toString(broken)); #endif // Clear the pending timeout. connection->nextTimeoutTime = LONG_LONG_MAX; - // Clear the outbound queue. - if (! connection->outboundQueue.isEmpty()) { - do { - DispatchEntry* dispatchEntry = connection->outboundQueue.dequeueAtHead(); - if (dispatchEntry->isSyncTarget()) { - decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); - } - mAllocator.releaseDispatchEntry(dispatchEntry); - } while (! connection->outboundQueue.isEmpty()); + // Input state will no longer be realistic. + connection->inputState.setOutOfSync(); - deactivateConnectionLocked(connection.get()); - } + // Clear the outbound queue. + drainOutboundQueueLocked(connection.get(), connection->outboundQueue.headSentinel.next); // Handle the case where the connection appears to be unrecoverably broken. // Ignore already broken or zombie connections. @@ -950,6 +1764,26 @@ void InputDispatcher::abortDispatchCycleLocked(nsecs_t currentTime, } } +void InputDispatcher::drainOutboundQueueLocked(Connection* connection, + DispatchEntry* firstDispatchEntryToDrain) { + for (DispatchEntry* dispatchEntry = firstDispatchEntryToDrain; + dispatchEntry != & connection->outboundQueue.tailSentinel;) { + DispatchEntry* next = dispatchEntry->next; + connection->outboundQueue.dequeue(dispatchEntry); + + if (dispatchEntry->isSyncTarget()) { + decrementPendingSyncDispatchesLocked(dispatchEntry->eventEntry); + } + mAllocator.releaseDispatchEntry(dispatchEntry); + + dispatchEntry = next; + } + + if (connection->outboundQueue.isEmpty()) { + deactivateConnectionLocked(connection); + } +} + bool InputDispatcher::handleReceiveCallback(int receiveFd, int events, void* data) { InputDispatcher* d = static_cast<InputDispatcher*>(data); @@ -1000,57 +1834,19 @@ void InputDispatcher::notifyConfigurationChanged(nsecs_t eventTime) { LOGD("notifyConfigurationChanged - eventTime=%lld", eventTime); #endif - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); ConfigurationChangedEntry* newEntry = mAllocator.obtainConfigurationChangedEntry(eventTime); - - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(newEntry); + needWake = enqueueInboundEventLocked(newEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } } -void InputDispatcher::notifyAppSwitchComing(nsecs_t eventTime) { -#if DEBUG_INBOUND_EVENT_DETAILS - LOGD("notifyAppSwitchComing - eventTime=%lld", eventTime); -#endif - - // Remove movement keys from the queue from most recent to least recent, stopping at the - // first non-movement key. - // TODO: Include a detailed description of why we do this... - - { // acquire lock - AutoMutex _l(mLock); - - for (EventEntry* entry = mInboundQueue.tail.prev; entry != & mInboundQueue.head; ) { - EventEntry* prev = entry->prev; - - if (entry->type == EventEntry::TYPE_KEY) { - KeyEntry* keyEntry = static_cast<KeyEntry*>(entry); - if (isMovementKey(keyEntry->keyCode)) { - LOGV("Dropping movement key during app switch: keyCode=%d, action=%d", - keyEntry->keyCode, keyEntry->action); - mInboundQueue.dequeue(keyEntry); - - setInjectionResultLocked(entry, INPUT_EVENT_INJECTION_FAILED); - - mAllocator.releaseKeyEntry(keyEntry); - } else { - // stop at last non-movement key - break; - } - } - - entry = prev; - } - } // release lock -} - void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t source, uint32_t policyFlags, int32_t action, int32_t flags, int32_t keyCode, int32_t scanCode, int32_t metaState, nsecs_t downTime) { @@ -1061,7 +1857,7 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou keyCode, scanCode, metaState, downTime); #endif - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); @@ -1070,11 +1866,10 @@ void InputDispatcher::notifyKey(nsecs_t eventTime, int32_t deviceId, int32_t sou deviceId, source, policyFlags, action, flags, keyCode, scanCode, metaState, repeatCount, downTime); - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(newEntry); + needWake = enqueueInboundEventLocked(newEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } } @@ -1101,7 +1896,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t } #endif - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); @@ -1112,8 +1907,8 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t // Try to append a move sample to the tail of the inbound queue for this device. // Give up if we encounter a non-move motion event for this device since that // means we cannot append any new samples until a new motion event has started. - for (EventEntry* entry = mInboundQueue.tail.prev; - entry != & mInboundQueue.head; entry = entry->prev) { + for (EventEntry* entry = mInboundQueue.tailSentinel.prev; + entry != & mInboundQueue.headSentinel; entry = entry->prev) { if (entry->type != EventEntry::TYPE_MOTION) { // Keep looking for motion events. continue; @@ -1140,18 +1935,6 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t LOGD("Appended motion sample onto batch for most recent " "motion event for this device in the inbound queue."); #endif - - // Sanity check for special case because dispatch is interruptible. - // The dispatch logic is partially interruptible and releases its lock while - // identifying targets. However, as soon as the targets have been identified, - // the dispatcher proceeds to write a dispatch entry into all relevant outbound - // queues and then promptly removes the motion entry from the queue. - // - // Consequently, we should never observe the case where the inbound queue contains - // an in-progress motion entry unless the current input targets are invalid - // (currently being computed). Check for this! - assert(! (motionEntry->dispatchInProgress && mCurrentInputTargetsValid)); - return; // done! } @@ -1178,7 +1961,7 @@ void InputDispatcher::notifyMotion(nsecs_t eventTime, int32_t deviceId, int32_t for (size_t i = 0; i < mActiveConnections.size(); i++) { Connection* connection = mActiveConnections.itemAt(i); if (! connection->outboundQueue.isEmpty()) { - DispatchEntry* dispatchEntry = connection->outboundQueue.tail.prev; + DispatchEntry* dispatchEntry = connection->outboundQueue.tailSentinel.prev; if (dispatchEntry->isSyncTarget()) { if (dispatchEntry->eventEntry->type != EventEntry::TYPE_MOTION) { goto NoBatchingOrStreaming; @@ -1220,11 +2003,10 @@ NoBatchingOrStreaming:; xPrecision, yPrecision, downTime, pointerCount, pointerIds, pointerCoords); - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(newEntry); + needWake = enqueueInboundEventLocked(newEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } } @@ -1240,11 +2022,15 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, nsecs_t endTime = now() + milliseconds_to_nanoseconds(timeoutMillis); EventEntry* injectedEntry; - bool wasEmpty; + bool needWake; { // acquire lock AutoMutex _l(mLock); - injectedEntry = createEntryFromInputEventLocked(event); + injectedEntry = createEntryFromInjectedInputEventLocked(event); + if (! injectedEntry) { + return INPUT_EVENT_INJECTION_FAILED; + } + injectedEntry->refCount += 1; injectedEntry->injectorPid = injectorPid; injectedEntry->injectorUid = injectorUid; @@ -1253,12 +2039,10 @@ int32_t InputDispatcher::injectInputEvent(const InputEvent* event, injectedEntry->injectionIsAsync = true; } - wasEmpty = mInboundQueue.isEmpty(); - mInboundQueue.enqueueAtTail(injectedEntry); - + needWake = enqueueInboundEventLocked(injectedEntry); } // release lock - if (wasEmpty) { + if (needWake) { mPollLoop->wake(); } @@ -1361,11 +2145,42 @@ void InputDispatcher::decrementPendingSyncDispatchesLocked(EventEntry* entry) { } } -InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( +static bool isValidKeyAction(int32_t action) { + switch (action) { + case AKEY_EVENT_ACTION_DOWN: + case AKEY_EVENT_ACTION_UP: + return true; + default: + return false; + } +} + +static bool isValidMotionAction(int32_t action) { + switch (action & AMOTION_EVENT_ACTION_MASK) { + case AMOTION_EVENT_ACTION_DOWN: + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + case AMOTION_EVENT_ACTION_MOVE: + case AMOTION_EVENT_ACTION_POINTER_DOWN: + case AMOTION_EVENT_ACTION_POINTER_UP: + case AMOTION_EVENT_ACTION_OUTSIDE: + return true; + default: + return false; + } +} + +InputDispatcher::EventEntry* InputDispatcher::createEntryFromInjectedInputEventLocked( const InputEvent* event) { switch (event->getType()) { case AINPUT_EVENT_TYPE_KEY: { const KeyEvent* keyEvent = static_cast<const KeyEvent*>(event); + if (! isValidKeyAction(keyEvent->getAction())) { + LOGE("Dropping injected key event since it has invalid action code 0x%x", + keyEvent->getAction()); + return NULL; + } + uint32_t policyFlags = POLICY_FLAG_INJECTED; KeyEntry* keyEntry = mAllocator.obtainKeyEntry(keyEvent->getEventTime(), @@ -1378,6 +2193,17 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( case AINPUT_EVENT_TYPE_MOTION: { const MotionEvent* motionEvent = static_cast<const MotionEvent*>(event); + if (! isValidMotionAction(motionEvent->getAction())) { + LOGE("Dropping injected motion event since it has invalid action code 0x%x.", + motionEvent->getAction()); + return NULL; + } + if (motionEvent->getPointerCount() == 0 + || motionEvent->getPointerCount() > MAX_POINTERS) { + LOGE("Dropping injected motion event since it has an invalid pointer count %d.", + motionEvent->getPointerCount()); + } + uint32_t policyFlags = POLICY_FLAG_INJECTED; const nsecs_t* sampleEventTimes = motionEvent->getSampleEventTimes(); @@ -1405,33 +2231,143 @@ InputDispatcher::EventEntry* InputDispatcher::createEntryFromInputEventLocked( } } -void InputDispatcher::resetKeyRepeatLocked() { - if (mKeyRepeatState.lastKeyEntry) { - mAllocator.releaseKeyEntry(mKeyRepeatState.lastKeyEntry); - mKeyRepeatState.lastKeyEntry = NULL; +void InputDispatcher::setInputWindows(const Vector<InputWindow>& inputWindows) { +#if DEBUG_FOCUS + LOGD("setInputWindows"); +#endif + { // acquire lock + AutoMutex _l(mLock); + + sp<InputChannel> touchedWindowChannel; + if (mTouchedWindow) { + touchedWindowChannel = mTouchedWindow->inputChannel; + mTouchedWindow = NULL; + } + size_t numTouchedWallpapers = mTouchedWallpaperWindows.size(); + if (numTouchedWallpapers != 0) { + for (size_t i = 0; i < numTouchedWallpapers; i++) { + mTempTouchedWallpaperChannels.push(mTouchedWallpaperWindows[i]->inputChannel); + } + mTouchedWallpaperWindows.clear(); + } + + bool hadFocusedWindow = mFocusedWindow != NULL; + + mFocusedWindow = NULL; + mWallpaperWindows.clear(); + + mWindows.clear(); + mWindows.appendVector(inputWindows); + + size_t numWindows = mWindows.size(); + for (size_t i = 0; i < numWindows; i++) { + InputWindow* window = & mWindows.editItemAt(i); + if (window->hasFocus) { + mFocusedWindow = window; + } + + if (window->layoutParamsType == InputWindow::TYPE_WALLPAPER) { + mWallpaperWindows.push(window); + + for (size_t j = 0; j < numTouchedWallpapers; j++) { + if (window->inputChannel == mTempTouchedWallpaperChannels[i]) { + mTouchedWallpaperWindows.push(window); + } + } + } + + if (window->inputChannel == touchedWindowChannel) { + mTouchedWindow = window; + } + } + + mTempTouchedWallpaperChannels.clear(); + + if ((hadFocusedWindow && ! mFocusedWindow) + || (mFocusedWindow && ! mFocusedWindow->visible)) { + preemptInputDispatchInnerLocked(); + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mPollLoop->wake(); +} + +void InputDispatcher::setFocusedApplication(const InputApplication* inputApplication) { +#if DEBUG_FOCUS + LOGD("setFocusedApplication"); +#endif + { // acquire lock + AutoMutex _l(mLock); + + releaseFocusedApplicationLocked(); + + if (inputApplication) { + mFocusedApplicationStorage = *inputApplication; + mFocusedApplication = & mFocusedApplicationStorage; + } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + // Wake up poll loop since it may need to make new input dispatching choices. + mPollLoop->wake(); +} + +void InputDispatcher::releaseFocusedApplicationLocked() { + if (mFocusedApplication) { + mFocusedApplication = NULL; + mFocusedApplicationStorage.handle.clear(); } } -void InputDispatcher::preemptInputDispatch() { -#if DEBUG_DISPATCH_CYCLE - LOGD("preemptInputDispatch"); +void InputDispatcher::setInputDispatchMode(bool enabled, bool frozen) { +#if DEBUG_FOCUS + LOGD("setInputDispatchMode: enabled=%d, frozen=%d", enabled, frozen); #endif - bool preemptedOne = false; + bool changed; { // acquire lock AutoMutex _l(mLock); - for (size_t i = 0; i < mActiveConnections.size(); i++) { - Connection* connection = mActiveConnections[i]; - if (connection->hasPendingSyncTarget()) { -#if DEBUG_DISPATCH_CYCLE - LOGD("channel '%s' ~ Preempted pending synchronous dispatch", - connection->getInputChannelName()); -#endif - connection->outboundQueue.tail.prev->targetFlags &= ~ InputTarget::FLAG_SYNC; - preemptedOne = true; + if (mDispatchEnabled != enabled || mDispatchFrozen != frozen) { + if (mDispatchFrozen && ! frozen) { + resetANRTimeoutsLocked(); } + + mDispatchEnabled = enabled; + mDispatchFrozen = frozen; + changed = true; + } else { + changed = false; } + +#if DEBUG_FOCUS + logDispatchStateLocked(); +#endif + } // release lock + + if (changed) { + // Wake up poll loop since it may need to make new input dispatching choices. + mPollLoop->wake(); + } +} + +void InputDispatcher::preemptInputDispatch() { +#if DEBUG_FOCUS + LOGD("preemptInputDispatch"); +#endif + + bool preemptedOne; + { // acquire lock + AutoMutex _l(mLock); + preemptedOne = preemptInputDispatchInnerLocked(); } // release lock if (preemptedOne) { @@ -1440,9 +2376,99 @@ void InputDispatcher::preemptInputDispatch() { } } -status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel) { +bool InputDispatcher::preemptInputDispatchInnerLocked() { + bool preemptedOne = false; + for (size_t i = 0; i < mActiveConnections.size(); i++) { + Connection* connection = mActiveConnections[i]; + if (connection->hasPendingSyncTarget()) { +#if DEBUG_DISPATCH_CYCLE + LOGD("channel '%s' ~ Preempted pending synchronous dispatch", + connection->getInputChannelName()); +#endif + connection->preemptSyncTarget(); + preemptedOne = true; + } + } + return preemptedOne; +} + +void InputDispatcher::logDispatchStateLocked() { + String8 dump; + dumpDispatchStateLocked(dump); + LOGD("%s", dump.string()); +} + +void InputDispatcher::dumpDispatchStateLocked(String8& dump) { + dump.appendFormat(" dispatchEnabled: %d\n", mDispatchEnabled); + dump.appendFormat(" dispatchFrozen: %d\n", mDispatchFrozen); + + if (mFocusedApplication) { + dump.appendFormat(" focusedApplication: name='%s', dispatchingTimeout=%0.3fms\n", + mFocusedApplication->name.string(), + mFocusedApplication->dispatchingTimeout / 1000000.0); + } else { + dump.append(" focusedApplication: <null>\n"); + } + dump.appendFormat(" focusedWindow: '%s'\n", + mFocusedWindow != NULL ? mFocusedWindow->inputChannel->getName().string() : "<null>"); + dump.appendFormat(" touchedWindow: '%s', touchDown=%d\n", + mTouchedWindow != NULL ? mTouchedWindow->inputChannel->getName().string() : "<null>", + mTouchDown); + for (size_t i = 0; i < mTouchedWallpaperWindows.size(); i++) { + dump.appendFormat(" touchedWallpaperWindows[%d]: '%s'\n", + i, mTouchedWallpaperWindows[i]->inputChannel->getName().string()); + } + for (size_t i = 0; i < mWindows.size(); i++) { + dump.appendFormat(" windows[%d]: '%s', paused=%s, hasFocus=%s, hasWallpaper=%s, " + "visible=%s, flags=0x%08x, type=0x%08x, " + "frame=[%d,%d][%d,%d], " + "visibleFrame=[%d,%d][%d,%d], " + "touchableArea=[%d,%d][%d,%d], " + "ownerPid=%d, ownerUid=%d, dispatchingTimeout=%0.3fms\n", + i, mWindows[i].inputChannel->getName().string(), + toString(mWindows[i].paused), + toString(mWindows[i].hasFocus), + toString(mWindows[i].hasWallpaper), + toString(mWindows[i].visible), + mWindows[i].layoutParamsFlags, mWindows[i].layoutParamsType, + mWindows[i].frameLeft, mWindows[i].frameTop, + mWindows[i].frameRight, mWindows[i].frameBottom, + mWindows[i].visibleFrameLeft, mWindows[i].visibleFrameTop, + mWindows[i].visibleFrameRight, mWindows[i].visibleFrameBottom, + mWindows[i].touchableAreaLeft, mWindows[i].touchableAreaTop, + mWindows[i].touchableAreaRight, mWindows[i].touchableAreaBottom, + mWindows[i].ownerPid, mWindows[i].ownerUid, + mWindows[i].dispatchingTimeout / 1000000.0); + } + + for (size_t i = 0; i < mMonitoringChannels.size(); i++) { + const sp<InputChannel>& channel = mMonitoringChannels[i]; + dump.appendFormat(" monitoringChannel[%d]: '%s'\n", + i, channel->getName().string()); + } + + for (size_t i = 0; i < mActiveConnections.size(); i++) { + const Connection* connection = mActiveConnections[i]; + dump.appendFormat(" activeConnection[%d]: '%s', status=%s, hasPendingSyncTarget=%s, " + "inputState.isNeutral=%s, inputState.isOutOfSync=%s\n", + i, connection->getInputChannelName(), connection->getStatusLabel(), + toString(connection->hasPendingSyncTarget()), + toString(connection->inputState.isNeutral()), + toString(connection->inputState.isOutOfSync())); + } + + if (isAppSwitchPendingLocked()) { + dump.appendFormat(" appSwitch: pending, due in %01.1fms\n", + (mAppSwitchDueTime - now()) / 1000000.0); + } else { + dump.append(" appSwitch: not pending\n"); + } +} + +status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChannel, bool monitor) { #if DEBUG_REGISTRATION - LOGD("channel '%s' ~ registerInputChannel", inputChannel->getName().string()); + LOGD("channel '%s' ~ registerInputChannel - monitor=%s", inputChannel->getName().string(), + toString(monitor)); #endif { // acquire lock @@ -1465,6 +2491,10 @@ status_t InputDispatcher::registerInputChannel(const sp<InputChannel>& inputChan int32_t receiveFd = inputChannel->getReceivePipeFd(); mConnectionsByReceiveFd.add(receiveFd, connection); + if (monitor) { + mMonitoringChannels.push(inputChannel); + } + mPollLoop->setCallback(receiveFd, POLLIN, handleReceiveCallback, this); runCommandsLockedInterruptible(); @@ -1492,6 +2522,13 @@ status_t InputDispatcher::unregisterInputChannel(const sp<InputChannel>& inputCh connection->status = Connection::STATUS_ZOMBIE; + for (size_t i = 0; i < mMonitoringChannels.size(); i++) { + if (mMonitoringChannels[i] == inputChannel) { + mMonitoringChannels.removeAt(i); + break; + } + } + mPollLoop->removeCallback(inputChannel->getReceivePipeFd()); nsecs_t currentTime = now(); @@ -1578,6 +2615,15 @@ void InputDispatcher::onDispatchCycleBrokenLocked( commandEntry->connection = connection; } +void InputDispatcher::doNotifyConfigurationChangedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->notifyConfigurationChanged(commandEntry->eventTime); + + mLock.lock(); +} + void InputDispatcher::doNotifyInputChannelBrokenLockedInterruptible( CommandEntry* commandEntry) { sp<Connection> connection = commandEntry->connection; @@ -1598,17 +2644,12 @@ void InputDispatcher::doNotifyInputChannelANRLockedInterruptible( if (connection->status != Connection::STATUS_ZOMBIE) { mLock.unlock(); - nsecs_t newTimeout; - bool resume = mPolicy->notifyInputChannelANR(connection->inputChannel, newTimeout); + nsecs_t newTimeout = mPolicy->notifyInputChannelANR(connection->inputChannel); mLock.lock(); nsecs_t currentTime = now(); - if (resume) { - resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); - } else { - abortDispatchCycleLocked(currentTime, connection, false /*(not) broken*/); - } + resumeAfterTimeoutDispatchCycleLocked(currentTime, connection, newTimeout); } } @@ -1625,6 +2666,57 @@ void InputDispatcher::doNotifyInputChannelRecoveredFromANRLockedInterruptible( } } +void InputDispatcher::doInterceptKeyBeforeDispatchingLockedInterruptible( + CommandEntry* commandEntry) { + KeyEntry* entry = commandEntry->keyEntry; + mReusableKeyEvent.initialize(entry->deviceId, entry->source, entry->action, entry->flags, + entry->keyCode, entry->scanCode, entry->metaState, entry->repeatCount, + entry->downTime, entry->eventTime); + + mLock.unlock(); + + bool consumed = mPolicy->interceptKeyBeforeDispatching(commandEntry->inputChannel, + & mReusableKeyEvent, entry->policyFlags); + + mLock.lock(); + + entry->interceptKeyResult = consumed + ? KeyEntry::INTERCEPT_KEY_RESULT_SKIP + : KeyEntry::INTERCEPT_KEY_RESULT_CONTINUE; + mAllocator.releaseKeyEntry(entry); +} + +void InputDispatcher::doPokeUserActivityLockedInterruptible(CommandEntry* commandEntry) { + mLock.unlock(); + + mPolicy->pokeUserActivity(commandEntry->eventTime, commandEntry->windowType, + commandEntry->userActivityEventType); + + mLock.lock(); +} + +void InputDispatcher::doTargetsNotReadyTimeoutLockedInterruptible( + CommandEntry* commandEntry) { + mLock.unlock(); + + nsecs_t newTimeout; + if (commandEntry->inputChannel.get()) { + newTimeout = mPolicy->notifyInputChannelANR(commandEntry->inputChannel); + } else if (commandEntry->inputApplicationHandle.get()) { + newTimeout = mPolicy->notifyANR(commandEntry->inputApplicationHandle); + } else { + newTimeout = 0; + } + + mLock.lock(); + + resumeAfterTargetsNotReadyTimeoutLocked(newTimeout); +} + +void InputDispatcher::dump(String8& dump) { + dumpDispatchStateLocked(dump); +} + // --- InputDispatcher::Allocator --- @@ -1668,6 +2760,8 @@ InputDispatcher::KeyEntry* InputDispatcher::Allocator::obtainKeyEntry(nsecs_t ev entry->metaState = metaState; entry->repeatCount = repeatCount; entry->downTime = downTime; + entry->syntheticRepeat = false; + entry->interceptKeyResult = KeyEntry::INTERCEPT_KEY_RESULT_UNKNOWN; return entry; } @@ -1702,10 +2796,18 @@ InputDispatcher::MotionEntry* InputDispatcher::Allocator::obtainMotionEntry(nsec } InputDispatcher::DispatchEntry* InputDispatcher::Allocator::obtainDispatchEntry( - EventEntry* eventEntry) { + EventEntry* eventEntry, + int32_t targetFlags, float xOffset, float yOffset, nsecs_t timeout) { DispatchEntry* entry = mDispatchEntryPool.alloc(); entry->eventEntry = eventEntry; eventEntry->refCount += 1; + entry->targetFlags = targetFlags; + entry->xOffset = xOffset; + entry->yOffset = yOffset; + entry->timeout = timeout; + entry->inProgress = false; + entry->headMotionSample = NULL; + entry->tailMotionSample = NULL; return entry; } @@ -1788,6 +2890,25 @@ void InputDispatcher::Allocator::appendMotionSample(MotionEntry* motionEntry, motionEntry->lastSample = sample; } + +// --- InputDispatcher::EventEntry --- + +void InputDispatcher::EventEntry::recycle() { + injectionResult = INPUT_EVENT_INJECTION_PENDING; + dispatchInProgress = false; + pendingSyncDispatches = 0; +} + + +// --- InputDispatcher::KeyEntry --- + +void InputDispatcher::KeyEntry::recycle() { + EventEntry::recycle(); + syntheticRepeat = false; + interceptKeyResult = INTERCEPT_KEY_RESULT_UNKNOWN; +} + + // --- InputDispatcher::MotionEntry --- uint32_t InputDispatcher::MotionEntry::countSamples() const { @@ -1798,6 +2919,189 @@ uint32_t InputDispatcher::MotionEntry::countSamples() const { return count; } + +// --- InputDispatcher::InputState --- + +InputDispatcher::InputState::InputState() : + mIsOutOfSync(false) { +} + +InputDispatcher::InputState::~InputState() { +} + +bool InputDispatcher::InputState::isNeutral() const { + return mKeyMementos.isEmpty() && mMotionMementos.isEmpty(); +} + +bool InputDispatcher::InputState::isOutOfSync() const { + return mIsOutOfSync; +} + +void InputDispatcher::InputState::setOutOfSync() { + if (! isNeutral()) { + mIsOutOfSync = true; + } +} + +void InputDispatcher::InputState::resetOutOfSync() { + mIsOutOfSync = false; +} + +InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackEvent( + const EventEntry* entry) { + switch (entry->type) { + case EventEntry::TYPE_KEY: + return trackKey(static_cast<const KeyEntry*>(entry)); + + case EventEntry::TYPE_MOTION: + return trackMotion(static_cast<const MotionEntry*>(entry)); + + default: + return CONSISTENT; + } +} + +InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackKey( + const KeyEntry* entry) { + int32_t action = entry->action; + for (size_t i = 0; i < mKeyMementos.size(); i++) { + KeyMemento& memento = mKeyMementos.editItemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source + && memento.keyCode == entry->keyCode + && memento.scanCode == entry->scanCode) { + switch (action) { + case AKEY_EVENT_ACTION_UP: + mKeyMementos.removeAt(i); + if (isNeutral()) { + mIsOutOfSync = false; + } + return CONSISTENT; + + case AKEY_EVENT_ACTION_DOWN: + return TOLERABLE; + + default: + return BROKEN; + } + } + } + + switch (action) { + case AKEY_EVENT_ACTION_DOWN: { + mKeyMementos.push(); + KeyMemento& memento = mKeyMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.keyCode = entry->keyCode; + memento.scanCode = entry->scanCode; + memento.downTime = entry->downTime; + return CONSISTENT; + } + + default: + return BROKEN; + } +} + +InputDispatcher::InputState::Consistency InputDispatcher::InputState::trackMotion( + const MotionEntry* entry) { + int32_t action = entry->action & AMOTION_EVENT_ACTION_MASK; + for (size_t i = 0; i < mMotionMementos.size(); i++) { + MotionMemento& memento = mMotionMementos.editItemAt(i); + if (memento.deviceId == entry->deviceId + && memento.source == entry->source) { + switch (action) { + case AMOTION_EVENT_ACTION_UP: + case AMOTION_EVENT_ACTION_CANCEL: + mMotionMementos.removeAt(i); + if (isNeutral()) { + mIsOutOfSync = false; + } + return CONSISTENT; + + case AMOTION_EVENT_ACTION_DOWN: + return TOLERABLE; + + case AMOTION_EVENT_ACTION_POINTER_DOWN: + if (entry->pointerCount == memento.pointerCount + 1) { + memento.setPointers(entry); + return CONSISTENT; + } + return BROKEN; + + case AMOTION_EVENT_ACTION_POINTER_UP: + if (entry->pointerCount == memento.pointerCount - 1) { + memento.setPointers(entry); + return CONSISTENT; + } + return BROKEN; + + case AMOTION_EVENT_ACTION_MOVE: + if (entry->pointerCount == memento.pointerCount) { + return CONSISTENT; + } + return BROKEN; + + default: + return BROKEN; + } + } + } + + switch (action) { + case AMOTION_EVENT_ACTION_DOWN: { + mMotionMementos.push(); + MotionMemento& memento = mMotionMementos.editTop(); + memento.deviceId = entry->deviceId; + memento.source = entry->source; + memento.xPrecision = entry->xPrecision; + memento.yPrecision = entry->yPrecision; + memento.downTime = entry->downTime; + memento.setPointers(entry); + return CONSISTENT; + } + + default: + return BROKEN; + } +} + +void InputDispatcher::InputState::MotionMemento::setPointers(const MotionEntry* entry) { + pointerCount = entry->pointerCount; + for (uint32_t i = 0; i < entry->pointerCount; i++) { + pointerIds[i] = entry->pointerIds[i]; + pointerCoords[i] = entry->lastSample->pointerCoords[i]; + } +} + +void InputDispatcher::InputState::synthesizeCancelationEvents( + Allocator* allocator, Vector<EventEntry*>& outEvents) const { + for (size_t i = 0; i < mKeyMementos.size(); i++) { + const KeyMemento& memento = mKeyMementos.itemAt(i); + outEvents.push(allocator->obtainKeyEntry(now(), + memento.deviceId, memento.source, 0, + AKEY_EVENT_ACTION_UP, AKEY_EVENT_FLAG_CANCELED, + memento.keyCode, memento.scanCode, 0, 0, memento.downTime)); + } + + for (size_t i = 0; i < mMotionMementos.size(); i++) { + const MotionMemento& memento = mMotionMementos.itemAt(i); + outEvents.push(allocator->obtainMotionEntry(now(), + memento.deviceId, memento.source, 0, + AMOTION_EVENT_ACTION_CANCEL, 0, 0, 0, + memento.xPrecision, memento.yPrecision, memento.downTime, + memento.pointerCount, memento.pointerIds, memento.pointerCoords)); + } +} + +void InputDispatcher::InputState::clear() { + mKeyMementos.clear(); + mMotionMementos.clear(); + mIsOutOfSync = false; +} + + // --- InputDispatcher::Connection --- InputDispatcher::Connection::Connection(const sp<InputChannel>& inputChannel) : @@ -1818,6 +3122,14 @@ void InputDispatcher::Connection::setNextTimeoutTime(nsecs_t currentTime, nsecs_ nextTimeoutTime = (timeout >= 0) ? currentTime + timeout : LONG_LONG_MAX; } +void InputDispatcher::Connection::resetTimeout(nsecs_t currentTime) { + if (outboundQueue.isEmpty()) { + nextTimeoutTime = LONG_LONG_MAX; + } else { + setNextTimeoutTime(currentTime, outboundQueue.headSentinel.next->timeout); + } +} + const char* InputDispatcher::Connection::getStatusLabel() const { switch (status) { case STATUS_NORMAL: @@ -1839,8 +3151,8 @@ const char* InputDispatcher::Connection::getStatusLabel() const { InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchEntryForEvent( const EventEntry* eventEntry) const { - for (DispatchEntry* dispatchEntry = outboundQueue.tail.prev; - dispatchEntry != & outboundQueue.head; dispatchEntry = dispatchEntry->prev) { + for (DispatchEntry* dispatchEntry = outboundQueue.tailSentinel.prev; + dispatchEntry != & outboundQueue.headSentinel; dispatchEntry = dispatchEntry->prev) { if (dispatchEntry->eventEntry == eventEntry) { return dispatchEntry; } @@ -1848,9 +3160,11 @@ InputDispatcher::DispatchEntry* InputDispatcher::Connection::findQueuedDispatchE return NULL; } + // --- InputDispatcher::CommandEntry --- -InputDispatcher::CommandEntry::CommandEntry() { +InputDispatcher::CommandEntry::CommandEntry() : + keyEntry(NULL) { } InputDispatcher::CommandEntry::~CommandEntry() { diff --git a/libs/ui/InputManager.cpp b/libs/ui/InputManager.cpp index ed4f07b..09fce38 100644 --- a/libs/ui/InputManager.cpp +++ b/libs/ui/InputManager.cpp @@ -72,52 +72,12 @@ status_t InputManager::stop() { return OK; } -status_t InputManager::registerInputChannel(const sp<InputChannel>& inputChannel) { - return mDispatcher->registerInputChannel(inputChannel); +sp<InputReaderInterface> InputManager::getReader() { + return mReader; } -status_t InputManager::unregisterInputChannel(const sp<InputChannel>& inputChannel) { - return mDispatcher->unregisterInputChannel(inputChannel); -} - -int32_t InputManager::injectInputEvent(const InputEvent* event, - int32_t injectorPid, int32_t injectorUid, int32_t syncMode, int32_t timeoutMillis) { - return mDispatcher->injectInputEvent(event, injectorPid, injectorUid, syncMode, timeoutMillis); -} - -void InputManager::preemptInputDispatch() { - mDispatcher->preemptInputDispatch(); -} - -void InputManager::getInputConfiguration(InputConfiguration* outConfiguration) { - mReader->getInputConfiguration(outConfiguration); -} - -status_t InputManager::getInputDeviceInfo(int32_t deviceId, InputDeviceInfo* outDeviceInfo) { - return mReader->getInputDeviceInfo(deviceId, outDeviceInfo); -} - -void InputManager::getInputDeviceIds(Vector<int32_t>& outDeviceIds) { - mReader->getInputDeviceIds(outDeviceIds); -} - -int32_t InputManager::getScanCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t scanCode) { - return mReader->getScanCodeState(deviceId, sourceMask, scanCode); -} - -int32_t InputManager::getKeyCodeState(int32_t deviceId, uint32_t sourceMask, - int32_t keyCode) { - return mReader->getKeyCodeState(deviceId, sourceMask, keyCode); -} - -int32_t InputManager::getSwitchState(int32_t deviceId, uint32_t sourceMask, int32_t sw) { - return mReader->getSwitchState(deviceId, sourceMask, sw); -} - -bool InputManager::hasKeys(int32_t deviceId, uint32_t sourceMask, - size_t numCodes, const int32_t* keyCodes, uint8_t* outFlags) { - return mReader->hasKeys(deviceId, sourceMask, numCodes, keyCodes, outFlags); +sp<InputDispatcherInterface> InputManager::getDispatcher() { + return mDispatcher; } } // namespace android diff --git a/libs/ui/InputReader.cpp b/libs/ui/InputReader.cpp index d57b38c..88084c0 100644 --- a/libs/ui/InputReader.cpp +++ b/libs/ui/InputReader.cpp @@ -573,6 +573,60 @@ bool InputReader::markSupportedKeyCodes(int32_t deviceId, uint32_t sourceMask, s } // release device registy reader lock } +void InputReader::dump(String8& dump) { + dumpDeviceInfo(dump); +} + +static void dumpMotionRange(String8& dump, + const char* name, const InputDeviceInfo::MotionRange* range) { + if (range) { + dump.appendFormat(" %s = { min: %0.3f, max: %0.3f, flat: %0.3f, fuzz: %0.3f }\n", + name, range->min, range->max, range->flat, range->fuzz); + } +} + +#define DUMP_MOTION_RANGE(range) \ + dumpMotionRange(dump, #range, deviceInfo.getMotionRange(AINPUT_MOTION_RANGE_##range)); + +void InputReader::dumpDeviceInfo(String8& dump) { + Vector<int32_t> deviceIds; + getInputDeviceIds(deviceIds); + + InputDeviceInfo deviceInfo; + for (size_t i = 0; i < deviceIds.size(); i++) { + int32_t deviceId = deviceIds[i]; + + status_t result = getInputDeviceInfo(deviceId, & deviceInfo); + if (result == NAME_NOT_FOUND) { + continue; + } else if (result != OK) { + dump.appendFormat(" ** Unexpected error %d getting information about input devices.\n", + result); + continue; + } + + dump.appendFormat(" Device %d: '%s'\n", + deviceInfo.getId(), deviceInfo.getName().string()); + dump.appendFormat(" sources = 0x%08x\n", + deviceInfo.getSources()); + dump.appendFormat(" keyboardType = %d\n", + deviceInfo.getKeyboardType()); + + dump.append(" motion ranges:\n"); + DUMP_MOTION_RANGE(X); + DUMP_MOTION_RANGE(Y); + DUMP_MOTION_RANGE(PRESSURE); + DUMP_MOTION_RANGE(SIZE); + DUMP_MOTION_RANGE(TOUCH_MAJOR); + DUMP_MOTION_RANGE(TOUCH_MINOR); + DUMP_MOTION_RANGE(TOOL_MAJOR); + DUMP_MOTION_RANGE(TOOL_MINOR); + DUMP_MOTION_RANGE(ORIENTATION); + } +} + +#undef DUMP_MOTION_RANGE + // --- InputReaderThread --- @@ -740,10 +794,6 @@ int32_t InputMapper::getMetaState() { } bool InputMapper::applyStandardPolicyActions(nsecs_t when, int32_t policyActions) { - if (policyActions & InputReaderPolicyInterface::ACTION_APP_SWITCH_COMING) { - getDispatcher()->notifyAppSwitchComing(when); - } - return policyActions & InputReaderPolicyInterface::ACTION_DISPATCH; } @@ -1249,20 +1299,12 @@ void TouchInputMapper::initializeLocked() { mLocked.orientedRanges.haveOrientation = false; } -static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) { - if (axis.valid) { - LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d", - name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); - } else { - LOGI(INDENT "Raw %s axis: unknown range", name); - } -} - void TouchInputMapper::configure() { InputMapper::configure(); // Configure basic parameters. configureParameters(); + logParameters(); // Configure absolute axis information. configureRawAxes(); @@ -1287,6 +1329,18 @@ void TouchInputMapper::configureParameters() { mParameters.useJumpyTouchFilter = getPolicy()->filterJumpyTouchEvents(); } +void TouchInputMapper::logParameters() { + if (mParameters.useBadTouchFilter) { + LOGI(INDENT "Bad touch filter enabled."); + } + if (mParameters.useAveragingTouchFilter) { + LOGI(INDENT "Averaging touch filter enabled."); + } + if (mParameters.useJumpyTouchFilter) { + LOGI(INDENT "Jumpy touch filter enabled."); + } +} + void TouchInputMapper::configureRawAxes() { mRawAxes.x.clear(); mRawAxes.y.clear(); @@ -1298,6 +1352,15 @@ void TouchInputMapper::configureRawAxes() { mRawAxes.orientation.clear(); } +static void logAxisInfo(RawAbsoluteAxisInfo axis, const char* name) { + if (axis.valid) { + LOGI(INDENT "Raw %s axis: min=%d, max=%d, flat=%d, fuzz=%d", + name, axis.minValue, axis.maxValue, axis.flat, axis.fuzz); + } else { + LOGI(INDENT "Raw %s axis: unknown range", name); + } +} + void TouchInputMapper::logRawAxes() { logAxisInfo(mRawAxes.x, "x"); logAxisInfo(mRawAxes.y, "y"); @@ -1331,8 +1394,10 @@ bool TouchInputMapper::configureSurfaceLocked() { bool sizeChanged = mLocked.surfaceWidth != width || mLocked.surfaceHeight != height; if (sizeChanged) { - LOGI("Device configured: id=0x%x, name=%s (display size was changed)", + LOGI("Device reconfigured (display size changed): id=0x%x, name=%s", getDeviceId(), getDeviceName().string()); + LOGI(INDENT "Width: %dpx", width); + LOGI(INDENT "Height: %dpx", height); mLocked.surfaceWidth = width; mLocked.surfaceHeight = height; @@ -1500,9 +1565,41 @@ bool TouchInputMapper::configureSurfaceLocked() { mLocked.orientedRanges.y.fuzz = orientedYScale; } + if (sizeChanged) { + logMotionRangesLocked(); + } + return true; } +static void logMotionRangeInfo(InputDeviceInfo::MotionRange* range, const char* name) { + if (range) { + LOGI(INDENT "Output %s range: min=%f, max=%f, flat=%f, fuzz=%f", + name, range->min, range->max, range->flat, range->fuzz); + } else { + LOGI(INDENT "Output %s range: unsupported", name); + } +} + +void TouchInputMapper::logMotionRangesLocked() { + logMotionRangeInfo(& mLocked.orientedRanges.x, "x"); + logMotionRangeInfo(& mLocked.orientedRanges.y, "y"); + logMotionRangeInfo(mLocked.orientedRanges.havePressure + ? & mLocked.orientedRanges.pressure : NULL, "pressure"); + logMotionRangeInfo(mLocked.orientedRanges.haveSize + ? & mLocked.orientedRanges.size : NULL, "size"); + logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea + ? & mLocked.orientedRanges.touchMajor : NULL, "touchMajor"); + logMotionRangeInfo(mLocked.orientedRanges.haveTouchArea + ? & mLocked.orientedRanges.touchMinor : NULL, "touchMinor"); + logMotionRangeInfo(mLocked.orientedRanges.haveToolArea + ? & mLocked.orientedRanges.toolMajor : NULL, "toolMajor"); + logMotionRangeInfo(mLocked.orientedRanges.haveToolArea + ? & mLocked.orientedRanges.toolMinor : NULL, "toolMinor"); + logMotionRangeInfo(mLocked.orientedRanges.haveOrientation + ? & mLocked.orientedRanges.orientation : NULL, "orientation"); +} + void TouchInputMapper::configureVirtualKeysLocked() { assert(mRawAxes.x.valid && mRawAxes.y.valid); @@ -1768,16 +1865,18 @@ void TouchInputMapper::resolveCalibration() { } void TouchInputMapper::logCalibration() { + LOGI(INDENT "Calibration:"); + // Touch Area switch (mCalibration.touchAreaCalibration) { case Calibration::TOUCH_AREA_CALIBRATION_NONE: - LOGI(INDENT " touch.touchArea.calibration: none"); + LOGI(INDENT INDENT "touch.touchArea.calibration: none"); break; case Calibration::TOUCH_AREA_CALIBRATION_GEOMETRIC: - LOGI(INDENT " touch.touchArea.calibration: geometric"); + LOGI(INDENT INDENT "touch.touchArea.calibration: geometric"); break; case Calibration::TOUCH_AREA_CALIBRATION_PRESSURE: - LOGI(INDENT " touch.touchArea.calibration: pressure"); + LOGI(INDENT INDENT "touch.touchArea.calibration: pressure"); break; default: assert(false); @@ -1786,40 +1885,40 @@ void TouchInputMapper::logCalibration() { // Tool Area switch (mCalibration.toolAreaCalibration) { case Calibration::TOOL_AREA_CALIBRATION_NONE: - LOGI(INDENT " touch.toolArea.calibration: none"); + LOGI(INDENT INDENT "touch.toolArea.calibration: none"); break; case Calibration::TOOL_AREA_CALIBRATION_GEOMETRIC: - LOGI(INDENT " touch.toolArea.calibration: geometric"); + LOGI(INDENT INDENT "touch.toolArea.calibration: geometric"); break; case Calibration::TOOL_AREA_CALIBRATION_LINEAR: - LOGI(INDENT " touch.toolArea.calibration: linear"); + LOGI(INDENT INDENT "touch.toolArea.calibration: linear"); break; default: assert(false); } if (mCalibration.haveToolAreaLinearScale) { - LOGI(INDENT " touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale); + LOGI(INDENT INDENT "touch.toolArea.linearScale: %f", mCalibration.toolAreaLinearScale); } if (mCalibration.haveToolAreaLinearBias) { - LOGI(INDENT " touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias); + LOGI(INDENT INDENT "touch.toolArea.linearBias: %f", mCalibration.toolAreaLinearBias); } if (mCalibration.haveToolAreaIsSummed) { - LOGI(INDENT " touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed); + LOGI(INDENT INDENT "touch.toolArea.isSummed: %d", mCalibration.toolAreaIsSummed); } // Pressure switch (mCalibration.pressureCalibration) { case Calibration::PRESSURE_CALIBRATION_NONE: - LOGI(INDENT " touch.pressure.calibration: none"); + LOGI(INDENT INDENT "touch.pressure.calibration: none"); break; case Calibration::PRESSURE_CALIBRATION_PHYSICAL: - LOGI(INDENT " touch.pressure.calibration: physical"); + LOGI(INDENT INDENT "touch.pressure.calibration: physical"); break; case Calibration::PRESSURE_CALIBRATION_AMPLITUDE: - LOGI(INDENT " touch.pressure.calibration: amplitude"); + LOGI(INDENT INDENT "touch.pressure.calibration: amplitude"); break; default: assert(false); @@ -1827,10 +1926,10 @@ void TouchInputMapper::logCalibration() { switch (mCalibration.pressureSource) { case Calibration::PRESSURE_SOURCE_PRESSURE: - LOGI(INDENT " touch.pressure.source: pressure"); + LOGI(INDENT INDENT "touch.pressure.source: pressure"); break; case Calibration::PRESSURE_SOURCE_TOUCH: - LOGI(INDENT " touch.pressure.source: touch"); + LOGI(INDENT INDENT "touch.pressure.source: touch"); break; case Calibration::PRESSURE_SOURCE_DEFAULT: break; @@ -1839,16 +1938,16 @@ void TouchInputMapper::logCalibration() { } if (mCalibration.havePressureScale) { - LOGI(INDENT " touch.pressure.scale: %f", mCalibration.pressureScale); + LOGI(INDENT INDENT "touch.pressure.scale: %f", mCalibration.pressureScale); } // Size switch (mCalibration.sizeCalibration) { case Calibration::SIZE_CALIBRATION_NONE: - LOGI(INDENT " touch.size.calibration: none"); + LOGI(INDENT INDENT "touch.size.calibration: none"); break; case Calibration::SIZE_CALIBRATION_NORMALIZED: - LOGI(INDENT " touch.size.calibration: normalized"); + LOGI(INDENT INDENT "touch.size.calibration: normalized"); break; default: assert(false); @@ -1857,10 +1956,10 @@ void TouchInputMapper::logCalibration() { // Orientation switch (mCalibration.orientationCalibration) { case Calibration::ORIENTATION_CALIBRATION_NONE: - LOGI(INDENT " touch.orientation.calibration: none"); + LOGI(INDENT INDENT "touch.orientation.calibration: none"); break; case Calibration::ORIENTATION_CALIBRATION_INTERPOLATED: - LOGI(INDENT " touch.orientation.calibration: interpolated"); + LOGI(INDENT INDENT "touch.orientation.calibration: interpolated"); break; default: assert(false); diff --git a/libs/utils/PollLoop.cpp b/libs/utils/PollLoop.cpp index 6d3eeee..fe76cd0 100644 --- a/libs/utils/PollLoop.cpp +++ b/libs/utils/PollLoop.cpp @@ -119,7 +119,8 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { if (outData != NULL) *outData = pending.data; return pending.ident; } - + + // Wait for wakeAndLock() waiters to run then set mPolling to true. mLock.lock(); while (mWaiters != 0) { mResume.wait(mLock); @@ -127,6 +128,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { mPolling = true; mLock.unlock(); + // Poll. int32_t result; size_t requestedCount = mRequestedFds.size(); @@ -168,6 +170,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { } #endif + // Process the poll results. mPendingCallbacks.clear(); mPendingFds.clear(); mPendingFdsPos = 0; @@ -218,6 +221,7 @@ int32_t PollLoop::pollOnce(int timeoutMillis, int* outEvents, void** outData) { } Done: + // Set mPolling to false and wake up the wakeAndLock() waiters. mLock.lock(); mPolling = false; if (mWaiters != 0) { @@ -357,11 +361,13 @@ ssize_t PollLoop::getRequestIndexLocked(int fd) { void PollLoop::wakeAndLock() { mLock.lock(); + mWaiters += 1; while (mPolling) { wake(); mAwake.wait(mLock); } + mWaiters -= 1; if (mWaiters == 0) { mResume.signal(); |
