Merge "Fix crash in SensorService. Ignore devices with no sensors." into mnc-dev
diff --git a/cmds/installd/commands.cpp b/cmds/installd/commands.cpp
index ebd71a0..faf48ef 100644
--- a/cmds/installd/commands.cpp
+++ b/cmds/installd/commands.cpp
@@ -745,6 +745,15 @@
ALOGE("execv(%s) failed: %s\n", PATCHOAT_BIN, strerror(errno));
}
+static bool check_boolean_property(const char* property_name, bool default_value = false) {
+ char tmp_property_value[PROPERTY_VALUE_MAX];
+ bool have_property = property_get(property_name, tmp_property_value, nullptr) > 0;
+ if (!have_property) {
+ return default_value;
+ }
+ return strcmp(tmp_property_value, "true") == 0;
+}
+
static void run_dex2oat(int zip_fd, int oat_fd, const char* input_file_name,
const char* output_file_name, int swap_fd, const char *pkgname, const char *instruction_set,
bool vm_safe_mode, bool debuggable)
@@ -805,9 +814,8 @@
(strcmp(vold_decrypt, "trigger_restart_min_framework") == 0 ||
(strcmp(vold_decrypt, "1") == 0)));
- char use_jit_property[PROPERTY_VALUE_MAX];
- bool have_jit_property = property_get("debug.usejit", use_jit_property, NULL) > 0;
- bool use_jit = have_jit_property && strcmp(use_jit_property, "true") == 0;
+ bool use_jit = check_boolean_property("debug.usejit");
+ bool generate_debug_info = check_boolean_property("debug.generate-debug-info");
static const char* DEX2OAT_BIN = "/system/bin/dex2oat";
@@ -900,6 +908,7 @@
+ (have_dex2oat_threads_flag ? 1 : 0)
+ (have_dex2oat_swap_fd ? 1 : 0)
+ (have_dex2oat_relocation_skip_flag ? 2 : 0)
+ + (generate_debug_info ? 1 : 0)
+ (debuggable ? 1 : 0)
+ dex2oat_flags_count];
int i = 0;
@@ -938,6 +947,9 @@
if (have_dex2oat_swap_fd) {
argv[i++] = dex2oat_swap_fd;
}
+ if (generate_debug_info) {
+ argv[i++] = "--generate-debug-info";
+ }
if (debuggable) {
argv[i++] = "--debuggable";
}
diff --git a/include/android/input.h b/include/android/input.h
index c7635b8..efbbb85 100644
--- a/include/android/input.h
+++ b/include/android/input.h
@@ -453,6 +453,7 @@
AINPUT_SOURCE_TOUCHSCREEN = 0x00001000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_MOUSE = 0x00002000 | AINPUT_SOURCE_CLASS_POINTER,
AINPUT_SOURCE_STYLUS = 0x00004000 | AINPUT_SOURCE_CLASS_POINTER,
+ AINPUT_SOURCE_BLUETOOTH_STYLUS = 0x00008000 | AINPUT_SOURCE_STYLUS,
AINPUT_SOURCE_TRACKBALL = 0x00010000 | AINPUT_SOURCE_CLASS_NAVIGATION,
AINPUT_SOURCE_TOUCHPAD = 0x00100000 | AINPUT_SOURCE_CLASS_POSITION,
AINPUT_SOURCE_TOUCH_NAVIGATION = 0x00200000 | AINPUT_SOURCE_CLASS_NONE,
diff --git a/include/input/Input.h b/include/input/Input.h
index 1da8356..4a67f47 100644
--- a/include/input/Input.h
+++ b/include/input/Input.h
@@ -383,6 +383,8 @@
inline int32_t getActionButton() const { return mActionButton; }
+ inline void setActionButton(int32_t button) { mActionButton = button; }
+
inline float getXOffset() const { return mXOffset; }
inline float getYOffset() const { return mYOffset; }
diff --git a/include/private/gui/LayerState.h b/include/private/gui/LayerState.h
index 2fa6ff9..cbe8733 100644
--- a/include/private/gui/LayerState.h
+++ b/include/private/gui/LayerState.h
@@ -39,6 +39,7 @@
enum {
eLayerHidden = 0x01, // SURFACE_HIDDEN in SurfaceControl.java
eLayerOpaque = 0x02, // SURFACE_OPAQUE
+ eLayerSecure = 0x80, // SECURE
};
enum {
@@ -48,10 +49,9 @@
eAlphaChanged = 0x00000008,
eMatrixChanged = 0x00000010,
eTransparentRegionChanged = 0x00000020,
- eVisibilityChanged = 0x00000040,
+ eFlagsChanged = 0x00000040,
eLayerStackChanged = 0x00000080,
eCropChanged = 0x00000100,
- eOpacityChanged = 0x00000200,
};
layer_state_t()
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 707a321..6ad47d8 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -310,11 +310,10 @@
layer_state_t* s = getLayerStateLocked(client, id);
if (!s)
return BAD_INDEX;
- if (mask & layer_state_t::eLayerOpaque) {
- s->what |= layer_state_t::eOpacityChanged;
- }
- if (mask & layer_state_t::eLayerHidden) {
- s->what |= layer_state_t::eVisibilityChanged;
+ if (mask & layer_state_t::eLayerOpaque ||
+ mask & layer_state_t::eLayerHidden ||
+ mask & layer_state_t::eLayerSecure) {
+ s->what |= layer_state_t::eFlagsChanged;
}
s->flags &= ~mask;
s->flags |= (flags & mask);
diff --git a/services/inputflinger/InputReader.cpp b/services/inputflinger/InputReader.cpp
index b54503e..f3cb0e6 100644
--- a/services/inputflinger/InputReader.cpp
+++ b/services/inputflinger/InputReader.cpp
@@ -3104,9 +3104,12 @@
&& mParameters.hasAssociatedDisplay) {
mSource = AINPUT_SOURCE_TOUCHSCREEN;
mDeviceMode = DEVICE_MODE_DIRECT;
- if (hasStylus() || hasExternalStylus()) {
+ if (hasStylus()) {
mSource |= AINPUT_SOURCE_STYLUS;
}
+ if (hasExternalStylus()) {
+ mSource |= AINPUT_SOURCE_BLUETOOTH_STYLUS;
+ }
} else if (mParameters.deviceType == Parameters::DEVICE_TYPE_TOUCH_NAVIGATION) {
mSource = AINPUT_SOURCE_TOUCH_NAVIGATION;
mDeviceMode = DEVICE_MODE_NAVIGATION;
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index 6267a4c..dd1bccf 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -89,7 +89,7 @@
result.appendFormat("handle=0x%08x, active-count=%zu, batch_period(ms)={ ", list[i].handle,
info.batchParams.size());
for (size_t j = 0; j < info.batchParams.size(); j++) {
- BatchParams params = info.batchParams.valueAt(j);
+ const BatchParams& params = info.batchParams.valueAt(j);
result.appendFormat("%4.1f%s", params.batchDelay / 1e6f,
j < info.batchParams.size() - 1 ? ", " : "");
}
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index f6ff092..550107c 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -195,9 +195,14 @@
mMapFlushEventsToConnections = new SensorEventConnection const * [minBufferSize];
mCurrentOperatingMode = NORMAL;
+ mNextSensorRegIndex = 0;
+ for (int i = 0; i < SENSOR_REGISTRATIONS_BUF_SIZE; ++i) {
+ mLastNSensorRegistrations.push();
+ }
+
+ mInitCheck = NO_ERROR;
mAckReceiver = new SensorEventAckReceiver(this);
mAckReceiver->run("SensorEventAckReceiver", PRIORITY_URGENT_DISPLAY);
- mInitCheck = NO_ERROR;
run("SensorService", PRIORITY_URGENT_DISPLAY);
}
}
@@ -324,11 +329,14 @@
result.appendFormat("non-wakeUp | ");
}
- const CircularBuffer* buf = mLastEventSeen.valueFor(s.getHandle());
- if (buf != NULL && s.getRequiredPermission().isEmpty()) {
- buf->printBuffer(result);
- } else {
- result.append("last=<> \n");
+ int bufIndex = mLastEventSeen.indexOfKey(s.getHandle());
+ if (bufIndex >= 0) {
+ const CircularBuffer* buf = mLastEventSeen.valueAt(bufIndex);
+ if (buf != NULL && s.getRequiredPermission().isEmpty()) {
+ buf->printBuffer(result);
+ } else {
+ result.append("last=<> \n");
+ }
}
result.append("\n");
}
@@ -346,7 +354,8 @@
result.appendFormat("Socket Buffer size = %d events\n",
mSocketBufferSize/sizeof(sensors_event_t));
- result.appendFormat("WakeLock Status: %s \n", mWakeLockAcquired ? "acquired" : "not held");
+ result.appendFormat("WakeLock Status: %s \n", mWakeLockAcquired ? "acquired" :
+ "not held");
result.appendFormat("Mode :");
switch(mCurrentOperatingMode) {
case NORMAL:
@@ -367,6 +376,34 @@
connection->dump(result);
}
}
+
+ result.appendFormat("Previous Registrations:\n");
+ // Log in the reverse chronological order.
+ int currentIndex = (mNextSensorRegIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
+ SENSOR_REGISTRATIONS_BUF_SIZE;
+ const int startIndex = currentIndex;
+ do {
+ const SensorRegistrationInfo& reg_info = mLastNSensorRegistrations[currentIndex];
+ if (SensorRegistrationInfo::isSentinel(reg_info)) {
+ // Ignore sentinel, proceed to next item.
+ currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
+ SENSOR_REGISTRATIONS_BUF_SIZE;
+ continue;
+ }
+ if (reg_info.mActivated) {
+ result.appendFormat("%02d:%02d:%02d activated package=%s handle=0x%08x "
+ "samplingRate=%dus maxReportLatency=%dus\n",
+ reg_info.mHour, reg_info.mMin, reg_info.mSec,
+ reg_info.mPackageName.string(), reg_info.mSensorHandle,
+ reg_info.mSamplingRateUs, reg_info.mMaxReportLatencyUs);
+ } else {
+ result.appendFormat("%02d:%02d:%02d de-activated package=%s handle=0x%08x\n",
+ reg_info.mHour, reg_info.mMin, reg_info.mSec,
+ reg_info.mPackageName.string(), reg_info.mSensorHandle);
+ }
+ currentIndex = (currentIndex - 1 + SENSOR_REGISTRATIONS_BUF_SIZE) %
+ SENSOR_REGISTRATIONS_BUF_SIZE;
+ } while(startIndex != currentIndex);
}
}
write(fd, result.string(), result.size());
@@ -890,6 +927,19 @@
if (err == NO_ERROR) {
connection->updateLooperRegistration(mLooper);
+ SensorRegistrationInfo ®_info =
+ mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex);
+ reg_info.mSensorHandle = handle;
+ reg_info.mSamplingRateUs = samplingPeriodNs/1000;
+ reg_info.mMaxReportLatencyUs = maxBatchReportLatencyNs/1000;
+ reg_info.mActivated = true;
+ reg_info.mPackageName = connection->getPackageName();
+ time_t rawtime = time(NULL);
+ struct tm * timeinfo = localtime(&rawtime);
+ reg_info.mHour = timeinfo->tm_hour;
+ reg_info.mMin = timeinfo->tm_min;
+ reg_info.mSec = timeinfo->tm_sec;
+ mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
}
if (err != NO_ERROR) {
@@ -910,6 +960,20 @@
if (err == NO_ERROR) {
SensorInterface* sensor = mSensorMap.valueFor(handle);
err = sensor ? sensor->activate(connection.get(), false) : status_t(BAD_VALUE);
+
+ }
+ if (err == NO_ERROR) {
+ SensorRegistrationInfo ®_info =
+ mLastNSensorRegistrations.editItemAt(mNextSensorRegIndex);
+ reg_info.mActivated = false;
+ reg_info.mPackageName= connection->getPackageName();
+ reg_info.mSensorHandle = handle;
+ time_t rawtime = time(NULL);
+ struct tm * timeinfo = localtime(&rawtime);
+ reg_info.mHour = timeinfo->tm_hour;
+ reg_info.mMin = timeinfo->tm_min;
+ reg_info.mSec = timeinfo->tm_sec;
+ mNextSensorRegIndex = (mNextSensorRegIndex + 1) % SENSOR_REGISTRATIONS_BUF_SIZE;
}
return err;
}
@@ -1182,14 +1246,38 @@
mPendingFlushConnections.clear();
}
+
+// ---------------------------------------------------------------------------
+SensorService::TrimmedSensorEvent::TrimmedSensorEvent(int sensorType) {
+ mTimestamp = -1;
+ const int numData = SensorService::getNumEventsForSensorType(sensorType);
+ if (sensorType == SENSOR_TYPE_STEP_COUNTER) {
+ mStepCounter = 0;
+ } else {
+ mData = new float[numData];
+ for (int i = 0; i < numData; ++i) {
+ mData[i] = -1.0;
+ }
+ }
+ mHour = mMin = mSec = INT32_MIN;
+}
+
+bool SensorService::TrimmedSensorEvent::isSentinel(const TrimmedSensorEvent& event) {
+ return (event.mHour == INT32_MIN && event.mMin == INT32_MIN && event.mSec == INT32_MIN);
+}
// --------------------------------------------------------------------------
SensorService::CircularBuffer::CircularBuffer(int sensor_event_type) {
mNextInd = 0;
- mTrimmedSensorEventArr = new TrimmedSensorEvent *[CIRCULAR_BUF_SIZE];
+ mBufSize = CIRCULAR_BUF_SIZE;
+ if (sensor_event_type == SENSOR_TYPE_STEP_COUNTER ||
+ sensor_event_type == SENSOR_TYPE_SIGNIFICANT_MOTION ||
+ sensor_event_type == SENSOR_TYPE_ACCELEROMETER) {
+ mBufSize = CIRCULAR_BUF_SIZE * 5;
+ }
+ mTrimmedSensorEventArr = new TrimmedSensorEvent *[mBufSize];
mSensorType = sensor_event_type;
- const int numData = SensorService::getNumEventsForSensorType(mSensorType);
- for (int i = 0; i < CIRCULAR_BUF_SIZE; ++i) {
- mTrimmedSensorEventArr[i] = new TrimmedSensorEvent(numData, mSensorType);
+ for (int i = 0; i < mBufSize; ++i) {
+ mTrimmedSensorEventArr[i] = new TrimmedSensorEvent(mSensorType);
}
}
@@ -1207,17 +1295,17 @@
curr_event->mHour = timeinfo->tm_hour;
curr_event->mMin = timeinfo->tm_min;
curr_event->mSec = timeinfo->tm_sec;
- mNextInd = (mNextInd + 1) % CIRCULAR_BUF_SIZE;
+ mNextInd = (mNextInd + 1) % mBufSize;
}
void SensorService::CircularBuffer::printBuffer(String8& result) const {
const int numData = SensorService::getNumEventsForSensorType(mSensorType);
int i = mNextInd, eventNum = 1;
- result.appendFormat("last %d events = < ", CIRCULAR_BUF_SIZE);
+ result.appendFormat("last %d events = < ", mBufSize);
do {
- if (mTrimmedSensorEventArr[i]->mTimestamp == -1) {
+ if (TrimmedSensorEvent::isSentinel(*mTrimmedSensorEventArr[i])) {
// Sentinel, ignore.
- i = (i + 1) % CIRCULAR_BUF_SIZE;
+ i = (i + 1) % mBufSize;
continue;
}
result.appendFormat("%d) ", eventNum++);
@@ -1231,15 +1319,15 @@
result.appendFormat("%lld %02d:%02d:%02d ", mTrimmedSensorEventArr[i]->mTimestamp,
mTrimmedSensorEventArr[i]->mHour, mTrimmedSensorEventArr[i]->mMin,
mTrimmedSensorEventArr[i]->mSec);
- i = (i + 1) % CIRCULAR_BUF_SIZE;
+ i = (i + 1) % mBufSize;
} while (i != mNextInd);
result.appendFormat(">\n");
}
bool SensorService::CircularBuffer::populateLastEvent(sensors_event_t *event) {
- int lastEventInd = (mNextInd - 1 + CIRCULAR_BUF_SIZE) % CIRCULAR_BUF_SIZE;
+ int lastEventInd = (mNextInd - 1 + mBufSize) % mBufSize;
// Check if the buffer is empty.
- if (mTrimmedSensorEventArr[lastEventInd]->mTimestamp == -1) {
+ if (TrimmedSensorEvent::isSentinel(*mTrimmedSensorEventArr[lastEventInd])) {
return false;
}
event->version = sizeof(sensors_event_t);
@@ -1255,7 +1343,7 @@
}
SensorService::CircularBuffer::~CircularBuffer() {
- for (int i = 0; i < CIRCULAR_BUF_SIZE; ++i) {
+ for (int i = 0; i < mBufSize; ++i) {
delete mTrimmedSensorEventArr[i];
}
delete [] mTrimmedSensorEventArr;
@@ -1301,8 +1389,9 @@
void SensorService::SensorEventConnection::dump(String8& result) {
Mutex::Autolock _l(mConnectionLock);
result.appendFormat("Operating Mode: %s\n", mDataInjectionMode ? "DATA_INJECTION" : "NORMAL");
- result.appendFormat("\t%s | WakeLockRefCount %d | uid %d | cache size %d | max cache size %d\n",
- mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize, mMaxCacheSize);
+ result.appendFormat("\t %s | WakeLockRefCount %d | uid %d | cache size %d | "
+ "max cache size %d\n", mPackageName.string(), mWakeLockRefCount, mUid, mCacheSize,
+ mMaxCacheSize);
for (size_t i = 0; i < mSensorInfo.size(); ++i) {
const FlushInfo& flushInfo = mSensorInfo.valueAt(i);
result.appendFormat("\t %s 0x%08x | status: %s | pending flush events %d \n",
diff --git a/services/sensorservice/SensorService.h b/services/sensorservice/SensorService.h
index 7c466c1..0a7abe8 100644
--- a/services/sensorservice/SensorService.h
+++ b/services/sensorservice/SensorService.h
@@ -54,6 +54,7 @@
#define SOCKET_BUFFER_SIZE_NON_BATCHED 4 * 1024
#define CIRCULAR_BUF_SIZE 10
+#define SENSOR_REGISTRATIONS_BUF_SIZE 20
struct sensors_poll_device_t;
struct sensors_module_t;
@@ -272,18 +273,8 @@
// for debugging.
int32_t mHour, mMin, mSec;
- TrimmedSensorEvent(int numData, int sensorType) {
- mTimestamp = -1;
- if (sensorType == SENSOR_TYPE_STEP_COUNTER) {
- mStepCounter = 0;
- } else {
- mData = new float[numData];
- for (int i = 0; i < numData; ++i) {
- mData[i] = -1.0;
- }
- }
- mHour = mMin = mSec = 0;
- }
+ TrimmedSensorEvent(int sensorType);
+ static bool isSentinel(const TrimmedSensorEvent& event);
~TrimmedSensorEvent() {
delete [] mData;
@@ -297,6 +288,7 @@
class CircularBuffer {
int mNextInd;
int mSensorType;
+ int mBufSize;
TrimmedSensorEvent ** mTrimmedSensorEventArr;
public:
CircularBuffer(int sensor_event_type);
@@ -306,6 +298,25 @@
~CircularBuffer();
};
+ struct SensorRegistrationInfo {
+ int32_t mSensorHandle;
+ String8 mPackageName;
+ bool mActivated;
+ int32_t mSamplingRateUs;
+ int32_t mMaxReportLatencyUs;
+ int32_t mHour, mMin, mSec;
+
+ SensorRegistrationInfo() : mPackageName() {
+ mSensorHandle = mSamplingRateUs = mMaxReportLatencyUs = INT32_MIN;
+ mHour = mMin = mSec = INT32_MIN;
+ mActivated = false;
+ }
+
+ static bool isSentinel(const SensorRegistrationInfo& info) {
+ return (info.mHour == INT32_MIN && info.mMin == INT32_MIN && info.mSec == INT32_MIN);
+ }
+ };
+
static int getNumEventsForSensorType(int sensor_event_type);
String8 getSensorName(int handle) const;
bool isVirtualSensor(int handle) const;
@@ -387,6 +398,8 @@
// The size of this vector is constant, only the items are mutable
KeyedVector<int32_t, CircularBuffer *> mLastEventSeen;
+ int mNextSensorRegIndex;
+ Vector<SensorRegistrationInfo> mLastNSensorRegistrations;
public:
void cleanupConnection(SensorEventConnection* connection);
status_t enable(const sp<SensorEventConnection>& connection, int handle,
diff --git a/services/surfaceflinger/Layer.cpp b/services/surfaceflinger/Layer.cpp
index e2a0167..39b80ec 100644
--- a/services/surfaceflinger/Layer.cpp
+++ b/services/surfaceflinger/Layer.cpp
@@ -76,7 +76,6 @@
mFiltering(false),
mNeedsFiltering(false),
mMesh(Mesh::TRIANGLE_FAN, 4, 2, 2),
- mSecure(false),
mProtectedByApp(false),
mHasSurface(false),
mClientRef(client),
@@ -96,6 +95,8 @@
layerFlags |= layer_state_t::eLayerHidden;
if (flags & ISurfaceComposerClient::eOpaque)
layerFlags |= layer_state_t::eLayerOpaque;
+ if (flags & ISurfaceComposerClient::eSecure)
+ layerFlags |= layer_state_t::eLayerSecure;
if (flags & ISurfaceComposerClient::eNonPremultiplied)
mPremultipliedAlpha = false;
@@ -256,7 +257,6 @@
mFormat = format;
mPotentialCursor = (flags & ISurfaceComposerClient::eCursorWindow) ? true : false;
- mSecure = (flags & ISurfaceComposerClient::eSecure) ? true : false;
mProtectedByApp = (flags & ISurfaceComposerClient::eProtectedByApp) ? true : false;
mCurrentOpacity = getOpacityForFormat(format);
@@ -858,6 +858,12 @@
return ((s.flags & layer_state_t::eLayerOpaque) != 0) || mCurrentOpacity;
}
+bool Layer::isSecure() const
+{
+ const Layer::State& s(mDrawingState);
+ return (s.flags & layer_state_t::eLayerSecure);
+}
+
bool Layer::isProtected() const
{
const sp<GraphicBuffer>& activeBuffer(mActiveBuffer);
diff --git a/services/surfaceflinger/Layer.h b/services/surfaceflinger/Layer.h
index 4e222b7..c1e5e9f 100644
--- a/services/surfaceflinger/Layer.h
+++ b/services/surfaceflinger/Layer.h
@@ -174,7 +174,7 @@
* isSecure - true if this surface is secure, that is if it prevents
* screenshots or VNC servers.
*/
- virtual bool isSecure() const { return mSecure; }
+ virtual bool isSecure() const;
/*
* isProtected - true if the layer may contain protected content in the
@@ -402,7 +402,6 @@
mutable Texture mTexture;
// page-flip thread (currently main thread)
- bool mSecure; // no screenshots
bool mProtectedByApp; // application requires protected path to external sink
// protected by mLock
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index a332cda..de0f921 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -2235,9 +2235,7 @@
if (layer->setTransparentRegionHint(s.transparentRegion))
flags |= eTraversalNeeded;
}
- if ((what & layer_state_t::eVisibilityChanged) ||
- (what & layer_state_t::eOpacityChanged)) {
- // TODO: should we just use an eFlagsChanged for this?
+ if (what & layer_state_t::eFlagsChanged) {
if (layer->setFlags(s.flags, s.mask))
flags |= eTraversalNeeded;
}