Add cursor type and hotspot to surface metadata.

Also bootstrap unit tests for PointerController. Need to mark 3
functions of SpriteController virtual so their behaviors can be
overridden.

Bug: 130822623
Test: SurfaceFlinger can get cursor type and hotspot.
Change-Id: I739cd03214364144bb4e22a166ecc7abfd3492fe
This commit is contained in:
Garfield Tan
2019-08-05 16:47:40 -07:00
parent 9dfafd6a6f
commit 67e479a4e8
10 changed files with 390 additions and 16 deletions

View File

@@ -497,11 +497,8 @@ static void nativeSetMetadata(JNIEnv* env, jclass clazz, jlong transactionObj,
auto transaction = reinterpret_cast<SurfaceComposerClient::Transaction*>(transactionObj);
std::vector<uint8_t> byteData(parcel->dataSize());
memcpy(byteData.data(), parcel->data(), parcel->dataSize());
SurfaceControl* const ctrl = reinterpret_cast<SurfaceControl*>(nativeObject);
transaction->setMetadata(ctrl, id, std::move(byteData));
transaction->setMetadata(ctrl, id, *parcel);
}
static void nativeSetColor(JNIEnv* env, jclass clazz, jlong transactionObj,

View File

@@ -20,6 +20,7 @@ cc_library_shared {
],
shared_libs: [
"libbinder",
"libcutils",
"liblog",
"libutils",

View File

@@ -245,7 +245,8 @@ void SpriteController::doUpdateSprites() {
if (update.state.surfaceControl != NULL && (becomingVisible || becomingHidden
|| (wantSurfaceVisibleAndDrawn && (update.state.dirty & (DIRTY_ALPHA
| DIRTY_POSITION | DIRTY_TRANSFORMATION_MATRIX | DIRTY_LAYER
| DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID))))) {
| DIRTY_VISIBILITY | DIRTY_HOTSPOT | DIRTY_DISPLAY_ID
| DIRTY_ICON_STYLE))))) {
needApplyTransaction = true;
if (wantSurfaceVisibleAndDrawn
@@ -274,6 +275,21 @@ void SpriteController::doUpdateSprites() {
update.state.transformationMatrix.dtdy);
}
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible
|| (update.state.dirty & (DIRTY_HOTSPOT | DIRTY_ICON_STYLE)))) {
Parcel p;
p.writeInt32(update.state.icon.style);
p.writeFloat(update.state.icon.hotSpotX);
p.writeFloat(update.state.icon.hotSpotY);
// Pass cursor metadata in the sprite surface so that when Android is running as a
// client OS (e.g. ARC++) the host OS can get the requested cursor metadata and
// update mouse cursor in the host OS.
t.setMetadata(
update.state.surfaceControl, METADATA_MOUSE_CURSOR, p);
}
int32_t surfaceLayer = mOverlayLayer + update.state.layer;
if (wantSurfaceVisibleAndDrawn
&& (becomingVisible || (update.state.dirty & DIRTY_LAYER))) {
@@ -397,9 +413,14 @@ void SpriteController::SpriteImpl::setIcon(const SpriteIcon& icon) {
} else {
dirty = DIRTY_BITMAP;
}
if (mLocked.state.icon.style != icon.style) {
mLocked.state.icon.style = icon.style;
dirty |= DIRTY_ICON_STYLE;
}
} else if (mLocked.state.icon.isValid()) {
mLocked.state.icon.bitmap.reset();
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT;
dirty = DIRTY_BITMAP | DIRTY_HOTSPOT | DIRTY_ICON_STYLE;
} else {
return; // setting to invalid icon and already invalid so nothing to do
}

View File

@@ -55,11 +55,12 @@ struct SpriteTransformationMatrix {
* Icon that a sprite displays, including its hotspot.
*/
struct SpriteIcon {
inline SpriteIcon() : hotSpotX(0), hotSpotY(0) { }
inline SpriteIcon(const SkBitmap& bitmap, float hotSpotX, float hotSpotY) :
bitmap(bitmap), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
inline SpriteIcon() : style(0), hotSpotX(0), hotSpotY(0) { }
inline SpriteIcon(const SkBitmap& bitmap, int32_t style, float hotSpotX, float hotSpotY) :
bitmap(bitmap), style(style), hotSpotX(hotSpotX), hotSpotY(hotSpotY) { }
SkBitmap bitmap;
int32_t style;
float hotSpotX;
float hotSpotY;
@@ -69,11 +70,12 @@ struct SpriteIcon {
bitmap.readPixels(bitmapCopy.info(), bitmapCopy.getPixels(), bitmapCopy.rowBytes(),
0, 0);
}
return SpriteIcon(bitmapCopy, hotSpotX, hotSpotY);
return SpriteIcon(bitmapCopy, style, hotSpotX, hotSpotY);
}
inline void reset() {
bitmap.reset();
style = 0;
hotSpotX = 0;
hotSpotY = 0;
}
@@ -149,15 +151,15 @@ public:
SpriteController(const sp<Looper>& looper, int32_t overlayLayer);
/* Creates a new sprite, initially invisible. */
sp<Sprite> createSprite();
virtual sp<Sprite> createSprite();
/* Opens or closes a transaction to perform a batch of sprite updates as part of
* a single operation such as setPosition and setAlpha. It is not necessary to
* open a transaction when updating a single property.
* Calls to openTransaction() nest and must be matched by an equal number
* of calls to closeTransaction(). */
void openTransaction();
void closeTransaction();
virtual void openTransaction();
virtual void closeTransaction();
private:
enum {
@@ -174,6 +176,7 @@ private:
DIRTY_VISIBILITY = 1 << 5,
DIRTY_HOTSPOT = 1 << 6,
DIRTY_DISPLAY_ID = 1 << 7,
DIRTY_ICON_STYLE = 1 << 8,
};
/* Describes the state of a sprite.

7
libs/input/TEST_MAPPING Normal file
View File

@@ -0,0 +1,7 @@
{
"presubmit": [
{
"name": "libinputservice_test"
}
]
}

View File

@@ -0,0 +1,45 @@
// Copyright (C) 2019 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_test {
name: "libinputservice_test",
srcs: [
"PointerController_test.cpp",
],
shared_libs: [
"libinputservice",
"libgui",
"libhwui",
"libutils",
],
static_libs: [
"libgmock",
"libgtest",
],
header_libs: [
"libbase_headers",
"libinputflinger_headers",
],
include_dirs: [
"frameworks/base/libs",
],
cflags: [
"-Wall",
"-Werror",
"-Wextra",
],
test_suites: [
"general-tests",
],
}

View File

@@ -0,0 +1,215 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "mocks/MockSprite.h"
#include "mocks/MockSpriteController.h"
#include <input/PointerController.h>
#include <input/SpriteController.h>
#include <atomic>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <thread>
namespace android {
enum TestCursorType {
CURSOR_TYPE_DEFAULT = 0,
CURSOR_TYPE_HOVER,
CURSOR_TYPE_TOUCH,
CURSOR_TYPE_ANCHOR,
CURSOR_TYPE_ADDITIONAL_1,
CURSOR_TYPE_ADDITIONAL_2,
CURSOR_TYPE_CUSTOM = -1,
};
using ::testing::AllOf;
using ::testing::Field;
using ::testing::NiceMock;
using ::testing::Mock;
using ::testing::Return;
using ::testing::Test;
std::pair<float, float> getHotSpotCoordinatesForType(int32_t type) {
return std::make_pair(type * 10, type * 10 + 5);
}
class MockPointerControllerPolicyInterface : public PointerControllerPolicyInterface {
public:
virtual void loadPointerIcon(SpriteIcon* icon, int32_t displayId) override;
virtual void loadPointerResources(PointerResources* outResources, int32_t displayId) override;
virtual void loadAdditionalMouseResources(std::map<int32_t, SpriteIcon>* outResources,
std::map<int32_t, PointerAnimation>* outAnimationResources, int32_t displayId) override;
virtual int32_t getDefaultPointerIconId() override;
virtual int32_t getCustomPointerIconId() override;
private:
void loadPointerIconForType(SpriteIcon* icon, int32_t cursorType);
};
void MockPointerControllerPolicyInterface::loadPointerIcon(SpriteIcon* icon, int32_t) {
loadPointerIconForType(icon, CURSOR_TYPE_DEFAULT);
}
void MockPointerControllerPolicyInterface::loadPointerResources(PointerResources* outResources,
int32_t) {
loadPointerIconForType(&outResources->spotHover, CURSOR_TYPE_HOVER);
loadPointerIconForType(&outResources->spotTouch, CURSOR_TYPE_TOUCH);
loadPointerIconForType(&outResources->spotAnchor, CURSOR_TYPE_ANCHOR);
}
void MockPointerControllerPolicyInterface::loadAdditionalMouseResources(
std::map<int32_t, SpriteIcon>* outResources,
std::map<int32_t, PointerAnimation>* outAnimationResources,
int32_t) {
SpriteIcon icon;
PointerAnimation anim;
for (int32_t cursorType : {CURSOR_TYPE_ADDITIONAL_1, CURSOR_TYPE_ADDITIONAL_2}) {
loadPointerIconForType(&icon, cursorType);
anim.animationFrames.push_back(icon);
anim.durationPerFrame = 10;
(*outResources)[cursorType] = icon;
(*outAnimationResources)[cursorType] = anim;
}
}
int32_t MockPointerControllerPolicyInterface::getDefaultPointerIconId() {
return CURSOR_TYPE_DEFAULT;
}
int32_t MockPointerControllerPolicyInterface::getCustomPointerIconId() {
return CURSOR_TYPE_CUSTOM;
}
void MockPointerControllerPolicyInterface::loadPointerIconForType(SpriteIcon* icon, int32_t type) {
icon->style = type;
std::pair<float, float> hotSpot = getHotSpotCoordinatesForType(type);
icon->hotSpotX = hotSpot.first;
icon->hotSpotY = hotSpot.second;
}
class PointerControllerTest : public Test {
protected:
PointerControllerTest();
~PointerControllerTest();
sp<MockSprite> mPointerSprite;
sp<MockPointerControllerPolicyInterface> mPolicy;
sp<MockSpriteController> mSpriteController;
sp<PointerController> mPointerController;
private:
void loopThread();
std::atomic<bool> mRunning = true;
class MyLooper : public Looper {
public:
MyLooper() : Looper(false) {}
~MyLooper() = default;
};
sp<MyLooper> mLooper;
std::thread mThread;
};
PointerControllerTest::PointerControllerTest() : mPointerSprite(new NiceMock<MockSprite>),
mLooper(new MyLooper), mThread(&PointerControllerTest::loopThread, this) {
mSpriteController = new NiceMock<MockSpriteController>(mLooper);
mPolicy = new MockPointerControllerPolicyInterface();
EXPECT_CALL(*mSpriteController, createSprite())
.WillOnce(Return(mPointerSprite));
mPointerController = new PointerController(mPolicy, mLooper, mSpriteController);
DisplayViewport viewport;
viewport.displayId = ADISPLAY_ID_DEFAULT;
viewport.logicalRight = 1600;
viewport.logicalBottom = 1200;
viewport.physicalRight = 800;
viewport.physicalBottom = 600;
viewport.deviceWidth = 400;
viewport.deviceHeight = 300;
mPointerController->setDisplayViewport(viewport);
}
PointerControllerTest::~PointerControllerTest() {
mRunning.store(false, std::memory_order_relaxed);
mThread.join();
}
void PointerControllerTest::loopThread() {
Looper::setForThread(mLooper);
while (mRunning.load(std::memory_order_relaxed)) {
mLooper->pollOnce(100);
}
}
TEST_F(PointerControllerTest, useDefaultCursorTypeByDefault) {
mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(CURSOR_TYPE_DEFAULT);
EXPECT_CALL(*mPointerSprite, setVisible(true));
EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
EXPECT_CALL(*mPointerSprite, setIcon(
AllOf(
Field(&SpriteIcon::style, CURSOR_TYPE_DEFAULT),
Field(&SpriteIcon::hotSpotX, hotspot.first),
Field(&SpriteIcon::hotSpotY, hotspot.second))));
mPointerController->reloadPointerResources();
}
TEST_F(PointerControllerTest, updatePointerIcon) {
mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
int32_t type = CURSOR_TYPE_ADDITIONAL_1;
std::pair<float, float> hotspot = getHotSpotCoordinatesForType(type);
EXPECT_CALL(*mPointerSprite, setVisible(true));
EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
EXPECT_CALL(*mPointerSprite, setIcon(
AllOf(
Field(&SpriteIcon::style, type),
Field(&SpriteIcon::hotSpotX, hotspot.first),
Field(&SpriteIcon::hotSpotY, hotspot.second))));
mPointerController->updatePointerIcon(type);
}
TEST_F(PointerControllerTest, setCustomPointerIcon) {
mPointerController->unfade(PointerController::TRANSITION_IMMEDIATE);
int32_t style = CURSOR_TYPE_CUSTOM;
float hotSpotX = 15;
float hotSpotY = 20;
SpriteIcon icon;
icon.style = style;
icon.hotSpotX = hotSpotX;
icon.hotSpotY = hotSpotY;
EXPECT_CALL(*mPointerSprite, setVisible(true));
EXPECT_CALL(*mPointerSprite, setAlpha(1.0f));
EXPECT_CALL(*mPointerSprite, setIcon(
AllOf(
Field(&SpriteIcon::style, style),
Field(&SpriteIcon::hotSpotX, hotSpotX),
Field(&SpriteIcon::hotSpotY, hotSpotY))));
mPointerController->setCustomPointerIcon(icon);
}
} // namespace android

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MOCK_SPRITE_H
#define _MOCK_SPRITE_H
#include <input/SpriteController.h>
#include <gmock/gmock.h>
namespace android {
class MockSprite : public Sprite {
public:
virtual ~MockSprite() = default;
MOCK_METHOD(void, setIcon, (const SpriteIcon& icon), (override));
MOCK_METHOD(void, setVisible, (bool), (override));
MOCK_METHOD(void, setPosition, (float, float), (override));
MOCK_METHOD(void, setLayer, (int32_t), (override));
MOCK_METHOD(void, setAlpha, (float), (override));
MOCK_METHOD(void, setTransformationMatrix, (const SpriteTransformationMatrix&), (override));
MOCK_METHOD(void, setDisplayId, (int32_t), (override));
};
} // namespace android
#endif // _MOCK_SPRITE_H

View File

@@ -0,0 +1,39 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef _MOCK_SPRITE_CONTROLLER_H
#define _MOCK_SPRITE_CONTROLLER_H
#include "MockSprite.h"
#include <input/SpriteController.h>
namespace android {
class MockSpriteController : public SpriteController {
public:
MockSpriteController(sp<Looper> looper) : SpriteController(looper, 0) {}
~MockSpriteController() {}
MOCK_METHOD(sp<Sprite>, createSprite, (), (override));
MOCK_METHOD(void, openTransaction, (), (override));
MOCK_METHOD(void, closeTransaction, (), (override));
};
} // namespace android
#endif // _MOCK_SPRITE_CONTROLLER_H

View File

@@ -165,6 +165,7 @@ static void loadSystemIconAsSpriteWithPointerIcon(JNIEnv* env, jobject contextOb
outPointerIcon->bitmap.readPixels(bitmapCopy->info(), bitmapCopy->getPixels(),
bitmapCopy->rowBytes(), 0, 0);
}
outSpriteIcon->style = outPointerIcon->style;
outSpriteIcon->hotSpotX = outPointerIcon->hotSpotX;
outSpriteIcon->hotSpotY = outPointerIcon->hotSpotY;
}
@@ -1252,7 +1253,8 @@ void NativeInputManager::loadPointerIcon(SpriteIcon* icon, int32_t displayId) {
status_t status = android_view_PointerIcon_load(env, pointerIconObj.get(),
displayContext.get(), &pointerIcon);
if (!status && !pointerIcon.isNullIcon()) {
*icon = SpriteIcon(pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
*icon = SpriteIcon(
pointerIcon.bitmap, pointerIcon.style, pointerIcon.hotSpotX, pointerIcon.hotSpotY);
} else {
*icon = SpriteIcon();
}
@@ -1293,10 +1295,12 @@ void NativeInputManager::loadAdditionalMouseResources(std::map<int32_t, SpriteIc
milliseconds_to_nanoseconds(pointerIcon.durationPerFrame);
animationData.animationFrames.reserve(numFrames);
animationData.animationFrames.push_back(SpriteIcon(
pointerIcon.bitmap, pointerIcon.hotSpotX, pointerIcon.hotSpotY));
pointerIcon.bitmap, pointerIcon.style,
pointerIcon.hotSpotX, pointerIcon.hotSpotY));
for (size_t i = 0; i < numFrames - 1; ++i) {
animationData.animationFrames.push_back(SpriteIcon(
pointerIcon.bitmapFrames[i], pointerIcon.hotSpotX, pointerIcon.hotSpotY));
pointerIcon.bitmapFrames[i], pointerIcon.style,
pointerIcon.hotSpotX, pointerIcon.hotSpotY));
}
}
}
@@ -1711,6 +1715,7 @@ static void nativeSetCustomPointerIcon(JNIEnv* env, jclass /* clazz */,
pointerIcon.bitmap.readPixels(spriteInfo, spriteIcon.bitmap.getPixels(),
spriteIcon.bitmap.rowBytes(), 0, 0);
}
spriteIcon.style = pointerIcon.style;
spriteIcon.hotSpotX = pointerIcon.hotSpotX;
spriteIcon.hotSpotY = pointerIcon.hotSpotY;
im->setCustomPointerIcon(spriteIcon);