diff options
41 files changed, 1784 insertions, 361 deletions
diff --git a/cmds/installd/tests/Android.bp b/cmds/installd/tests/Android.bp index 630c1f3652..443dce6827 100644 --- a/cmds/installd/tests/Android.bp +++ b/cmds/installd/tests/Android.bp @@ -5,13 +5,13 @@ cc_test { srcs: ["installd_utils_test.cpp"], shared_libs: [ "libbase", - "liblog", "libutils", "libcutils", ], static_libs: [ - "libinstalld", "libdiskusage", + "libinstalld", + "liblog", ], } @@ -23,14 +23,14 @@ cc_test { "libbase", "libbinder", "libcutils", - "liblog", - "liblogwrap", "libselinux", "libutils", ], static_libs: [ - "libinstalld", "libdiskusage", + "libinstalld", + "liblog", + "liblogwrap", ], } @@ -42,13 +42,13 @@ cc_test { "libbase", "libbinder", "libcutils", - "liblog", - "liblogwrap", "libselinux", "libutils", ], static_libs: [ - "libinstalld", "libdiskusage", + "libinstalld", + "liblog", + "liblogwrap", ], } diff --git a/include/android/sensor.h b/include/android/sensor.h index a88733cac7..2db0ee7fd9 100644 --- a/include/android/sensor.h +++ b/include/android/sensor.h @@ -197,7 +197,7 @@ enum { * A sensor event. */ -/* NOTE: Must match hardware/sensors.h */ +/* NOTE: changes to these structs have to be backward compatible */ typedef struct ASensorVector { union { float v[3]; @@ -259,7 +259,7 @@ typedef struct { }; } AAdditionalInfoEvent; -/* NOTE: Must match hardware/sensors.h */ +/* NOTE: changes to this struct has to be backward compatible */ typedef struct ASensorEvent { int32_t version; /* sizeof(struct ASensorEvent) */ int32_t sensor; diff --git a/include/audiomanager/IPlayer.h b/include/audiomanager/IPlayer.h deleted file mode 100644 index de5c1c7b64..0000000000 --- a/include/audiomanager/IPlayer.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_IPLAYER_H -#define ANDROID_IPLAYER_H - -#include <stdint.h> -#include <sys/types.h> - -#include <media/VolumeShaper.h> -#include <utils/RefBase.h> -#include <utils/Errors.h> -#include <binder/IInterface.h> - -namespace android { - -// ---------------------------------------------------------------------------- - -class IPlayer : public IInterface -{ -public: - DECLARE_META_INTERFACE(Player); - - virtual void start() = 0; - - virtual void pause() = 0; - - virtual void stop() = 0; - - virtual void setVolume(float vol) = 0; - - virtual void setPan(float pan) = 0; - - virtual void setStartDelayMs(int delayMs) = 0; - - virtual void applyVolumeShaper( - const sp<VolumeShaper::Configuration>& configuration, - const sp<VolumeShaper::Operation>& operation) = 0; -}; - -// ---------------------------------------------------------------------------- - -class BnPlayer : public BnInterface<IPlayer> -{ -public: - virtual status_t onTransact( uint32_t code, - const Parcel& data, - Parcel* reply, - uint32_t flags = 0); -}; - -// ---------------------------------------------------------------------------- - -}; // namespace android - -#endif // ANDROID_IPLAYER_H diff --git a/include/input/VelocityTracker.h b/include/input/VelocityTracker.h index 795f575a2e..ffa1614b55 100644 --- a/include/input/VelocityTracker.h +++ b/include/input/VelocityTracker.h @@ -264,6 +264,40 @@ private: Movement mMovements[HISTORY_SIZE]; }; +class ImpulseVelocityTrackerStrategy : public VelocityTrackerStrategy { +public: + ImpulseVelocityTrackerStrategy(); + virtual ~ImpulseVelocityTrackerStrategy(); + + virtual void clear(); + virtual void clearPointers(BitSet32 idBits); + virtual void addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions); + virtual bool getEstimator(uint32_t id, VelocityTracker::Estimator* outEstimator) const; + +private: + // Sample horizon. + // We don't use too much history by default since we want to react to quick + // changes in direction. + static constexpr nsecs_t HORIZON = 100 * 1000000; // 100 ms + + // Number of samples to keep. + static constexpr size_t HISTORY_SIZE = 20; + + struct Movement { + nsecs_t eventTime; + BitSet32 idBits; + VelocityTracker::Position positions[MAX_POINTERS]; + + inline const VelocityTracker::Position& getPosition(uint32_t id) const { + return positions[idBits.getIndexOfBit(id)]; + } + }; + + size_t mIndex; + Movement mMovements[HISTORY_SIZE]; +}; + } // namespace android #endif // _LIBINPUT_VELOCITY_TRACKER_H diff --git a/libs/gui/LayerState.cpp b/libs/gui/LayerState.cpp index 9b06e63610..573f6856d6 100644 --- a/libs/gui/LayerState.cpp +++ b/libs/gui/LayerState.cpp @@ -45,6 +45,8 @@ status_t layer_state_t::write(Parcel& output) const output.writeInt32(overrideScalingMode); output.writeStrongBinder(IInterface::asBinder(barrierGbp)); output.writeStrongBinder(relativeLayerHandle); + output.writeStrongBinder(parentHandleForChild); + output.writeStrongBinder(childHandle); output.write(transparentRegion); return NO_ERROR; } @@ -77,6 +79,8 @@ status_t layer_state_t::read(const Parcel& input) barrierGbp = interface_cast<IGraphicBufferProducer>(input.readStrongBinder()); relativeLayerHandle = input.readStrongBinder(); + parentHandleForChild = input.readStrongBinder(); + childHandle = input.readStrongBinder(); input.read(transparentRegion); return NO_ERROR; } diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp index 5b1c599a13..6c9d88b599 100644 --- a/libs/gui/Surface.cpp +++ b/libs/gui/Surface.cpp @@ -1509,6 +1509,12 @@ int Surface::setBuffersDataSpace(android_dataspace dataSpace) return NO_ERROR; } +android_dataspace_t Surface::getBuffersDataSpace() { + ALOGV("Surface::getBuffersDataSpace"); + Mutex::Autolock lock(mMutex); + return mDataSpace; +} + void Surface::freeAllBuffers() { for (int i = 0; i < NUM_BUFFER_SLOTS; i++) { mSlots[i].buffer = 0; diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp index 7ae2672249..b0ae7e0bdf 100644 --- a/libs/gui/SurfaceComposerClient.cpp +++ b/libs/gui/SurfaceComposerClient.cpp @@ -176,6 +176,9 @@ public: status_t reparentChildren(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id, const sp<IBinder>& newParentHandle); + status_t reparentChild(const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id, const sp<IBinder>& newParentHandle, + const sp<IBinder>& childHandle); status_t detachChildren(const sp<SurfaceComposerClient>& client, const sp<IBinder>& id); status_t setOverrideScalingMode(const sp<SurfaceComposerClient>& client, @@ -493,6 +496,21 @@ status_t Composer::reparentChildren( return NO_ERROR; } +status_t Composer::reparentChild(const sp<SurfaceComposerClient>& client, + const sp<IBinder>& id, + const sp<IBinder>& newParentHandle, + const sp<IBinder>& childHandle) { + Mutex::Autolock lock(mLock); + layer_state_t* s = getLayerStateLocked(client, id); + if (!s) { + return BAD_INDEX; + } + s->what |= layer_state_t::eReparentChild; + s->parentHandleForChild = newParentHandle; + s->childHandle = childHandle; + return NO_ERROR; +} + status_t Composer::detachChildren( const sp<SurfaceComposerClient>& client, const sp<IBinder>& id) { @@ -831,6 +849,11 @@ status_t SurfaceComposerClient::reparentChildren(const sp<IBinder>& id, return getComposer().reparentChildren(this, id, newParentHandle); } +status_t SurfaceComposerClient::reparentChild(const sp<IBinder>& id, + const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle) { + return getComposer().reparentChild(this, id, newParentHandle, childHandle); +} + status_t SurfaceComposerClient::detachChildren(const sp<IBinder>& id) { return getComposer().detachChildren(this, id); } diff --git a/libs/gui/SurfaceControl.cpp b/libs/gui/SurfaceControl.cpp index 58bd273de6..b9c5ef9580 100644 --- a/libs/gui/SurfaceControl.cpp +++ b/libs/gui/SurfaceControl.cpp @@ -191,6 +191,13 @@ status_t SurfaceControl::reparentChildren(const sp<IBinder>& newParentHandle) { return mClient->reparentChildren(mHandle, newParentHandle); } +status_t SurfaceControl::reparentChild(const sp<IBinder>& newParentHandle, + const sp<IBinder>& childHandle) { + status_t err = validate(); + if (err < 0) return err; + return mClient->reparentChild(mHandle, newParentHandle, childHandle); +} + status_t SurfaceControl::detachChildren() { status_t err = validate(); if (err < 0) return err; diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h index 55dd6bf067..3fe29d955b 100644 --- a/libs/gui/include/gui/Surface.h +++ b/libs/gui/include/gui/Surface.h @@ -281,6 +281,8 @@ public: // detachNextBuffer, or attachBuffer call. status_t getAndFlushRemovedBuffers(std::vector<sp<GraphicBuffer>>* out); + android_dataspace_t getBuffersDataSpace(); + protected: enum { NUM_BUFFER_SLOTS = BufferQueueDefs::NUM_BUFFER_SLOTS }; enum { DEFAULT_FORMAT = PIXEL_FORMAT_RGBA_8888 }; diff --git a/libs/gui/include/gui/SurfaceComposerClient.h b/libs/gui/include/gui/SurfaceComposerClient.h index 145c0597bd..6e2cb83544 100644 --- a/libs/gui/include/gui/SurfaceComposerClient.h +++ b/libs/gui/include/gui/SurfaceComposerClient.h @@ -161,6 +161,8 @@ public: const sp<Surface>& handle, uint64_t frameNumber); status_t reparentChildren(const sp<IBinder>& id, const sp<IBinder>& newParentHandle); + status_t reparentChild(const sp<IBinder>& id, const sp<IBinder>& newParentHandle, + const sp<IBinder>& childHandle); status_t detachChildren(const sp<IBinder>& id); status_t setOverrideScalingMode(const sp<IBinder>& id, int32_t overrideScalingMode); diff --git a/libs/gui/include/gui/SurfaceControl.h b/libs/gui/include/gui/SurfaceControl.h index c15209d32c..d8b67ef96a 100644 --- a/libs/gui/include/gui/SurfaceControl.h +++ b/libs/gui/include/gui/SurfaceControl.h @@ -124,6 +124,12 @@ public: // Reparents all children of this layer to the new parent handle. status_t reparentChildren(const sp<IBinder>& newParentHandle); + // Reparents a specified child from this layer to the new parent handle. + // The child, parent, and new parent must all have the same client. + // This can be used instead of reparentChildren if the caller wants to + // only re-parent specific children. + status_t reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle); + // Detaches all child surfaces (and their children recursively) // from their SurfaceControl. // The child SurfaceControl's will not throw exceptions or return errors, diff --git a/libs/gui/include/private/gui/LayerState.h b/libs/gui/include/private/gui/LayerState.h index 307c764702..4f73e04e22 100644 --- a/libs/gui/include/private/gui/LayerState.h +++ b/libs/gui/include/private/gui/LayerState.h @@ -59,7 +59,8 @@ struct layer_state_t { eGeometryAppliesWithResize = 0x00001000, eReparentChildren = 0x00002000, eDetachChildren = 0x00004000, - eRelativeLayerChanged = 0x00008000 + eRelativeLayerChanged = 0x00008000, + eReparentChild = 0x00010000 }; layer_state_t() @@ -107,6 +108,9 @@ struct layer_state_t { sp<IBinder> relativeLayerHandle; + sp<IBinder> parentHandleForChild; + sp<IBinder> childHandle; + // non POD must be last. see write/read Region transparentRegion; }; diff --git a/libs/input/VelocityTracker.cpp b/libs/input/VelocityTracker.cpp index 62acea360e..f5b896b803 100644 --- a/libs/input/VelocityTracker.cpp +++ b/libs/input/VelocityTracker.cpp @@ -75,7 +75,9 @@ static std::string vectorToString(const float* a, uint32_t m) { str += " ]"; return str; } +#endif +#if DEBUG_STRATEGY static std::string matrixToString(const float* a, uint32_t m, uint32_t n, bool rowMajor) { std::string str; str = "["; @@ -141,6 +143,11 @@ bool VelocityTracker::configureStrategy(const char* strategy) { } VelocityTrackerStrategy* VelocityTracker::createStrategy(const char* strategy) { + if (!strcmp("impulse", strategy)) { + // Physical model of pushing an object. Quality: VERY GOOD. + // Works with duplicate coordinates, unclean finger liftoff. + return new ImpulseVelocityTrackerStrategy(); + } if (!strcmp("lsq1", strategy)) { // 1st order least squares. Quality: POOR. // Frequently underfits the touch data especially when the finger accelerates @@ -352,9 +359,6 @@ bool VelocityTracker::getEstimator(uint32_t id, Estimator* outEstimator) const { // --- LeastSquaresVelocityTrackerStrategy --- -const nsecs_t LeastSquaresVelocityTrackerStrategy::HORIZON; -const uint32_t LeastSquaresVelocityTrackerStrategy::HISTORY_SIZE; - LeastSquaresVelocityTrackerStrategy::LeastSquaresVelocityTrackerStrategy( uint32_t degree, Weighting weighting) : mDegree(degree), mWeighting(weighting) { @@ -863,10 +867,6 @@ void IntegratingVelocityTrackerStrategy::populateEstimator(const State& state, // --- LegacyVelocityTrackerStrategy --- -const nsecs_t LegacyVelocityTrackerStrategy::HORIZON; -const uint32_t LegacyVelocityTrackerStrategy::HISTORY_SIZE; -const nsecs_t LegacyVelocityTrackerStrategy::MIN_DURATION; - LegacyVelocityTrackerStrategy::LegacyVelocityTrackerStrategy() { clear(); } @@ -979,4 +979,192 @@ bool LegacyVelocityTrackerStrategy::getEstimator(uint32_t id, return true; } +// --- ImpulseVelocityTrackerStrategy --- + +ImpulseVelocityTrackerStrategy::ImpulseVelocityTrackerStrategy() { + clear(); +} + +ImpulseVelocityTrackerStrategy::~ImpulseVelocityTrackerStrategy() { +} + +void ImpulseVelocityTrackerStrategy::clear() { + mIndex = 0; + mMovements[0].idBits.clear(); +} + +void ImpulseVelocityTrackerStrategy::clearPointers(BitSet32 idBits) { + BitSet32 remainingIdBits(mMovements[mIndex].idBits.value & ~idBits.value); + mMovements[mIndex].idBits = remainingIdBits; +} + +void ImpulseVelocityTrackerStrategy::addMovement(nsecs_t eventTime, BitSet32 idBits, + const VelocityTracker::Position* positions) { + if (++mIndex == HISTORY_SIZE) { + mIndex = 0; + } + + Movement& movement = mMovements[mIndex]; + movement.eventTime = eventTime; + movement.idBits = idBits; + uint32_t count = idBits.count(); + for (uint32_t i = 0; i < count; i++) { + movement.positions[i] = positions[i]; + } +} + +/** + * Calculate the total impulse provided to the screen and the resulting velocity. + * + * The touchscreen is modeled as a physical object. + * Initial condition is discussed below, but for now suppose that v(t=0) = 0 + * + * The kinetic energy of the object at the release is E=0.5*m*v^2 + * Then vfinal = sqrt(2E/m). The goal is to calculate E. + * + * The kinetic energy at the release is equal to the total work done on the object by the finger. + * The total work W is the sum of all dW along the path. + * + * dW = F*dx, where dx is the piece of path traveled. + * Force is change of momentum over time, F = dp/dt = m dv/dt. + * Then substituting: + * dW = m (dv/dt) * dx = m * v * dv + * + * Summing along the path, we get: + * W = sum(dW) = sum(m * v * dv) = m * sum(v * dv) + * Since the mass stays constant, the equation for final velocity is: + * vfinal = sqrt(2*sum(v * dv)) + * + * Here, + * dv : change of velocity = (v[i+1]-v[i]) + * dx : change of distance = (x[i+1]-x[i]) + * dt : change of time = (t[i+1]-t[i]) + * v : instantaneous velocity = dx/dt + * + * The final formula is: + * vfinal = sqrt(2) * sqrt(sum((v[i]-v[i-1])*|v[i]|)) for all i + * The absolute value is needed to properly account for the sign. If the velocity over a + * particular segment descreases, then this indicates braking, which means that negative + * work was done. So for two positive, but decreasing, velocities, this contribution would be + * negative and will cause a smaller final velocity. + * + * Initial condition + * There are two ways to deal with initial condition: + * 1) Assume that v(0) = 0, which would mean that the screen is initially at rest. + * This is not entirely accurate. We are only taking the past X ms of touch data, where X is + * currently equal to 100. However, a touch event that created a fling probably lasted for longer + * than that, which would mean that the user has already been interacting with the touchscreen + * and it has probably already been moving. + * 2) Assume that the touchscreen has already been moving at a certain velocity, calculate this + * initial velocity and the equivalent energy, and start with this initial energy. + * Consider an example where we have the following data, consisting of 3 points: + * time: t0, t1, t2 + * x : x0, x1, x2 + * v : 0 , v1, v2 + * Here is what will happen in each of these scenarios: + * 1) By directly applying the formula above with the v(0) = 0 boundary condition, we will get + * vfinal = sqrt(2*(|v1|*(v1-v0) + |v2|*(v2-v1))). This can be simplified since v0=0 + * vfinal = sqrt(2*(|v1|*v1 + |v2|*(v2-v1))) = sqrt(2*(v1^2 + |v2|*(v2 - v1))) + * since velocity is a real number + * 2) If we treat the screen as already moving, then it must already have an energy (per mass) + * equal to 1/2*v1^2. Then the initial energy should be 1/2*v1*2, and only the second segment + * will contribute to the total kinetic energy (since we can effectively consider that v0=v1). + * This will give the following expression for the final velocity: + * vfinal = sqrt(2*(1/2*v1^2 + |v2|*(v2-v1))) + * This analysis can be generalized to an arbitrary number of samples. + * + * + * Comparing the two equations above, we see that the only mathematical difference + * is the factor of 1/2 in front of the first velocity term. + * This boundary condition would allow for the "proper" calculation of the case when all of the + * samples are equally spaced in time and distance, which should suggest a constant velocity. + * + * Note that approach 2) is sensitive to the proper ordering of the data in time, since + * the boundary condition must be applied to the oldest sample to be accurate. + */ +static float calculateImpulseVelocity(const nsecs_t* t, const float* x, size_t count) { + // The input should be in reversed time order (most recent sample at index i=0) + // t[i] is in nanoseconds, but due to FP arithmetic, convert to seconds inside this function + static constexpr float NANOS_PER_SECOND = 1E-9; + static constexpr float sqrt2 = 1.41421356237; + + if (count < 2) { + return 0; // if 0 or 1 points, velocity is zero + } + if (t[1] > t[0]) { // Algorithm will still work, but not perfectly + ALOGE("Samples provided to calculateImpulseVelocity in the wrong order"); + } + if (count == 2) { // if 2 points, basic linear calculation + if (t[1] == t[0]) { + ALOGE("Events have identical time stamps t=%" PRId64 ", setting velocity = 0", t[0]); + return 0; + } + return (x[1] - x[0]) / (NANOS_PER_SECOND * (t[1] - t[0])); + } + // Guaranteed to have at least 3 points here + float work = 0; + float vprev, vcurr; // v[i-1] and v[i], respectively + vprev = 0; + for (size_t i = count - 1; i > 0 ; i--) { // start with the oldest sample and go forward in time + if (t[i] == t[i-1]) { + ALOGE("Events have identical time stamps t=%" PRId64 ", skipping sample", t[i]); + continue; + } + vcurr = (x[i] - x[i-1]) / (NANOS_PER_SECOND * (t[i] - t[i-1])); + work += (vcurr - vprev) * fabsf(vcurr); + if (i == count - 1) { + work *= 0.5; // initial condition, case 2) above + } + vprev = vcurr; + } + return (work < 0 ? -1.0 : 1.0) * sqrtf(fabsf(work)) * sqrt2; +} + +bool ImpulseVelocityTrackerStrategy::getEstimator(uint32_t id, + VelocityTracker::Estimator* outEstimator) const { + outEstimator->clear(); + + // Iterate over movement samples in reverse time order and collect samples. + float x[HISTORY_SIZE]; + float y[HISTORY_SIZE]; + nsecs_t time[HISTORY_SIZE]; + size_t m = 0; // number of points that will be used for fitting + size_t index = mIndex; + const Movement& newestMovement = mMovements[mIndex]; + do { + const Movement& movement = mMovements[index]; + if (!movement.idBits.hasBit(id)) { + break; + } + + nsecs_t age = newestMovement.eventTime - movement.eventTime; + if (age > HORIZON) { + break; + } + + const VelocityTracker::Position& position = movement.getPosition(id); + x[m] = position.x; + y[m] = position.y; + time[m] = movement.eventTime; + index = (index == 0 ? HISTORY_SIZE : index) - 1; + } while (++m < HISTORY_SIZE); + + if (m == 0) { + return false; // no data + } + outEstimator->xCoeff[0] = 0; + outEstimator->yCoeff[0] = 0; + outEstimator->xCoeff[1] = calculateImpulseVelocity(time, x, m); + outEstimator->yCoeff[1] = calculateImpulseVelocity(time, y, m); + outEstimator->xCoeff[2] = 0; + outEstimator->yCoeff[2] = 0; + outEstimator->time = newestMovement.eventTime; + outEstimator->degree = 2; // similar results to 2nd degree fit + outEstimator->confidence = 1; +#if DEBUG_STRATEGY + ALOGD("velocity: (%f, %f)", outEstimator->xCoeff[1], outEstimator->yCoeff[1]); +#endif + return true; +} + } // namespace android diff --git a/libs/input/tests/Android.bp b/libs/input/tests/Android.bp index 029a42091e..e3e1dec00b 100644 --- a/libs/input/tests/Android.bp +++ b/libs/input/tests/Android.bp @@ -6,6 +6,7 @@ cc_test { "InputChannel_test.cpp", "InputEvent_test.cpp", "InputPublisherAndConsumer_test.cpp", + "VelocityTracker_test.cpp", ], shared_libs: [ "libinput", @@ -13,6 +14,7 @@ cc_test { "libutils", "libbinder", "libui", + "libbase", ] } diff --git a/libs/input/tests/VelocityTracker_test.cpp b/libs/input/tests/VelocityTracker_test.cpp new file mode 100644 index 0000000000..43b6012e0d --- /dev/null +++ b/libs/input/tests/VelocityTracker_test.cpp @@ -0,0 +1,664 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "VelocityTracker_test" + +#include <math.h> + +#include <android-base/stringprintf.h> +#include <gtest/gtest.h> +#include <input/VelocityTracker.h> + +using android::base::StringPrintf; + +namespace android { + +constexpr int32_t DEFAULT_POINTER_ID = 0; // pointer ID used for manually defined tests + +// velocity must be in the range (1-tol)*EV <= velocity <= (1+tol)*EV +// here EV = expected value, tol = VELOCITY_TOLERANCE +constexpr float VELOCITY_TOLERANCE = 0.2; + +// --- VelocityTrackerTest --- +class VelocityTrackerTest : public testing::Test { }; + +static void checkVelocity(float Vactual, float Vtarget) { + // Compare directions + if ((Vactual > 0 && Vtarget <= 0) || (Vactual < 0 && Vtarget >= 0)) { + FAIL() << StringPrintf("Velocity %f does not have the same direction" + " as the target velocity %f", Vactual, Vtarget); + } + + // Compare magnitudes + const float Vlower = fabsf(Vtarget * (1 - VELOCITY_TOLERANCE)); + const float Vupper = fabsf(Vtarget * (1 + VELOCITY_TOLERANCE)); + if (fabsf(Vactual) < Vlower) { + FAIL() << StringPrintf("Velocity %f is more than %.0f%% below target velocity %f", + Vactual, VELOCITY_TOLERANCE * 100, Vtarget); + } + if (fabsf(Vactual) > Vupper) { + FAIL() << StringPrintf("Velocity %f is more than %.0f%% above target velocity %f", + Vactual, VELOCITY_TOLERANCE * 100, Vtarget); + } + SUCCEED() << StringPrintf("Velocity %f within %.0f%% of target %f)", + Vactual, VELOCITY_TOLERANCE * 100, Vtarget); +} + +void failWithMessage(std::string message) { + FAIL() << message; // cannot do this directly from a non-void function +} + +struct Position { + nsecs_t time; + float x; + float y; +}; + + +MotionEvent* createSimpleMotionEvent(const Position* positions, size_t numSamples) { + /** + * Only populate the basic fields of a MotionEvent, such as time and a single axis + * Designed for use with manually-defined tests. + * Create a new MotionEvent on the heap, caller responsible for destroying the object. + */ + if (numSamples < 1) { + failWithMessage(StringPrintf("Need at least 1 sample to create a MotionEvent." + " Received numSamples=%zu", numSamples)); + } + + MotionEvent* event = new MotionEvent(); + PointerCoords coords; + PointerProperties properties[1]; + + properties[0].id = DEFAULT_POINTER_ID; + properties[0].toolType = AMOTION_EVENT_TOOL_TYPE_FINGER; + + // First sample added separately with initialize + coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[0].x); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[0].y); + event->initialize(0, AINPUT_SOURCE_TOUCHSCREEN, AMOTION_EVENT_ACTION_MOVE, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, positions[0].time, 1, properties, &coords); + + for (size_t i = 1; i < numSamples; i++) { + coords.setAxisValue(AMOTION_EVENT_AXIS_X, positions[i].x); + coords.setAxisValue(AMOTION_EVENT_AXIS_Y, positions[i].y); + event->addSample(positions[i].time, &coords); + } + return event; +} + +static void computeAndCheckVelocity(const Position* positions, size_t numSamples, + int32_t axis, float targetVelocity) { + VelocityTracker vt(nullptr); + float Vx, Vy; + + MotionEvent* event = createSimpleMotionEvent(positions, numSamples); + vt.addMovement(event); + + vt.getVelocity(DEFAULT_POINTER_ID, &Vx, &Vy); + + switch (axis) { + case AMOTION_EVENT_AXIS_X: + checkVelocity(Vx, targetVelocity); + break; + case AMOTION_EVENT_AXIS_Y: + checkVelocity(Vy, targetVelocity); + break; + default: + FAIL() << "Axis must be either AMOTION_EVENT_AXIS_X or AMOTION_EVENT_AXIS_Y"; + } + delete event; +} + +/* + * ================== VelocityTracker tests generated manually ===================================== + */ + // @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy +TEST_F(VelocityTrackerTest, DISABLED_ThreePointsPositiveVelocityTest) { + // Same coordinate is reported 2 times in a row + // It is difficult to determine the correct answer here, but at least the direction + // of the reported velocity should be positive. + Position values[] = { + { 0, 273, NAN }, + { 12585000, 293, NAN }, + { 14730000, 293, NAN }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1600); +} + +TEST_F(VelocityTrackerTest, ThreePointsZeroVelocityTest) { + // Same coordinate is reported 3 times in a row + Position values[] = { + { 0, 293, NAN }, + { 6132000, 293, NAN }, + { 11283000, 293, NAN }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 0); +} + +TEST_F(VelocityTrackerTest, ThreePointsLinearVelocityTest) { + // Fixed velocity at 5 points per 10 milliseconds + Position values[] = { + { 0, 0, NAN }, + { 10000000, 5, NAN }, + { 20000000, 10, NAN }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 500); +} + + +/** + * ================== VelocityTracker tests generated by recording real events ===================== + * + * To add a test, record the input coordinates and event times to all calls + * to void VelocityTracker::addMovement(const MotionEvent* event). + * Also record all calls to VelocityTracker::clear(). + * Finally, record the output of VelocityTracker::getVelocity(...) + * This will give you the necessary data to create a new test. + */ + +// --------------- Recorded by hand on swordfish --------------------------------------------------- +// @todo Currently disabled, enable when switching away from lsq2 VelocityTrackerStrategy +TEST_F(VelocityTrackerTest, DISABLED_SwordfishFlingDown) { + // Recording of a fling on Swordfish that could cause a fling in the wrong direction + Position values[] = { + { 0, 271, 96 }, + { 16071042, 269.786346, 106.922775 }, + { 35648403, 267.983063, 156.660034 }, + { 52313925, 262.638397, 220.339081 }, + { 68976522, 266.138824, 331.581116 }, + { 85639375, 274.79245, 428.113159 }, + { 96948871, 274.79245, 428.113159 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 623.577637); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 8523.348633); +} + +// --------------- Recorded by hand on sailfish, generated by a script ----------------------------- +// For some of these tests, the X-direction velocity checking has been removed, because the lsq2 +// and the impulse VelocityTrackerStrategies did not agree within 20%. +// Since the flings were recorded in the Y-direction, the intentional user action should only +// be relevant for the Y axis. +// There have been also cases where lsq2 and impulse disagreed more than 20% in the Y-direction. +// Those recordings have been discarded because we didn't feel one strategy's interpretation was +// more correct than another's but didn't want to increase the tolerance for the entire test suite. +// +// There are 18 tests total below: 9 in the positive Y direction and 9 in the opposite. +// The recordings were loosely binned into 3 categories - slow, faster, and fast, which roughly +// characterizes the velocity of the finger motion. +// These can be treated approximately as: +// slow - less than 1 page gets scrolled +// faster - more than 1 page gets scrolled, but less than 3 +// fast - entire list is scrolled (fling is done as hard as possible) + +TEST_F(VelocityTrackerTest, SailfishFlingUpSlow1) { + // Sailfish - fling up - slow - 1 + Position values[] = { + { 235089067457000, 528.00, 983.00 }, + { 235089084684000, 527.00, 981.00 }, + { 235089093349000, 527.00, 977.00 }, + { 235089095677625, 527.00, 975.93 }, + { 235089101859000, 527.00, 970.00 }, + { 235089110378000, 528.00, 960.00 }, + { 235089112497111, 528.25, 957.51 }, + { 235089118760000, 531.00, 946.00 }, + { 235089126686000, 535.00, 931.00 }, + { 235089129316820, 536.33, 926.02 }, + { 235089135199000, 540.00, 914.00 }, + { 235089144297000, 546.00, 896.00 }, + { 235089146136443, 547.21, 892.36 }, + { 235089152923000, 553.00, 877.00 }, + { 235089160784000, 559.00, 851.00 }, + { 235089162955851, 560.66, 843.82 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 872.794617); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 951.698181); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3604.819336); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3044.966064); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpSlow2) { + // Sailfish - fling up - slow - 2 + Position values[] = { + { 235110560704000, 522.00, 1107.00 }, + { 235110575764000, 522.00, 1107.00 }, + { 235110584385000, 522.00, 1107.00 }, + { 235110588421179, 521.52, 1106.52 }, + { 235110592830000, 521.00, 1106.00 }, + { 235110601385000, 520.00, 1104.00 }, + { 235110605088160, 519.14, 1102.27 }, + { 235110609952000, 518.00, 1100.00 }, + { 235110618353000, 517.00, 1093.00 }, + { 235110621755146, 516.60, 1090.17 }, + { 235110627010000, 517.00, 1081.00 }, + { 235110634785000, 518.00, 1063.00 }, + { 235110638422450, 518.87, 1052.58 }, + { 235110643161000, 520.00, 1039.00 }, + { 235110651767000, 524.00, 1011.00 }, + { 235110655089581, 525.54, 1000.19 }, + { 235110660368000, 530.00, 980.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4096.583008); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3455.094238); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpSlow3) { + // Sailfish - fling up - slow - 3 + Position values[] = { + { 792536237000, 580.00, 1317.00 }, + { 792541538987, 580.63, 1311.94 }, + { 792544613000, 581.00, 1309.00 }, + { 792552301000, 583.00, 1295.00 }, + { 792558362309, 585.13, 1282.92 }, + { 792560828000, 586.00, 1278.00 }, + { 792569446000, 589.00, 1256.00 }, + { 792575185095, 591.54, 1241.41 }, + { 792578491000, 593.00, 1233.00 }, + { 792587044000, 597.00, 1211.00 }, + { 792592008172, 600.28, 1195.92 }, + { 792594616000, 602.00, 1188.00 }, + { 792603129000, 607.00, 1167.00 }, + { 792608831290, 609.48, 1155.83 }, + { 792612321000, 611.00, 1149.00 }, + { 792620768000, 615.00, 1131.00 }, + { 792625653873, 617.32, 1121.73 }, + { 792629200000, 619.00, 1115.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 574.33429); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 617.40564); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2361.982666); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -2500.055664); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpFaster1) { + // Sailfish - fling up - faster - 1 + Position values[] = { + { 235160420675000, 610.00, 1042.00 }, + { 235160428220000, 609.00, 1026.00 }, + { 235160436544000, 609.00, 1024.00 }, + { 235160441852394, 609.64, 1020.82 }, + { 235160444878000, 610.00, 1019.00 }, + { 235160452673000, 613.00, 1006.00 }, + { 235160458519743, 617.18, 992.06 }, + { 235160461061000, 619.00, 986.00 }, + { 235160469798000, 627.00, 960.00 }, + { 235160475186713, 632.22, 943.02 }, + { 235160478051000, 635.00, 934.00 }, + { 235160486489000, 644.00, 906.00 }, + { 235160491853697, 649.56, 890.56 }, + { 235160495177000, 653.00, 881.00 }, + { 235160504148000, 662.00, 858.00 }, + { 235160509231495, 666.81, 845.37 }, + { 235160512603000, 670.00, 837.00 }, + { 235160520366000, 679.00, 814.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1274.141724); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 1438.53186); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3877.35498); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -3695.859619); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpFaster2) { + // Sailfish - fling up - faster - 2 + Position values[] = { + { 847153808000, 576.00, 1264.00 }, + { 847171174000, 576.00, 1262.00 }, + { 847179640000, 576.00, 1257.00 }, + { 847185187540, 577.41, 1249.22 }, + { 847187487000, 578.00, 1246.00 }, + { 847195710000, 581.00, 1227.00 }, + { 847202027059, 583.93, 1209.40 }, + { 847204324000, 585.00, 1203.00 }, + { 847212672000, 590.00, 1176.00 }, + { 847218861395, 594.36, 1157.11 }, + { 847221190000, 596.00, 1150.00 }, + { 847230484000, 602.00, 1124.00 }, + { 847235701400, 607.56, 1103.83 }, + { 847237986000, 610.00, 1095.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4280.07959); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -4241.004395); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpFaster3) { + // Sailfish - fling up - faster - 3 + Position values[] = { + { 235200532789000, 507.00, 1084.00 }, + { 235200549221000, 507.00, 1083.00 }, + { 235200557841000, 507.00, 1081.00 }, + { 235200558051189, 507.00, 1080.95 }, + { 235200566314000, 507.00, 1078.00 }, + { 235200574876586, 508.97, 1070.12 }, + { 235200575006000, 509.00, 1070.00 }, + { 235200582900000, 514.00, 1054.00 }, + { 235200591276000, 525.00, 1023.00 }, + { 235200591701829, 525.56, 1021.42 }, + { 235200600064000, 542.00, 976.00 }, + { 235200608519000, 563.00, 911.00 }, + { 235200608527086, 563.02, 910.94 }, + { 235200616933000, 590.00, 844.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -8715.686523); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -7639.026367); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpFast1) { + // Sailfish - fling up - fast - 1 + Position values[] = { + { 920922149000, 561.00, 1412.00 }, + { 920930185000, 559.00, 1377.00 }, + { 920930262463, 558.98, 1376.66 }, + { 920938547000, 559.00, 1371.00 }, + { 920947096857, 562.91, 1342.68 }, + { 920947302000, 563.00, 1342.00 }, + { 920955502000, 577.00, 1272.00 }, + { 920963931021, 596.87, 1190.54 }, + { 920963987000, 597.00, 1190.00 }, + { 920972530000, 631.00, 1093.00 }, + { 920980765511, 671.31, 994.68 }, + { 920980906000, 672.00, 993.00 }, + { 920989261000, 715.00, 903.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5670.329102); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, 5991.866699); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -13021.101562); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -15093.995117); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpFast2) { + // Sailfish - fling up - fast - 2 + Position values[] = { + { 235247153233000, 518.00, 1168.00 }, + { 235247170452000, 517.00, 1167.00 }, + { 235247178908000, 515.00, 1159.00 }, + { 235247179556213, 514.85, 1158.39 }, + { 235247186821000, 515.00, 1125.00 }, + { 235247195265000, 521.00, 1051.00 }, + { 235247196389476, 521.80, 1041.15 }, + { 235247203649000, 538.00, 932.00 }, + { 235247212253000, 571.00, 794.00 }, + { 235247213222491, 574.72, 778.45 }, + { 235247220736000, 620.00, 641.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20286.958984); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -20494.587891); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingUpFast3) { + // Sailfish - fling up - fast - 3 + Position values[] = { + { 235302568736000, 529.00, 1167.00 }, + { 235302576644000, 523.00, 1140.00 }, + { 235302579395063, 520.91, 1130.61 }, + { 235302585140000, 522.00, 1130.00 }, + { 235302593615000, 527.00, 1065.00 }, + { 235302596207444, 528.53, 1045.12 }, + { 235302602102000, 559.00, 872.00 }, + { 235302610545000, 652.00, 605.00 }, + { 235302613019881, 679.26, 526.73 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -39295.941406); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, -36461.421875); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownSlow1) { + // Sailfish - fling down - slow - 1 + Position values[] = { + { 235655749552755, 582.00, 432.49 }, + { 235655750638000, 582.00, 433.00 }, + { 235655758865000, 582.00, 440.00 }, + { 235655766221523, 581.16, 448.43 }, + { 235655767594000, 581.00, 450.00 }, + { 235655776044000, 580.00, 462.00 }, + { 235655782890696, 579.18, 474.35 }, + { 235655784360000, 579.00, 477.00 }, + { 235655792795000, 578.00, 496.00 }, + { 235655799559531, 576.27, 515.04 }, + { 235655800612000, 576.00, 518.00 }, + { 235655809535000, 574.00, 542.00 }, + { 235655816988015, 572.17, 564.86 }, + { 235655817685000, 572.00, 567.00 }, + { 235655825981000, 569.00, 595.00 }, + { 235655833808653, 566.26, 620.60 }, + { 235655834541000, 566.00, 623.00 }, + { 235655842893000, 563.00, 649.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -419.749695); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -398.303894); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3309.016357); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3969.099854); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownSlow2) { + // Sailfish - fling down - slow - 2 + Position values[] = { + { 235671152083370, 485.24, 558.28 }, + { 235671154126000, 485.00, 559.00 }, + { 235671162497000, 484.00, 566.00 }, + { 235671168750511, 483.27, 573.29 }, + { 235671171071000, 483.00, 576.00 }, + { 235671179390000, 482.00, 588.00 }, + { 235671185417210, 481.31, 598.98 }, + { 235671188173000, 481.00, 604.00 }, + { 235671196371000, 480.00, 624.00 }, + { 235671202084196, 479.27, 639.98 }, + { 235671204235000, 479.00, 646.00 }, + { 235671212554000, 478.00, 673.00 }, + { 235671219471011, 476.39, 697.12 }, + { 235671221159000, 476.00, 703.00 }, + { 235671229592000, 474.00, 734.00 }, + { 235671236281462, 472.43, 758.38 }, + { 235671238098000, 472.00, 765.00 }, + { 235671246532000, 470.00, 799.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -262.80426); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -243.665344); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4215.682129); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4587.986816); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownSlow3) { + // Sailfish - fling down - slow - 3 + Position values[] = { + { 170983201000, 557.00, 533.00 }, + { 171000668000, 556.00, 534.00 }, + { 171007359750, 554.73, 535.27 }, + { 171011197000, 554.00, 536.00 }, + { 171017660000, 552.00, 540.00 }, + { 171024201831, 549.97, 544.73 }, + { 171027333000, 549.00, 547.00 }, + { 171034603000, 545.00, 557.00 }, + { 171041043371, 541.98, 567.55 }, + { 171043147000, 541.00, 571.00 }, + { 171051052000, 536.00, 586.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -723.413513); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -651.038452); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 2091.502441); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 1934.517456); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownFaster1) { + // Sailfish - fling down - faster - 1 + Position values[] = { + { 235695280333000, 558.00, 451.00 }, + { 235695283971237, 558.43, 454.45 }, + { 235695289038000, 559.00, 462.00 }, + { 235695297388000, 561.00, 478.00 }, + { 235695300638465, 561.83, 486.25 }, + { 235695305265000, 563.00, 498.00 }, + { 235695313591000, 564.00, 521.00 }, + { 235695317305492, 564.43, 532.68 }, + { 235695322181000, 565.00, 548.00 }, + { 235695330709000, 565.00, 577.00 }, + { 235695333972227, 565.00, 588.10 }, + { 235695339250000, 565.00, 609.00 }, + { 235695347839000, 565.00, 642.00 }, + { 235695351313257, 565.00, 656.18 }, + { 235695356412000, 565.00, 677.00 }, + { 235695364899000, 563.00, 710.00 }, + { 235695368118682, 562.24, 722.52 }, + { 235695373403000, 564.00, 744.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4254.639648); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4698.415039); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownFaster2) { + // Sailfish - fling down - faster - 2 + Position values[] = { + { 235709624766000, 535.00, 579.00 }, + { 235709642256000, 534.00, 580.00 }, + { 235709643350278, 533.94, 580.06 }, + { 235709650760000, 532.00, 584.00 }, + { 235709658615000, 530.00, 593.00 }, + { 235709660170495, 529.60, 594.78 }, + { 235709667095000, 527.00, 606.00 }, + { 235709675616000, 524.00, 628.00 }, + { 235709676983261, 523.52, 631.53 }, + { 235709684289000, 521.00, 652.00 }, + { 235709692763000, 518.00, 682.00 }, + { 235709693804993, 517.63, 685.69 }, + { 235709701438000, 515.00, 709.00 }, + { 235709709830000, 512.00, 739.00 }, + { 235709710626776, 511.72, 741.85 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -430.440247); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -447.600311); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 3953.859375); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4316.155273); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownFaster3) { + // Sailfish - fling down - faster - 3 + Position values[] = { + { 235727628927000, 540.00, 440.00 }, + { 235727636810000, 537.00, 454.00 }, + { 235727646176000, 536.00, 454.00 }, + { 235727653586628, 535.12, 456.65 }, + { 235727654557000, 535.00, 457.00 }, + { 235727663024000, 534.00, 465.00 }, + { 235727670410103, 533.04, 479.45 }, + { 235727670691000, 533.00, 480.00 }, + { 235727679255000, 531.00, 501.00 }, + { 235727687233704, 529.09, 526.73 }, + { 235727687628000, 529.00, 528.00 }, + { 235727696113000, 526.00, 558.00 }, + { 235727704057546, 523.18, 588.98 }, + { 235727704576000, 523.00, 591.00 }, + { 235727713099000, 520.00, 626.00 }, + { 235727720880776, 516.33, 655.36 }, + { 235727721580000, 516.00, 658.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4484.617676); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 4927.92627); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownFast1) { + // Sailfish - fling down - fast - 1 + Position values[] = { + { 235762352849000, 467.00, 286.00 }, + { 235762360250000, 443.00, 344.00 }, + { 235762362787412, 434.77, 363.89 }, + { 235762368807000, 438.00, 359.00 }, + { 235762377220000, 425.00, 423.00 }, + { 235762379608561, 421.31, 441.17 }, + { 235762385698000, 412.00, 528.00 }, + { 235762394133000, 406.00, 648.00 }, + { 235762396429369, 404.37, 680.67 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 19084.931641); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16064.685547); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownFast2) { + // Sailfish - fling down - fast - 2 + Position values[] = { + { 235772487188000, 576.00, 204.00 }, + { 235772495159000, 553.00, 236.00 }, + { 235772503568000, 551.00, 240.00 }, + { 235772508192247, 545.55, 254.17 }, + { 235772512051000, 541.00, 266.00 }, + { 235772520794000, 520.00, 337.00 }, + { 235772525015263, 508.92, 394.43 }, + { 235772529174000, 498.00, 451.00 }, + { 235772537635000, 484.00, 589.00 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 18660.048828); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 16918.439453); // lsq2 +} + + +TEST_F(VelocityTrackerTest, SailfishFlingDownFast3) { + // Sailfish - fling down - fast - 3 + Position values[] = { + { 507650295000, 628.00, 233.00 }, + { 507658234000, 605.00, 269.00 }, + { 507666784000, 601.00, 274.00 }, + { 507669660483, 599.65, 275.68 }, + { 507675427000, 582.00, 308.00 }, + { 507683740000, 541.00, 404.00 }, + { 507686506238, 527.36, 435.95 }, + { 507692220000, 487.00, 581.00 }, + { 507700707000, 454.00, 792.00 }, + { 507703352649, 443.71, 857.77 }, + }; + size_t count = sizeof(values) / sizeof(Position); + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6772.508301); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_X, -6388.48877); // lsq2 + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 29765.908203); // impulse + computeAndCheckVelocity(values, count, AMOTION_EVENT_AXIS_Y, 28354.796875); // lsq2 +} + + +} // namespace android diff --git a/libs/sensor/OWNERS b/libs/sensor/OWNERS new file mode 100644 index 0000000000..6a38a1ff14 --- /dev/null +++ b/libs/sensor/OWNERS @@ -0,0 +1,2 @@ +ashutoshj@google.com +pengxu@google.com diff --git a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp index f9f87ff1b8..6b86433a09 100644 --- a/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp +++ b/libs/vr/libbufferhubqueue/buffer_hub_queue_client.cpp @@ -429,7 +429,7 @@ pdx::Status<void> BufferHubQueue::FreeAllBuffers() { if (!status) { ALOGE( "ProducerQueue::FreeAllBuffers: Failed to remove buffer at " - "slot=%d.", + "slot=%zu.", slot); last_error = status.error_status(); } diff --git a/services/audiomanager/Android.bp b/services/audiomanager/Android.bp index 22b084abc6..12ad47e15a 100644 --- a/services/audiomanager/Android.bp +++ b/services/audiomanager/Android.bp @@ -3,7 +3,6 @@ cc_library_shared { srcs: [ "IAudioManager.cpp", - "IPlayer.cpp", ], shared_libs: [ diff --git a/services/audiomanager/IPlayer.cpp b/services/audiomanager/IPlayer.cpp deleted file mode 100644 index e8a9c3472f..0000000000 --- a/services/audiomanager/IPlayer.cpp +++ /dev/null @@ -1,189 +0,0 @@ -/* -** -** Copyright 2017, The Android Open Source Project -** -** Licensed under the Apache License, Version 2.0 (the "License"); -** you may not use this file except in compliance with the License. -** You may obtain a copy of the License at -** -** http://www.apache.org/licenses/LICENSE-2.0 -** -** Unless required by applicable law or agreed to in writing, software -** distributed under the License is distributed on an "AS IS" BASIS, -** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -** See the License for the specific language governing permissions and -** limitations under the License. -*/ - -#define LOG_TAG "IPlayer" -//#define LOG_NDEBUG 0 -#include <utils/Log.h> - -#include <stdint.h> -#include <sys/types.h> - -#include <binder/Parcel.h> - -#include <audiomanager/IPlayer.h> - -namespace android { - -enum { - START = IBinder::FIRST_CALL_TRANSACTION, - PAUSE = IBinder::FIRST_CALL_TRANSACTION + 1, - STOP = IBinder::FIRST_CALL_TRANSACTION + 2, - SET_VOLUME = IBinder::FIRST_CALL_TRANSACTION + 3, - SET_PAN = IBinder::FIRST_CALL_TRANSACTION + 4, - SET_START_DELAY_MS = IBinder::FIRST_CALL_TRANSACTION + 5, - APPLY_VOLUME_SHAPER = IBinder::FIRST_CALL_TRANSACTION + 6, -}; - -class BpPlayer : public BpInterface<IPlayer> -{ -public: - explicit BpPlayer(const sp<IBinder>& impl) - : BpInterface<IPlayer>(impl) - { - } - - virtual void start() - { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - remote()->transact(START, data, &reply); - } - - virtual void pause() - { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - remote()->transact(PAUSE, data, &reply); - } - - virtual void stop() - { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - remote()->transact(STOP, data, &reply); - } - - virtual void setVolume(float vol) - { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - data.writeFloat(vol); - remote()->transact(SET_VOLUME, data, &reply); - } - - virtual void setPan(float pan) - { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - data.writeFloat(pan); - remote()->transact(SET_PAN, data, &reply); - } - - virtual void setStartDelayMs(int32_t delayMs) { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - data.writeInt32(delayMs); - remote()->transact(SET_START_DELAY_MS, data, &reply); - } - - virtual void applyVolumeShaper( - const sp<VolumeShaper::Configuration>& configuration, - const sp<VolumeShaper::Operation>& operation) { - Parcel data, reply; - data.writeInterfaceToken(IPlayer::getInterfaceDescriptor()); - - status_t status = configuration.get() == nullptr - ? data.writeInt32(0) - : data.writeInt32(1) - ?: configuration->writeToParcel(&data); - if (status != NO_ERROR) { - ALOGW("applyVolumeShaper failed configuration parceling: %d", status); - return; // ignore error - } - - status = operation.get() == nullptr - ? status = data.writeInt32(0) - : data.writeInt32(1) - ?: operation->writeToParcel(&data); - if (status != NO_ERROR) { - ALOGW("applyVolumeShaper failed operation parceling: %d", status); - return; // ignore error - } - - status = remote()->transact(APPLY_VOLUME_SHAPER, data, &reply); - - ALOGW_IF(status != NO_ERROR, "applyVolumeShaper failed transact: %d", status); - return; // one way transaction, ignore error - } -}; - -IMPLEMENT_META_INTERFACE(Player, "android.media.IPlayer"); - -// ---------------------------------------------------------------------- - -status_t BnPlayer::onTransact( - uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) -{ - switch (code) { - case START: { - CHECK_INTERFACE(IPlayer, data, reply); - start(); - return NO_ERROR; - } break; - case PAUSE: { - CHECK_INTERFACE(IPlayer, data, reply); - pause(); - return NO_ERROR; - } - case STOP: { - CHECK_INTERFACE(IPlayer, data, reply); - stop(); - return NO_ERROR; - } break; - case SET_VOLUME: { - CHECK_INTERFACE(IPlayer, data, reply); - setVolume(data.readFloat()); - return NO_ERROR; - } break; - case SET_PAN: { - CHECK_INTERFACE(IPlayer, data, reply); - setPan(data.readFloat()); - return NO_ERROR; - } break; - case SET_START_DELAY_MS: { - CHECK_INTERFACE(IPlayer, data, reply); - setStartDelayMs(data.readInt32()); - return NO_ERROR; - } break; - case APPLY_VOLUME_SHAPER: { - CHECK_INTERFACE(IPlayer, data, reply); - sp<VolumeShaper::Configuration> configuration; - sp<VolumeShaper::Operation> operation; - - int32_t present; - status_t status = data.readInt32(&present); - if (status == NO_ERROR && present != 0) { - configuration = new VolumeShaper::Configuration(); - status = configuration->readFromParcel(data); - } - status = status ?: data.readInt32(&present); - if (status == NO_ERROR && present != 0) { - operation = new VolumeShaper::Operation(); - status = operation->readFromParcel(data); - } - if (status == NO_ERROR) { - // one way transaction, no error returned - applyVolumeShaper(configuration, operation); - } - return NO_ERROR; - } break; - default: - return BBinder::onTransact(code, data, reply, flags); - } -} - -} // namespace android diff --git a/services/sensorservice/OWNERS b/services/sensorservice/OWNERS new file mode 100644 index 0000000000..6a38a1ff14 --- /dev/null +++ b/services/sensorservice/OWNERS @@ -0,0 +1,2 @@ +ashutoshj@google.com +pengxu@google.com diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp index 7d9b0b730a..da3b2758d1 100644 --- a/services/sensorservice/SensorDevice.cpp +++ b/services/sensorservice/SensorDevice.cpp @@ -223,8 +223,13 @@ ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) { } void SensorDevice::autoDisable(void *ident, int handle) { - Info& info( mActivationCount.editValueFor(handle) ); Mutex::Autolock _l(mLock); + ssize_t activationIndex = mActivationCount.indexOfKey(handle); + if (activationIndex < 0) { + ALOGW("Handle %d cannot be found in activation record", handle); + return; + } + Info& info(mActivationCount.editValueAt(activationIndex)); info.removeBatchParamsForIdent(ident); } @@ -235,7 +240,12 @@ status_t SensorDevice::activate(void* ident, int handle, int enabled) { bool actuateHardware = false; Mutex::Autolock _l(mLock); - Info& info( mActivationCount.editValueFor(handle) ); + ssize_t activationIndex = mActivationCount.indexOfKey(handle); + if (activationIndex < 0) { + ALOGW("Handle %d cannot be found in activation record", handle); + return BAD_VALUE; + } + Info& info(mActivationCount.editValueAt(activationIndex)); ALOGD_IF(DEBUG_CONNECTIONS, "SensorDevice::activate: ident=%p, handle=0x%08x, enabled=%d, count=%zu", @@ -329,7 +339,12 @@ status_t SensorDevice::batch( ident, handle, flags, samplingPeriodNs, maxBatchReportLatencyNs); Mutex::Autolock _l(mLock); - Info& info(mActivationCount.editValueFor(handle)); + ssize_t activationIndex = mActivationCount.indexOfKey(handle); + if (activationIndex < 0) { + ALOGW("Handle %d cannot be found in activation record", handle); + return BAD_VALUE; + } + Info& info(mActivationCount.editValueAt(activationIndex)); if (info.batchParams.indexOfKey(ident) < 0) { BatchParams params(samplingPeriodNs, maxBatchReportLatencyNs); diff --git a/services/surfaceflinger/Client.cpp b/services/surfaceflinger/Client.cpp index 8ba6cb9ba7..ea6541a734 100644 --- a/services/surfaceflinger/Client.cpp +++ b/services/surfaceflinger/Client.cpp @@ -140,7 +140,8 @@ status_t Client::createSurface( { sp<Layer> parent = nullptr; if (parentHandle != nullptr) { - parent = getLayerUser(parentHandle); + auto layerHandle = reinterpret_cast<Layer::Handle*>(parentHandle.get()); + parent = layerHandle->owner.promote(); if (parent == nullptr) { return NAME_NOT_FOUND; } diff --git a/services/surfaceflinger/DisplayDevice.cpp b/services/surfaceflinger/DisplayDevice.cpp index 248ef53f55..0244c1b862 100644 --- a/services/surfaceflinger/DisplayDevice.cpp +++ b/services/surfaceflinger/DisplayDevice.cpp @@ -28,6 +28,7 @@ #include <utils/RefBase.h> #include <utils/Log.h> +#include <ui/DebugUtils.h> #include <ui/DisplayInfo.h> #include <ui/PixelFormat.h> @@ -617,6 +618,7 @@ uint32_t DisplayDevice::getPrimaryDisplayOrientationTransform() { void DisplayDevice::dump(String8& result) const { const Transform& tr(mGlobalTransform); + ANativeWindow* const window = mNativeWindow.get(); EGLint redSize, greenSize, blueSize, alphaSize; eglGetConfigAttrib(mDisplay, mConfig, EGL_RED_SIZE, &redSize); eglGetConfigAttrib(mDisplay, mConfig, EGL_GREEN_SIZE, &greenSize); @@ -626,9 +628,9 @@ void DisplayDevice::dump(String8& result) const { result.appendFormat(" type=%x, hwcId=%d, layerStack=%u, (%4dx%4d), ANativeWindow=%p " "(%d:%d:%d:%d), orient=%2d (type=%08x), " "flips=%u, isSecure=%d, powerMode=%d, activeConfig=%d, numLayers=%zu\n", - mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, - mNativeWindow.get(), redSize, greenSize, blueSize, alphaSize, mOrientation, - tr.getType(), getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig, + mType, mHwcDisplayId, mLayerStack, mDisplayWidth, mDisplayHeight, window, + redSize, greenSize, blueSize, alphaSize, mOrientation, tr.getType(), + getPageFlipCount(), mIsSecure, mPowerMode, mActiveConfig, mVisibleLayersSortedByZ.size()); result.appendFormat(" v:[%d,%d,%d,%d], f:[%d,%d,%d,%d], s:[%d,%d,%d,%d]," "transform:[[%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f][%0.3f,%0.3f,%0.3f]]\n", @@ -636,6 +638,9 @@ void DisplayDevice::dump(String8& result) const { mFrame.left, mFrame.top, mFrame.right, mFrame.bottom, mScissor.left, mScissor.top, mScissor.right, mScissor.bottom, tr[0][0], tr[1][0], tr[2][0], tr[0][1], tr[1][1], tr[2][1], tr[0][2], tr[1][2], tr[2][2]); + auto const surface = static_cast<Surface*>(window); + android_dataspace dataspace = surface->getBuffersDataSpace(); + result.appendFormat(" dataspace: %s (%d)\n", dataspaceDetails(dataspace).c_str(), dataspace); String8 surfaceDump; mDisplaySurface->dumpAsString(surfaceDump); diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp index e92565fd9c..9435a187d9 100755 --- a/services/surfaceflinger/Layer.cpp +++ b/services/surfaceflinger/Layer.cpp @@ -2501,7 +2501,7 @@ void Layer::miniDump(String8& result, int32_t hwcId) const { const Layer::State& layerState(getDrawingState()); const HWCInfo& hwcInfo = mHwcLayers.at(hwcId); - result.appendFormat(" %10u | ", layerState.z); + result.appendFormat(" %10d | ", layerState.z); result.appendFormat("%10s | ", to_string(getCompositionType(hwcId)).c_str()); const Rect& frame = hwcInfo.displayFrame; @@ -2625,14 +2625,53 @@ bool Layer::reparentChildren(const sp<IBinder>& newParentHandle) { return true; } +bool Layer::reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle) { + if (newParentHandle == nullptr || childHandle == nullptr) { + return false; + } + + auto handle = static_cast<Handle*>(newParentHandle.get()); + sp<Layer> newParent = handle->owner.promote(); + if (newParent == nullptr) { + ALOGE("Unable to promote Layer handle"); + return false; + } + + handle = static_cast<Handle*>(childHandle.get()); + sp<Layer> child = handle->owner.promote(); + if (child == nullptr) { + ALOGE("Unable to promote child Layer handle"); + return false; + } + + if (mCurrentChildren.indexOf(child) < 0) { + ALOGE("Child layer is not child of current layer"); + return false; + } + + sp<Client> parentClient(mClientRef.promote()); + sp<Client> childClient(child->mClientRef.promote()); + sp<Client> newParentClient(newParent->mClientRef.promote()); + + if (parentClient != childClient || childClient != newParentClient) { + ALOGE("Current layer, child layer, and new parent layer must have the same client"); + return false; + } + + newParent->addChild(child); + mCurrentChildren.remove(child); + return true; +} + bool Layer::detachChildren() { traverseInZOrder(LayerVector::StateSet::Drawing, [this](Layer* child) { if (child == this) { return; } + sp<Client> parentClient = mClientRef.promote(); sp<Client> client(child->mClientRef.promote()); - if (client != nullptr) { + if (client != nullptr && parentClient != client) { client->detachLayer(child); } }); diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h index f7b82e4fb7..f94833b32f 100644 --- a/services/surfaceflinger/Layer.h +++ b/services/surfaceflinger/Layer.h @@ -241,6 +241,7 @@ public: bool setOverrideScalingMode(int32_t overrideScalingMode); void setInfo(uint32_t type, uint32_t appId); bool reparentChildren(const sp<IBinder>& layer); + bool reparentChild(const sp<IBinder>& newParentHandle, const sp<IBinder>& childHandle); bool detachChildren(); // If we have received a new buffer this frame, we will pass its surface diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 78af35b87d..cae4deacb0 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -2788,7 +2788,14 @@ status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, if (parent == nullptr) { mCurrentState.layersSortedByZ.add(lbc); } else { - if (mCurrentState.layersSortedByZ.indexOf(parent) < 0) { + bool found = false; + mCurrentState.traverseInZOrder([&](Layer* layer) { + if (layer == parent.get()) { + found = true; + } + }); + + if (!found) { ALOGE("addClientLayer called with a removed parent"); return NAME_NOT_FOUND; } @@ -3114,6 +3121,13 @@ uint32_t SurfaceFlinger::setClientStateLocked( // We don't trigger a traversal here because if no other state is // changed, we don't want this to cause any more work } + // Always re-parent the children that explicitly requested to get + // re-parented before the general re-parent of all children. + if (what & layer_state_t::eReparentChild) { + if (layer->reparentChild(s.parentHandleForChild, s.childHandle)) { + flags |= eTransactionNeeded|eTraversalNeeded; + } + } if (what & layer_state_t::eReparentChildren) { if (layer->reparentChildren(s.reparentHandle)) { flags |= eTransactionNeeded|eTraversalNeeded; diff --git a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp index a92e1f9f89..71aa52dee6 100644 --- a/services/surfaceflinger/SurfaceFlinger_hwc1.cpp +++ b/services/surfaceflinger/SurfaceFlinger_hwc1.cpp @@ -2679,6 +2679,13 @@ uint32_t SurfaceFlinger::setClientStateLocked( // We don't trigger a traversal here because if no other state is // changed, we don't want this to cause any more work } + // Always re-parent the children that explicitly requested to get + // re-parented before the general re-parent of all children. + if (what & layer_state_t::eReparentChild) { + if (layer->reparentChild(s.parentHandleForChild, s.childHandle)) { + flags |= eTransactionNeeded|eTraversalNeeded; + } + } if (what & layer_state_t::eReparentChildren) { if (layer->reparentChildren(s.reparentHandle)) { flags |= eTransactionNeeded|eTraversalNeeded; diff --git a/services/surfaceflinger/tests/Transaction_test.cpp b/services/surfaceflinger/tests/Transaction_test.cpp index 4ce14f8d3a..dea6503e4e 100644 --- a/services/surfaceflinger/tests/Transaction_test.cpp +++ b/services/surfaceflinger/tests/Transaction_test.cpp @@ -990,7 +990,7 @@ TEST_F(ChildLayerTest, ReparentChildren) { } } -TEST_F(ChildLayerTest, DetachChildren) { +TEST_F(ChildLayerTest, DetachChildrenSameClient) { SurfaceComposerClient::openGlobalTransaction(); mChild->show(); mChild->setPosition(10, 10); @@ -1015,6 +1015,52 @@ TEST_F(ChildLayerTest, DetachChildren) { mChild->hide(); SurfaceComposerClient::closeGlobalTransaction(true); + // Since the child has the same client as the parent, it will not get + // detached and will be hidden. + { + ScreenCapture::captureScreen(&mCapture); + mCapture->expectFGColor(64, 64); + mCapture->expectFGColor(74, 74); + mCapture->expectFGColor(84, 84); + } +} + +TEST_F(ChildLayerTest, DetachChildrenDifferentClient) { + sp<SurfaceComposerClient> mNewComposerClient = new SurfaceComposerClient; + sp<SurfaceControl> mChildNewClient = mNewComposerClient->createSurface( + String8("New Child Test Surface"), 10, 10, PIXEL_FORMAT_RGBA_8888, + 0, mFGSurfaceControl.get()); + + ASSERT_TRUE(mChildNewClient != NULL); + ASSERT_TRUE(mChildNewClient->isValid()); + + fillSurfaceRGBA8(mChildNewClient, 200, 200, 200); + + SurfaceComposerClient::openGlobalTransaction(); + mChild->hide(); + mChildNewClient->show(); + mChildNewClient->setPosition(10, 10); + mFGSurfaceControl->setPosition(64, 64); + SurfaceComposerClient::closeGlobalTransaction(true); + + { + ScreenCapture::captureScreen(&mCapture); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + + SurfaceComposerClient::openGlobalTransaction(); + mFGSurfaceControl->detachChildren(); + SurfaceComposerClient::closeGlobalTransaction(true); + + SurfaceComposerClient::openGlobalTransaction(); + mChildNewClient->hide(); + SurfaceComposerClient::closeGlobalTransaction(true); + // Nothing should have changed. { ScreenCapture::captureScreen(&mCapture); @@ -1123,4 +1169,48 @@ TEST_F(ChildLayerTest, Bug36858924) { fillSurfaceRGBA8(mFGSurfaceControl, 0, 255, 0); } +TEST_F(ChildLayerTest, ReparentChild) { + SurfaceComposerClient::openGlobalTransaction(); + mChild->show(); + mChild->setPosition(10, 10); + mFGSurfaceControl->setPosition(64, 64); + SurfaceComposerClient::closeGlobalTransaction(true); + + { + ScreenCapture::captureScreen(&mCapture); + // Top left of foreground must now be visible + mCapture->expectFGColor(64, 64); + // But 10 pixels in we should see the child surface + mCapture->expectChildColor(74, 74); + // And 10 more pixels we should be back to the foreground surface + mCapture->expectFGColor(84, 84); + } + mFGSurfaceControl->reparentChild(mBGSurfaceControl->getHandle(), mChild->getHandle()); + { + ScreenCapture::captureScreen(&mCapture); + mCapture->expectFGColor(64, 64); + // In reparenting we should have exposed the entire foreground surface. + mCapture->expectFGColor(74, 74); + // And the child layer should now begin at 10, 10 (since the BG + // layer is at (0, 0)). + mCapture->expectBGColor(9, 9); + mCapture->expectChildColor(10, 10); + } +} + +TEST_F(ChildLayerTest, NestedChildren) { + sp<SurfaceControl> grandchild = mComposerClient->createSurface( + String8("Grandchild surface"), + 10, 10, PIXEL_FORMAT_RGBA_8888, + 0, mChild.get()); + fillSurfaceRGBA8(grandchild, 50, 50, 50); + + { + ScreenCapture::captureScreen(&mCapture); + // Expect the grandchild to begin at 64, 64 because it's a child of mChild layer + // which begins at 64, 64 + mCapture->checkPixel(64, 64, 50, 50, 50); + } +} + } diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp index 60916f3ab9..d97ffa3148 100644 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.cpp @@ -36,7 +36,6 @@ #include <thread> constexpr Config NULL_DISPLAY_CONFIG = static_cast<Config>(0); -constexpr Display DEFAULT_DISPLAY = static_cast<Display>(1); using namespace sftest; @@ -168,7 +167,7 @@ void FakeComposerClient::enableCallback(bool enable) { ALOGV("enableCallback"); mCallbacksOn = enable; if (mCallbacksOn) { - mClient->onHotplug(DEFAULT_DISPLAY, IComposerCallback::Connection::CONNECTED); + mClient->onHotplug(PRIMARY_DISPLAY, IComposerCallback::Connection::CONNECTED); } } @@ -507,7 +506,7 @@ void FakeComposerClient::requestVSync(uint64_t vsyncTime) { if (mSurfaceComposer != nullptr) { mSurfaceComposer->injectVSync(timestamp); } else { - mClient->onVsync(DEFAULT_DISPLAY, timestamp); + mClient->onVsync(PRIMARY_DISPLAY, timestamp); } } } diff --git a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h index 294abb2c59..2a5a8ad03f 100644 --- a/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h +++ b/services/surfaceflinger/tests/fakehwc/FakeComposerClient.h @@ -19,6 +19,9 @@ #include "ComposerClient.h" #include "RenderState.h" +// Needed for display type/ID enums +#include <hardware/hwcomposer_defs.h> + #include <utils/Condition.h> #include <chrono> @@ -40,6 +43,13 @@ class SurfaceComposerClient; namespace sftest { +// NOTE: The ID's need to be exactly these. VR composer and parts of +// the SurfaceFlinger assume the display IDs to have these values +// despite the enum being documented as a display type. +// TODO: Reference to actual documentation +constexpr Display PRIMARY_DISPLAY = static_cast<Display>(HWC_DISPLAY_PRIMARY); +constexpr Display EXTERNAL_DISPLAY = static_cast<Display>(HWC_DISPLAY_EXTERNAL); + class FakeComposerClient : public ComposerBase { public: FakeComposerClient(); diff --git a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp index 8902ede301..9ac3331892 100644 --- a/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp +++ b/services/surfaceflinger/tests/fakehwc/SFFakeHwc_test.cpp @@ -168,17 +168,15 @@ void DisplayTest::SetUp() { android::hardware::ProcessState::self()->startThreadPool(); android::ProcessState::self()->startThreadPool(); - EXPECT_CALL(*mMockComposer, getDisplayType(1, _)) + EXPECT_CALL(*mMockComposer, getDisplayType(PRIMARY_DISPLAY, _)) .WillOnce(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), Return(Error::NONE))); - // Seems to be doubled right now, once for display ID 1 and once for 0. This sounds fishy - // but encoding that here exactly. - EXPECT_CALL(*mMockComposer, getDisplayAttribute(1, 1, _, _)) - .Times(5) - .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); - // TODO: Find out what code is generating the ID 0. - EXPECT_CALL(*mMockComposer, getDisplayAttribute(0, 1, _, _)) - .Times(5) + // Primary display will be queried twice for all 5 attributes. One + // set of queries comes from the SurfaceFlinger proper an the + // other set from the VR composer. + // TODO: Is VR composer always present? Change to atLeast(5)? + EXPECT_CALL(*mMockComposer, getDisplayAttribute(PRIMARY_DISPLAY, 1, _, _)) + .Times(2 * 5) .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); startSurfaceFlinger(); @@ -207,31 +205,32 @@ void DisplayTest::TearDown() { TEST_F(DisplayTest, Hotplug) { ALOGD("DisplayTest::Hotplug"); - EXPECT_CALL(*mMockComposer, getDisplayType(2, _)) + EXPECT_CALL(*mMockComposer, getDisplayType(EXTERNAL_DISPLAY, _)) .Times(2) .WillRepeatedly(DoAll(SetArgPointee<1>(IComposerClient::DisplayType::PHYSICAL), Return(Error::NONE))); // The attribute queries will get done twice. This is for defaults - EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, _, _)) + EXPECT_CALL(*mMockComposer, getDisplayAttribute(EXTERNAL_DISPLAY, 1, _, _)) .Times(2 * 3) .WillRepeatedly(Invoke(mMockComposer, &MockComposerClient::getDisplayAttributeFake)); // ... and then special handling for dimensions. Specifying this // rules later means that gmock will try them first, i.e., // ordering of width/height vs. the default implementation for // other queries is significant. - EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::WIDTH, _)) + EXPECT_CALL(*mMockComposer, + getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::WIDTH, _)) .Times(2) .WillRepeatedly(DoAll(SetArgPointee<3>(400), Return(Error::NONE))); - EXPECT_CALL(*mMockComposer, getDisplayAttribute(2, 1, IComposerClient::Attribute::HEIGHT, _)) + EXPECT_CALL(*mMockComposer, + getDisplayAttribute(EXTERNAL_DISPLAY, 1, IComposerClient::Attribute::HEIGHT, _)) .Times(2) .WillRepeatedly(DoAll(SetArgPointee<3>(200), Return(Error::NONE))); // TODO: Width and height queries are not actually called. Display // info returns dimensions 0x0 in display info. Why? - mMockComposer->hotplugDisplay(static_cast<Display>(2), - IComposerCallback::Connection::CONNECTED); + mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED); { sp<android::IBinder> display( @@ -257,13 +256,11 @@ TEST_F(DisplayTest, Hotplug) { } } - mMockComposer->hotplugDisplay(static_cast<Display>(2), - IComposerCallback::Connection::DISCONNECTED); + mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED); mMockComposer->clearFrames(); - mMockComposer->hotplugDisplay(static_cast<Display>(2), - IComposerCallback::Connection::CONNECTED); + mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::CONNECTED); { sp<android::IBinder> display( @@ -288,8 +285,7 @@ TEST_F(DisplayTest, Hotplug) { ASSERT_EQ(NO_ERROR, surfaceControl->show()); } } - mMockComposer->hotplugDisplay(static_cast<Display>(2), - IComposerCallback::Connection::DISCONNECTED); + mMockComposer->hotplugDisplay(EXTERNAL_DISPLAY, IComposerCallback::Connection::DISCONNECTED); } //////////////////////////////////////////////// @@ -664,7 +660,7 @@ TEST_F(TransactionTest, LayerSetMatrix) { {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_H_ROT_90, {64, 64, 128, 128}}, {{0.f, 1.f, 1.f, 0.f}, HWC_TRANSFORM_FLIP_V_ROT_90, {64, 64, 128, 128}}}; // clang-format on - constexpr int TEST_COUNT = sizeof(MATRIX_TESTS)/sizeof(matrixTestData); + constexpr int TEST_COUNT = sizeof(MATRIX_TESTS) / sizeof(matrixTestData); for (int i = 0; i < TEST_COUNT; i++) { // TODO: How to leverage the HWC2 stringifiers? diff --git a/services/surfaceflinger/tests/hwc2/Android.mk b/services/surfaceflinger/tests/hwc2/Android.mk index 203ced5c3a..6d20349794 100644 --- a/services/surfaceflinger/tests/hwc2/Android.mk +++ b/services/surfaceflinger/tests/hwc2/Android.mk @@ -36,7 +36,9 @@ LOCAL_SHARED_LIBRARIES := \ libui \ libgui \ liblog \ - libsync + libsync \ + libskia \ + android.hardware.graphics.common@1.0 LOCAL_STATIC_LIBRARIES := \ libbase \ libadf \ @@ -49,6 +51,7 @@ LOCAL_SRC_FILES := \ Hwc2TestLayers.cpp \ Hwc2TestBuffer.cpp \ Hwc2TestClientTarget.cpp \ - Hwc2TestVirtualDisplay.cpp + Hwc2TestVirtualDisplay.cpp \ + Hwc2TestPixelComparator.cpp include $(BUILD_NATIVE_TEST) diff --git a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp index 4055527b13..4878c140ed 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp +++ b/services/surfaceflinger/tests/hwc2/Hwc2Test.cpp @@ -1775,6 +1775,145 @@ protected: } } + void createAndPresentVirtualDisplay(size_t layerCnt, + Hwc2TestCoverage coverage, + const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage>& + coverageExceptions) + { + Hwc2TestVirtualDisplay testVirtualDisplay(coverage); + hwc2_display_t display; + android_pixel_format_t desiredFormat = HAL_PIXEL_FORMAT_RGBA_8888; + + do { + // Items dependent on the display dimensions + hwc2_error_t err = HWC2_ERROR_NONE; + const UnsignedArea& dimension = + testVirtualDisplay.getDisplayDimension(); + ASSERT_NO_FATAL_FAILURE(createVirtualDisplay(dimension.width, + dimension.height, &desiredFormat, &display, &err)); + ASSERT_TRUE(err == HWC2_ERROR_NONE) + << "Cannot allocate virtual display"; + + ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_ON)); + ASSERT_NO_FATAL_FAILURE(enableVsync(display)); + + std::vector<hwc2_config_t> configs; + ASSERT_NO_FATAL_FAILURE(getDisplayConfigs(display, &configs)); + + for (auto config : configs) { + ASSERT_NO_FATAL_FAILURE(setActiveConfig(display, config)); + + Area displayArea; + ASSERT_NO_FATAL_FAILURE(getActiveDisplayArea(display, + &displayArea)); + + std::vector<hwc2_layer_t> layers; + ASSERT_NO_FATAL_FAILURE(createLayers(display, &layers, + layerCnt)); + Hwc2TestLayers testLayers(layers, coverage, displayArea, + coverageExceptions); + + /* + * Layouts that do not cover an entire virtual display will + * cause undefined behavior. + * Enable optimizeLayouts to avoid this. + */ + testLayers.optimizeLayouts(); + do { + // Items dependent on the testLayers properties + std::set<hwc2_layer_t> clientLayers; + std::set<hwc2_layer_t> clearLayers; + uint32_t numTypes, numRequests; + bool hasChanges, skip; + bool flipClientTarget; + int32_t presentFence; + Hwc2TestClientTarget testClientTarget; + buffer_handle_t outputBufferHandle; + android::base::unique_fd outputBufferReleaseFence; + + ASSERT_NO_FATAL_FAILURE(setLayerProperties(display, layers, + &testLayers, &skip)); + + if (skip) + continue; + + ASSERT_NO_FATAL_FAILURE(validateDisplay(display, &numTypes, + &numRequests, &hasChanges)); + + if (hasChanges) + EXPECT_LE(numTypes, static_cast<uint32_t>(layers.size())) + << "wrong number of requests"; + + ASSERT_NO_FATAL_FAILURE(handleCompositionChanges(display, + testLayers, layers, numTypes, &clientLayers)); + + ASSERT_NO_FATAL_FAILURE(handleRequests(display, layers, + numRequests, &clearLayers, &flipClientTarget)); + ASSERT_NO_FATAL_FAILURE(setClientTarget(display, + &testClientTarget, testLayers, clientLayers, + clearLayers, flipClientTarget, displayArea)); + ASSERT_NO_FATAL_FAILURE(acceptDisplayChanges(display)); + + ASSERT_EQ(testVirtualDisplay.getOutputBuffer( + &outputBufferHandle, &outputBufferReleaseFence), 0); + ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display, + outputBufferHandle, outputBufferReleaseFence)); + + EXPECT_NO_FATAL_FAILURE(presentDisplay(display, + &presentFence)); + ASSERT_NO_FATAL_FAILURE(closeFences(display, presentFence)); + + ASSERT_EQ(testVirtualDisplay.verifyOutputBuffer(&testLayers, + &layers, &clearLayers), 0); + + /* + * Upscaling the image causes minor pixel differences. + * Work around this by using some threshold. + * + * Fail test if we are off by more than 1% of our + * pixels. + */ + ComparatorResult& comparatorResult = ComparatorResult::get(); + int threshold = (dimension.width * dimension.height) / 100; + double diffPercent = (comparatorResult.getDifferentPixelCount() * 100.0) / + (dimension.width * dimension.height); + + if (comparatorResult.getDifferentPixelCount() != 0) + EXPECT_TRUE(false) + << comparatorResult.getDifferentPixelCount() << " pixels (" + << diffPercent << "%) are different."; + + if (comparatorResult.getDifferentPixelCount() > threshold) { + EXPECT_TRUE(false) + << "Mismatched pixel count exceeds threshold. " + << "Writing buffers to file."; + + const ::testing::TestInfo* const test_info = + ::testing::UnitTest::GetInstance() + ->current_test_info(); + + EXPECT_EQ(testVirtualDisplay.writeBuffersToFile( + test_info->name()), 0) + << "Failed to write buffers."; + } + + ASSERT_LE(comparatorResult.getDifferentPixelCount(), threshold) + << comparatorResult.getDifferentPixelCount() << " pixels (" + << diffPercent << "%) are different. " + << "Exceeds 1% threshold, terminating test. " + << "Test case: " << testLayers.dump(); + + } while (testLayers.advance()); + + ASSERT_NO_FATAL_FAILURE(destroyLayers(display, + std::move(layers))); + } + ASSERT_NO_FATAL_FAILURE(disableVsync(display)); + ASSERT_NO_FATAL_FAILURE(setPowerMode(display, HWC2_POWER_MODE_OFF)); + ASSERT_NO_FATAL_FAILURE(destroyVirtualDisplay(display)); + } while (testVirtualDisplay.advance()); + } + hwc2_device_t* mHwc2Device = nullptr; enum class Hwc2TestHotplugStatus { @@ -4479,7 +4618,7 @@ TEST_F(Hwc2Test, SET_OUTPUT_BUFFER) buffer_handle_t handle; android::base::unique_fd acquireFence; - if (testVirtualDisplay->getBuffer(&handle, &acquireFence) >= 0) + if (testVirtualDisplay->getOutputBuffer(&handle, &acquireFence) >= 0) EXPECT_NO_FATAL_FAILURE(test->setOutputBuffer(display, handle, acquireFence)); })); @@ -4499,7 +4638,7 @@ TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_bad_display) ASSERT_NO_FATAL_FAILURE(test->getBadDisplay(&badDisplay)); - if (testVirtualDisplay->getBuffer(&handle, &acquireFence) < 0) + if (testVirtualDisplay->getOutputBuffer(&handle, &acquireFence) < 0) return; ASSERT_NO_FATAL_FAILURE(test->setOutputBuffer(badDisplay, @@ -4539,7 +4678,7 @@ TEST_F(Hwc2Test, SET_OUTPUT_BUFFER_unsupported) android::base::unique_fd acquireFence; hwc2_error_t err = HWC2_ERROR_NONE; - if (testVirtualDisplay.getBuffer(&handle, &acquireFence) < 0) + if (testVirtualDisplay.getOutputBuffer(&handle, &acquireFence) < 0) continue; ASSERT_NO_FATAL_FAILURE(setOutputBuffer(display, handle, @@ -4557,3 +4696,74 @@ TEST_F(Hwc2Test, DUMP) ASSERT_NO_FATAL_FAILURE(dump(&buffer)); } + +/* + * TODO(b/64724708): Hwc2TestPropertyName::BufferArea MUST be default for all + * virtual display tests as we don't handle this case correctly. + * + * Only default dataspace is supported in our drawing code. + */ +const std::unordered_map<Hwc2TestPropertyName, Hwc2TestCoverage> + virtualDisplayExceptions = + {{Hwc2TestPropertyName::BufferArea, Hwc2TestCoverage::Default}, + {Hwc2TestPropertyName::Dataspace, Hwc2TestCoverage::Default}}; + +/* TESTCASE: Tests that the HWC2 can present 1 layer with default coverage on a + * virtual display. */ +TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_1) +{ + Hwc2TestCoverage coverage = Hwc2TestCoverage::Default; + const size_t layerCnt = 1; + ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage, + virtualDisplayExceptions)); +} + +/* TESTCASE: Tests that the HWC2 can present 1 layer with basic coverage on a + * virtual display. */ +TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_basic_1) +{ + Hwc2TestCoverage coverage = Hwc2TestCoverage::Basic; + const size_t layerCnt = 1; + ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage, + virtualDisplayExceptions)); +} + +/* TESTCASE: Tests that the HWC2 can present 2 layers with default coverage on a + * virtual display. */ +TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_2) +{ + Hwc2TestCoverage coverage = Hwc2TestCoverage::Default; + const size_t layerCnt = 2; + ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage, + virtualDisplayExceptions)); +} + +/* TESTCASE: Tests that the HWC2 can present 3 layers with default coverage on a + * virtual display. */ +TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_3) +{ + Hwc2TestCoverage coverage = Hwc2TestCoverage::Default; + const size_t layerCnt = 3; + ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage, + virtualDisplayExceptions)); +} + +/* TESTCASE: Tests that the HWC2 can present 4 layers with default coverage on a + * virtual display. */ +TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_4) +{ + Hwc2TestCoverage coverage = Hwc2TestCoverage::Default; + const size_t layerCnt = 4; + ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage, + virtualDisplayExceptions)); +} + +/* TESTCASE: Tests that the HWC2 can present 5 layers with default coverage on a + * virtual display. */ +TEST_F(Hwc2Test, PRESENT_VIRTUAL_DISPLAY_default_5) +{ + Hwc2TestCoverage coverage = Hwc2TestCoverage::Default; + const size_t layerCnt = 5; + ASSERT_NO_FATAL_FAILURE(createAndPresentVirtualDisplay(layerCnt, coverage, + virtualDisplayExceptions)); +} diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp index 1d3a1d38ac..648456295d 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.cpp @@ -23,14 +23,17 @@ #include <gui/BufferItemConsumer.h> #include <ui/GraphicBuffer.h> +#include <android/hardware/graphics/common/1.0/types.h> #include <math/vec4.h> #include <GLES3/gl3.h> - +#include <SkImageEncoder.h> +#include <SkStream.h> #include "Hwc2TestBuffer.h" #include "Hwc2TestLayers.h" using namespace android; +using android::hardware::graphics::common::V1_0::BufferUsage; /* Returns a fence from egl */ typedef void (*FenceCallback)(int32_t fence, void* callbackArgs); @@ -396,8 +399,9 @@ int Hwc2TestBuffer::generateBuffer() { /* Create new graphic buffer with correct dimensions */ mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height, - mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER, - "hwc2_test_buffer"); + mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, "hwc2_test_buffer"); + int ret = mGraphicBuffer->initCheck(); if (ret) { return ret; @@ -408,7 +412,8 @@ int Hwc2TestBuffer::generateBuffer() /* Locks the buffer for writing */ uint8_t* img; - mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN), + (void**)(&img)); uint32_t stride = mGraphicBuffer->getStride(); @@ -458,31 +463,22 @@ Hwc2TestClientTargetBuffer::Hwc2TestClientTargetBuffer() Hwc2TestClientTargetBuffer::~Hwc2TestClientTargetBuffer() { } -/* Generates a client target buffer using the layers assigned for client - * composition. Takes into account the individual layer properties such as +/* Generates a buffer from layersToDraw. + * Takes into account the individual layer properties such as * transform, blend mode, source crop, etc. */ -int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle, - int32_t* outFence, const Area& bufferArea, +static void compositeBufferFromLayers( + const android::sp<android::GraphicBuffer>& graphicBuffer, + android_pixel_format_t format, const Area& bufferArea, const Hwc2TestLayers* testLayers, - const std::set<hwc2_layer_t>* clientLayers, + const std::set<hwc2_layer_t>* layersToDraw, const std::set<hwc2_layer_t>* clearLayers) { - /* Create new graphic buffer with correct dimensions */ - mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height, - mFormat, GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_HW_RENDER, - "hwc2_test_buffer"); - int ret = mGraphicBuffer->initCheck(); - if (ret) { - return ret; - } - if (!mGraphicBuffer->handle) { - return -EINVAL; - } - + /* Locks the buffer for writing */ uint8_t* img; - mGraphicBuffer->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img)); + graphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN), + (void**)(&img)); - uint32_t stride = mGraphicBuffer->getStride(); + uint32_t stride = graphicBuffer->getStride(); float bWDiv3 = bufferArea.width / 3; float bW2Div3 = bufferArea.width * 2 / 3; @@ -497,10 +493,10 @@ int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle, uint8_t r = 0, g = 0, b = 0; float a = 0.0f; - /* Cycle through each client layer from back to front and + /* Cycle through each layer from back to front and * update the pixel color. */ - for (auto layer = clientLayers->rbegin(); - layer != clientLayers->rend(); ++layer) { + for (auto layer = layersToDraw->rbegin(); + layer != layersToDraw->rend(); ++layer) { const hwc_rect_t df = testLayers->getDisplayFrame(*layer); @@ -570,8 +566,8 @@ int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle, * (100x50) at the end of the transformation. */ if (transform & HWC_TRANSFORM_ROT_90) { float tmp = xPos; - xPos = -yPos * dfW / dfH; - yPos = tmp * dfH / dfW; + xPos = yPos * dfW / dfH; + yPos = -tmp * dfH / dfW; } /* Change origin back to the top left corner of the @@ -682,14 +678,114 @@ int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle, } /* Set the pixel color */ - setColor(x, y, mFormat, stride, img, r, g, b, a * 255); + setColor(x, y, format, stride, img, r, g, b, a * 255); } } - mGraphicBuffer->unlock(); + graphicBuffer->unlock(); +} + +/* Generates a client target buffer using the layers assigned for client + * composition. Takes into account the individual layer properties such as + * transform, blend mode, source crop, etc. */ +int Hwc2TestClientTargetBuffer::get(buffer_handle_t* outHandle, + int32_t* outFence, const Area& bufferArea, + const Hwc2TestLayers* testLayers, + const std::set<hwc2_layer_t>* clientLayers, + const std::set<hwc2_layer_t>* clearLayers) +{ + /* Create new graphic buffer with correct dimensions */ + mGraphicBuffer = new GraphicBuffer(bufferArea.width, bufferArea.height, + mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN | + BufferUsage::COMPOSER_OVERLAY, "hwc2_test_buffer"); + + int ret = mGraphicBuffer->initCheck(); + if (ret) + return ret; + + if (!mGraphicBuffer->handle) + return -EINVAL; + + compositeBufferFromLayers(mGraphicBuffer, mFormat, bufferArea, testLayers, + clientLayers, clearLayers); *outFence = mFenceGenerator->get(); *outHandle = mGraphicBuffer->handle; return 0; } + +void Hwc2TestVirtualBuffer::updateBufferArea(const Area& bufferArea) +{ + mBufferArea.width = bufferArea.width; + mBufferArea.height = bufferArea.height; +} + +bool Hwc2TestVirtualBuffer::writeBufferToFile(std::string path) +{ + SkFILEWStream file(path.c_str()); + const SkImageInfo info = SkImageInfo::Make(mBufferArea.width, + mBufferArea.height, SkColorType::kRGBA_8888_SkColorType, + SkAlphaType::kPremul_SkAlphaType); + + uint8_t* img; + mGraphicBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_WRITE_OFTEN), + (void**)(&img)); + + SkPixmap pixmap(info, img, mGraphicBuffer->getStride()); + bool result = file.isValid() && SkEncodeImage(&file, pixmap, + SkEncodedImageFormat::kPNG, 100); + + mGraphicBuffer->unlock(); + return result; +} + +/* Generates a buffer that holds the expected result of compositing all of our + * layers */ +int Hwc2TestExpectedBuffer::generateExpectedBuffer( + const Hwc2TestLayers* testLayers, + const std::vector<hwc2_layer_t>* allLayers, + const std::set<hwc2_layer_t>* clearLayers) +{ + mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height, + mFormat, BufferUsage::CPU_READ_OFTEN | BufferUsage::CPU_WRITE_OFTEN, + "hwc2_test_buffer"); + + int ret = mGraphicBuffer->initCheck(); + if (ret) + return ret; + + if (!mGraphicBuffer->handle) + return -EINVAL; + + const std::set<hwc2_layer_t> allLayerSet(allLayers->begin(), + allLayers->end()); + + compositeBufferFromLayers(mGraphicBuffer, mFormat, mBufferArea, testLayers, + &allLayerSet, clearLayers); + + return 0; +} + +int Hwc2TestOutputBuffer::getOutputBuffer(buffer_handle_t* outHandle, + int32_t* outFence) +{ + if (mBufferArea.width == -1 || mBufferArea.height == -1) + return -EINVAL; + + mGraphicBuffer = new GraphicBuffer(mBufferArea.width, mBufferArea.height, + mFormat, BufferUsage::CPU_READ_OFTEN | + BufferUsage::GPU_RENDER_TARGET, "hwc2_test_buffer"); + + int ret = mGraphicBuffer->initCheck(); + if (ret) + return ret; + + if (!mGraphicBuffer->handle) + return -EINVAL; + + *outFence = -1; + *outHandle = mGraphicBuffer->handle; + + return 0; +} diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h index b2b3a6696a..fd54fef2d7 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestBuffer.h @@ -71,4 +71,38 @@ protected: const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888; }; + +class Hwc2TestVirtualBuffer { +public: + void updateBufferArea(const Area& bufferArea); + + bool writeBufferToFile(std::string path); + + android::sp<android::GraphicBuffer>& graphicBuffer() + { + return mGraphicBuffer; + } + +protected: + android::sp<android::GraphicBuffer> mGraphicBuffer; + + Area mBufferArea = {-1, -1}; + + const android_pixel_format_t mFormat = HAL_PIXEL_FORMAT_RGBA_8888; +}; + + +class Hwc2TestExpectedBuffer : public Hwc2TestVirtualBuffer { +public: + int generateExpectedBuffer(const Hwc2TestLayers* testLayers, + const std::vector<hwc2_layer_t>* allLayers, + const std::set<hwc2_layer_t>* clearLayers); +}; + + +class Hwc2TestOutputBuffer : public Hwc2TestVirtualBuffer { +public: + int getOutputBuffer(buffer_handle_t* outHandle, int32_t* outFence); +}; + #endif /* ifndef _HWC2_TEST_BUFFER_H */ diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp new file mode 100644 index 0000000000..904b927abf --- /dev/null +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.cpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2017 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 <sstream> +#include <android/hardware/graphics/common/1.0/types.h> + +#include "Hwc2TestPixelComparator.h" + +using android::hardware::graphics::common::V1_0::BufferUsage; + +uint32_t ComparatorResult::getPixel(int32_t x, int32_t y, uint32_t stride, + uint8_t* img) const +{ + uint32_t r = img[(y * stride + x) * 4 + 0]; + uint32_t g = img[(y * stride + x) * 4 + 1]; + uint32_t b = img[(y * stride + x) * 4 + 2]; + uint32_t a = img[(y * stride + x) * 4 + 3]; + + uint32_t pixel = 0; + pixel |= r; + pixel |= g << 8; + pixel |= b << 16; + pixel |= a << 24; + return pixel; +} + +void ComparatorResult::CompareBuffers( + android::sp<android::GraphicBuffer>& resultBuffer, + android::sp<android::GraphicBuffer>& expectedBuffer) +{ + uint8_t* resultBufferImg; + uint8_t* expectedBufferImg; + resultBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_READ_OFTEN), + (void**)(&resultBufferImg)); + + expectedBuffer->lock(static_cast<uint32_t>(BufferUsage::CPU_READ_OFTEN), + (void**)(&expectedBufferImg)); + mComparisons.clear(); + int32_t mDifferentPixelCount = 0; + int32_t mBlankPixelCount = 0; + + for (uint32_t y = 0; y < resultBuffer->getHeight(); y++) { + for (uint32_t x = 0; x < resultBuffer->getWidth(); x++) { + uint32_t result = getPixel(x, y, resultBuffer->getStride(), + resultBufferImg); + uint32_t expected = getPixel(x, y, expectedBuffer->getStride(), + expectedBufferImg); + + if (result == 0) + mBlankPixelCount++; + + if (result != expected) + mDifferentPixelCount++; + + mComparisons.emplace_back(std::make_tuple(x, y, result, expected)); + } + } + resultBuffer->unlock(); + expectedBuffer->unlock(); +} + +std::string ComparatorResult::pixelDiff(uint32_t x, uint32_t y, + uint32_t resultPixel, uint32_t expectedPixel) const +{ + uint32_t resultAlpha = (resultPixel >> 24) & 0xFF; + uint32_t resultBlue = (resultPixel >> 16) & 0xFF; + uint32_t resultGreen = (resultPixel >> 8) & 0xFF; + uint32_t resultRed = resultPixel & 0xFF; + + uint32_t expectedAlpha = (expectedPixel >> 24) & 0xFF; + uint32_t expectedBlue = (expectedPixel >> 16) & 0xFF; + uint32_t expectedGreen = (expectedPixel >> 8) & 0xFF; + uint32_t expectedRed = expectedPixel & 0xFF; + + std::ostringstream stream; + + stream << "x: " << x << " y: " << y << std::endl; + stream << std::hex; + stream << "Result pixel: " << resultRed << "|" << resultGreen << "|" + << resultBlue << "|" << resultAlpha << std::endl; + + stream << "Expected pixel: " << expectedRed << "|" << expectedGreen << "|" + << expectedBlue << "|" << expectedAlpha << std::endl; + + return stream.str(); +} + +std::string ComparatorResult::dumpComparison() const +{ + std::ostringstream stream; + stream << "Number of different pixels: " << mDifferentPixelCount; + + for (const auto& comparison : mComparisons) { + if (std::get<2>(comparison) != std::get<3>(comparison)) + stream << pixelDiff(std::get<0>(comparison), + std::get<1>(comparison), std::get<2>(comparison), + std::get<3>(comparison)); + } + return stream.str(); +} diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h new file mode 100644 index 0000000000..55fa936943 --- /dev/null +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestPixelComparator.h @@ -0,0 +1,59 @@ +/* + * Copyright (C) 2017 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 _HWC2_TEST_PIXEL_COMPARATOR_H +#define _HWC2_TEST_PIXEL_COMPARATOR_H + +#include <ui/GraphicBuffer.h> +#include <cstdint> +#include <string> +#include <utility> +#include <vector> + +class ComparatorResult { +public: + static ComparatorResult& get() + { + static ComparatorResult instance; + return instance; + } + + void CompareBuffers(android::sp<android::GraphicBuffer>& resultBuffer, + android::sp<android::GraphicBuffer>& expectedBuffer); + + std::string dumpComparison() const; + + ComparatorResult(const ComparatorResult&) = delete; + ComparatorResult(ComparatorResult&&) = delete; + ComparatorResult& operator=(ComparatorResult const&) = delete; + ComparatorResult& operator=(ComparatorResult&&) = delete; + + int32_t getDifferentPixelCount() const { return mDifferentPixelCount; } + int32_t getBlankPixelCount() const { return mBlankPixelCount; } + +private: + ComparatorResult() = default; + uint32_t getPixel(int32_t x, int32_t y, uint32_t stride, uint8_t* img) const; + std::string pixelDiff(uint32_t x, uint32_t y, uint32_t resultPixel, + uint32_t expectedPixel) const; + + int32_t mDifferentPixelCount; + int32_t mBlankPixelCount; + /* std::tuple<X coordinate, Y coordinate, resultPixel, expectedPixel> */ + std::vector<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t>> + mComparisons; +}; + +#endif /* ifndef _HWC2_TEST_PIXEL_COMPARATOR_H */ diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp index b5522de3a5..5b3bbeb708 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.cpp @@ -335,9 +335,9 @@ std::string Hwc2TestDisplayDimension::dump() const return dmp.str(); } -void Hwc2TestDisplayDimension::setDependent(Hwc2TestBuffer* buffer) +void Hwc2TestDisplayDimension::setDependent(Hwc2TestVirtualBuffer* buffer) { - mBuffer = buffer; + mBuffers.insert(buffer); updateDependents(); } @@ -345,8 +345,8 @@ void Hwc2TestDisplayDimension::updateDependents() { const UnsignedArea& curr = get(); - if (mBuffer) - mBuffer->updateBufferArea({static_cast<int32_t>(curr.width), + for (Hwc2TestVirtualBuffer* buffer : mBuffers) + buffer->updateBufferArea({static_cast<int32_t>(curr.width), static_cast<int32_t>(curr.height)}); } diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h index c2029aba04..cb811e06e2 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestProperties.h @@ -243,6 +243,7 @@ protected: static const std::array<bool, 6> mCompositionSupport; }; +class Hwc2TestVirtualBuffer; class Hwc2TestDisplayDimension : public Hwc2TestProperty<UnsignedArea> { public: @@ -250,12 +251,12 @@ public: std::string dump() const; - void setDependent(Hwc2TestBuffer* buffer); + void setDependent(Hwc2TestVirtualBuffer* buffer); private: void updateDependents(); - Hwc2TestBuffer* mBuffer; + std::set<Hwc2TestVirtualBuffer*> mBuffers; static const std::vector<UnsignedArea> mDefaultDisplayDimensions; static const std::vector<UnsignedArea> mBasicDisplayDimensions; diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp index d0fbc0b5ad..e6cceb82eb 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.cpp @@ -15,14 +15,18 @@ */ #include <sstream> +#include <sys/stat.h> #include "Hwc2TestVirtualDisplay.h" +#define DIR_NAME "images" + Hwc2TestVirtualDisplay::Hwc2TestVirtualDisplay( Hwc2TestCoverage coverage) : mDisplayDimension(coverage) { - mDisplayDimension.setDependent(&mBuffer); + mDisplayDimension.setDependent(&mOutputBuffer); + mDisplayDimension.setDependent(&mExpectedBuffer); } std::string Hwc2TestVirtualDisplay::dump() const @@ -36,11 +40,11 @@ std::string Hwc2TestVirtualDisplay::dump() const return dmp.str(); } -int Hwc2TestVirtualDisplay::getBuffer(buffer_handle_t* outHandle, +int Hwc2TestVirtualDisplay::getOutputBuffer(buffer_handle_t* outHandle, android::base::unique_fd* outAcquireFence) { int32_t acquireFence; - int ret = mBuffer.get(outHandle, &acquireFence); + int ret = mOutputBuffer.getOutputBuffer(outHandle, &acquireFence); outAcquireFence->reset(acquireFence); return ret; } @@ -59,3 +63,36 @@ UnsignedArea Hwc2TestVirtualDisplay::getDisplayDimension() const { return mDisplayDimension.get(); } + +int Hwc2TestVirtualDisplay::verifyOutputBuffer(const Hwc2TestLayers* testLayers, + const std::vector<hwc2_layer_t>* allLayers, + const std::set<hwc2_layer_t>* clearLayers) +{ + int ret = mExpectedBuffer.generateExpectedBuffer(testLayers, allLayers, + clearLayers); + if (ret) + return ret; + + ComparatorResult::get().CompareBuffers(mOutputBuffer.graphicBuffer(), + mExpectedBuffer.graphicBuffer()); + + return 0; +} + +int Hwc2TestVirtualDisplay::writeBuffersToFile(std::string name) +{ + std::ostringstream expectedPath; + std::ostringstream resultPath; + int ret = mkdir(DIR_NAME, DEFFILEMODE); + if (ret && errno != EEXIST) + return ret; + + expectedPath << DIR_NAME << "/expected-" << name << ".png"; + resultPath << DIR_NAME << "/result-" << name << ".png"; + + if (!mExpectedBuffer.writeBufferToFile(expectedPath.str()) || + !mOutputBuffer.writeBufferToFile(resultPath.str())) + return -1; + + return 0; +} diff --git a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h index 09420ef629..10c8ef0f1c 100644 --- a/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h +++ b/services/surfaceflinger/tests/hwc2/Hwc2TestVirtualDisplay.h @@ -18,6 +18,7 @@ #define _HWC2_TEST_VIRTUAL_DISPLAY_H #include "Hwc2TestBuffer.h" +#include "Hwc2TestPixelComparator.h" #include "Hwc2TestProperties.h" #define HWC2_INCLUDE_STRINGIFICATION @@ -32,17 +33,22 @@ public: std::string dump() const; - int getBuffer(buffer_handle_t* outHandle, + int getOutputBuffer(buffer_handle_t* outHandle, android::base::unique_fd* outAcquireFence); + int verifyOutputBuffer(const Hwc2TestLayers* testLayers, + const std::vector<hwc2_layer_t>* allLayers, + const std::set<hwc2_layer_t>* clearLayers); + + int writeBuffersToFile(std::string name); void reset(); bool advance(); UnsignedArea getDisplayDimension() const; private: - Hwc2TestBuffer mBuffer; - + Hwc2TestOutputBuffer mOutputBuffer; + Hwc2TestExpectedBuffer mExpectedBuffer; Hwc2TestDisplayDimension mDisplayDimension; }; |