summaryrefslogtreecommitdiff
path: root/libs
diff options
context:
space:
mode:
Diffstat (limited to 'libs')
-rw-r--r--libs/gui/BLASTBufferQueue.cpp144
-rw-r--r--libs/gui/Surface.cpp6
-rw-r--r--libs/gui/SurfaceComposerClient.cpp5
-rw-r--r--libs/gui/include/gui/BLASTBufferQueue.h8
-rw-r--r--libs/gui/include/gui/Surface.h2
-rw-r--r--libs/gui/include/gui/TraceUtils.h16
-rw-r--r--libs/nativewindow/include/system/window.h5
-rw-r--r--libs/sensor/ISensorServer.cpp12
-rw-r--r--libs/sensor/Sensor.cpp6
-rw-r--r--libs/sensor/SensorManager.cpp15
-rw-r--r--libs/sensor/include/sensor/SensorManager.h1
11 files changed, 142 insertions, 78 deletions
diff --git a/libs/gui/BLASTBufferQueue.cpp b/libs/gui/BLASTBufferQueue.cpp
index 97c467053e..000f458fd1 100644
--- a/libs/gui/BLASTBufferQueue.cpp
+++ b/libs/gui/BLASTBufferQueue.cpp
@@ -485,20 +485,26 @@ void BLASTBufferQueue::releaseBuffer(const ReleaseCallbackId& callbackId,
mSyncedFrameNumbers.erase(callbackId.framenumber);
}
-void BLASTBufferQueue::acquireNextBufferLocked(
+status_t BLASTBufferQueue::acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) {
- // If the next transaction is set, we want to guarantee the our acquire will not fail, so don't
- // include the extra buffer when checking if we can acquire the next buffer.
- const bool includeExtraAcquire = !transaction;
- const bool maxAcquired = maxBuffersAcquired(includeExtraAcquire);
- if (mNumFrameAvailable == 0 || maxAcquired) {
- BQA_LOGV("Can't process next buffer maxBuffersAcquired=%s", boolToString(maxAcquired));
- return;
+ // Check if we have frames available and we have not acquired the maximum number of buffers.
+ // Even with this check, the consumer can fail to acquire an additional buffer if the consumer
+ // has already acquired (mMaxAcquiredBuffers + 1) and the new buffer is not droppable. In this
+ // case mBufferItemConsumer->acquireBuffer will return with NO_BUFFER_AVAILABLE.
+ if (mNumFrameAvailable == 0) {
+ BQA_LOGV("Can't acquire next buffer. No available frames");
+ return BufferQueue::NO_BUFFER_AVAILABLE;
+ }
+
+ if (mNumAcquired >= (mMaxAcquiredBuffers + 2)) {
+ BQA_LOGV("Can't acquire next buffer. Already acquired max frames %d max:%d + 2",
+ mNumAcquired, mMaxAcquiredBuffers);
+ return BufferQueue::NO_BUFFER_AVAILABLE;
}
if (mSurfaceControl == nullptr) {
BQA_LOGE("ERROR : surface control is null");
- return;
+ return NAME_NOT_FOUND;
}
SurfaceComposerClient::Transaction localTransaction;
@@ -515,10 +521,10 @@ void BLASTBufferQueue::acquireNextBufferLocked(
mBufferItemConsumer->acquireBuffer(&bufferItem, 0 /* expectedPresent */, false);
if (status == BufferQueue::NO_BUFFER_AVAILABLE) {
BQA_LOGV("Failed to acquire a buffer, err=NO_BUFFER_AVAILABLE");
- return;
+ return status;
} else if (status != OK) {
BQA_LOGE("Failed to acquire a buffer, err=%s", statusToString(status).c_str());
- return;
+ return status;
}
auto buffer = bufferItem.mGraphicBuffer;
@@ -528,7 +534,7 @@ void BLASTBufferQueue::acquireNextBufferLocked(
if (buffer == nullptr) {
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
BQA_LOGE("Buffer was empty");
- return;
+ return BAD_VALUE;
}
if (rejectBuffer(bufferItem)) {
@@ -537,8 +543,7 @@ void BLASTBufferQueue::acquireNextBufferLocked(
mSize.width, mSize.height, mRequestedSize.width, mRequestedSize.height,
buffer->getWidth(), buffer->getHeight(), bufferItem.mTransform);
mBufferItemConsumer->releaseBuffer(bufferItem, Fence::NO_FENCE);
- acquireNextBufferLocked(transaction);
- return;
+ return acquireNextBufferLocked(transaction);
}
mNumAcquired++;
@@ -592,9 +597,23 @@ void BLASTBufferQueue::acquireNextBufferLocked(
t->setDesiredPresentTime(bufferItem.mTimestamp);
}
- if (!mNextFrameTimelineInfoQueue.empty()) {
- t->setFrameTimelineInfo(mNextFrameTimelineInfoQueue.front());
- mNextFrameTimelineInfoQueue.pop();
+ // Drop stale frame timeline infos
+ while (!mPendingFrameTimelines.empty() &&
+ mPendingFrameTimelines.front().first < bufferItem.mFrameNumber) {
+ ATRACE_FORMAT_INSTANT("dropping stale frameNumber: %" PRIu64 " vsyncId: %" PRId64,
+ mPendingFrameTimelines.front().first,
+ mPendingFrameTimelines.front().second.vsyncId);
+ mPendingFrameTimelines.pop();
+ }
+
+ if (!mPendingFrameTimelines.empty() &&
+ mPendingFrameTimelines.front().first == bufferItem.mFrameNumber) {
+ ATRACE_FORMAT_INSTANT("Transaction::setFrameTimelineInfo frameNumber: %" PRIu64
+ " vsyncId: %" PRId64,
+ bufferItem.mFrameNumber,
+ mPendingFrameTimelines.front().second.vsyncId);
+ t->setFrameTimelineInfo(mPendingFrameTimelines.front().second);
+ mPendingFrameTimelines.pop();
}
{
@@ -626,6 +645,7 @@ void BLASTBufferQueue::acquireNextBufferLocked(
bufferItem.mTimestamp, bufferItem.mIsAutoTimestamp ? "(auto)" : "",
static_cast<uint32_t>(mPendingTransactions.size()), bufferItem.mGraphicBuffer->getId(),
bufferItem.mAutoRefresh ? " mAutoRefresh" : "", bufferItem.mTransform);
+ return OK;
}
Rect BLASTBufferQueue::computeCrop(const BufferItem& item) {
@@ -648,44 +668,19 @@ void BLASTBufferQueue::acquireAndReleaseBuffer() {
mBufferItemConsumer->releaseBuffer(bufferItem, bufferItem.mFence);
}
-void BLASTBufferQueue::flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock) {
- if (!mSyncedFrameNumbers.empty() && mNumFrameAvailable > 0) {
- // We are waiting on a previous sync's transaction callback so allow another sync
- // transaction to proceed.
- //
- // We need to first flush out the transactions that were in between the two syncs.
- // We do this by merging them into mSyncTransaction so any buffer merging will get
- // a release callback invoked. The release callback will be async so we need to wait
- // on max acquired to make sure we have the capacity to acquire another buffer.
- if (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting to flush shadow queue...");
- mCallbackCV.wait(lock);
- }
- while (mNumFrameAvailable > 0) {
- // flush out the shadow queue
- acquireAndReleaseBuffer();
- }
- }
-
- while (maxBuffersAcquired(false /* includeExtraAcquire */)) {
- BQA_LOGD("waiting for free buffer.");
- mCallbackCV.wait(lock);
- }
-}
-
void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
std::function<void(SurfaceComposerClient::Transaction*)> prevCallback = nullptr;
SurfaceComposerClient::Transaction* prevTransaction = nullptr;
- bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
{
- BBQ_TRACE();
std::unique_lock _lock{mMutex};
+ BBQ_TRACE();
+
+ bool waitForTransactionCallback = !mSyncedFrameNumbers.empty();
const bool syncTransactionSet = mTransactionReadyCallback != nullptr;
BQA_LOGV("onFrameAvailable-start syncTransactionSet=%s", boolToString(syncTransactionSet));
if (syncTransactionSet) {
- bool mayNeedToWaitForBuffer = true;
// If we are going to re-use the same mSyncTransaction, release the buffer that may
// already be set in the Transaction. This is to allow us a free slot early to continue
// processing a new buffer.
@@ -696,14 +691,29 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
bufferData->frameNumber);
releaseBuffer(bufferData->generateReleaseCallbackId(),
bufferData->acquireFence);
- // Because we just released a buffer, we know there's no need to wait for a free
- // buffer.
- mayNeedToWaitForBuffer = false;
}
}
- if (mayNeedToWaitForBuffer) {
- flushAndWaitForFreeBuffer(_lock);
+ if (waitForTransactionCallback) {
+ // We are waiting on a previous sync's transaction callback so allow another sync
+ // transaction to proceed.
+ //
+ // We need to first flush out the transactions that were in between the two syncs.
+ // We do this by merging them into mSyncTransaction so any buffer merging will get
+ // a release callback invoked.
+ while (mNumFrameAvailable > 0) {
+ // flush out the shadow queue
+ acquireAndReleaseBuffer();
+ }
+ } else {
+ // Make sure the frame available count is 0 before proceeding with a sync to ensure
+ // the correct frame is used for the sync. The only way mNumFrameAvailable would be
+ // greater than 0 is if we already ran out of buffers previously. This means we
+ // need to flush the buffers before proceeding with the sync.
+ while (mNumFrameAvailable > 0) {
+ BQA_LOGD("waiting until no queued buffers");
+ mCallbackCV.wait(_lock);
+ }
}
}
@@ -719,14 +729,23 @@ void BLASTBufferQueue::onFrameAvailable(const BufferItem& item) {
item.mFrameNumber, boolToString(syncTransactionSet));
if (syncTransactionSet) {
- acquireNextBufferLocked(mSyncTransaction);
+ // Add to mSyncedFrameNumbers before waiting in case any buffers are released
+ // while waiting for a free buffer. The release and commit callback will try to
+ // acquire buffers if there are any available, but we don't want it to acquire
+ // in the case where a sync transaction wants the buffer.
+ mSyncedFrameNumbers.emplace(item.mFrameNumber);
+ // If there's no available buffer and we're in a sync transaction, we need to wait
+ // instead of returning since we guarantee a buffer will be acquired for the sync.
+ while (acquireNextBufferLocked(mSyncTransaction) == BufferQueue::NO_BUFFER_AVAILABLE) {
+ BQA_LOGD("waiting for available buffer");
+ mCallbackCV.wait(_lock);
+ }
// Only need a commit callback when syncing to ensure the buffer that's synced has been
// sent to SF
incStrong((void*)transactionCommittedCallbackThunk);
mSyncTransaction->addTransactionCommittedCallback(transactionCommittedCallbackThunk,
static_cast<void*>(this));
- mSyncedFrameNumbers.emplace(item.mFrameNumber);
if (mAcquireSingleBuffer) {
prevCallback = mTransactionReadyCallback;
prevTransaction = mSyncTransaction;
@@ -829,15 +848,6 @@ bool BLASTBufferQueue::rejectBuffer(const BufferItem& item) {
return mSize != bufferSize;
}
-// Check if we have acquired the maximum number of buffers.
-// Consumer can acquire an additional buffer if that buffer is not droppable. Set
-// includeExtraAcquire is true to include this buffer to the count. Since this depends on the state
-// of the buffer, the next acquire may return with NO_BUFFER_AVAILABLE.
-bool BLASTBufferQueue::maxBuffersAcquired(bool includeExtraAcquire) const {
- int maxAcquiredBuffers = mMaxAcquiredBuffers + (includeExtraAcquire ? 2 : 1);
- return mNumAcquired >= maxAcquiredBuffers;
-}
-
class BBQSurface : public Surface {
private:
std::mutex mMutex;
@@ -874,12 +884,13 @@ public:
return mBbq->setFrameRate(frameRate, compatibility, changeFrameRateStrategy);
}
- status_t setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) override {
+ status_t setFrameTimelineInfo(uint64_t frameNumber,
+ const FrameTimelineInfo& frameTimelineInfo) override {
std::unique_lock _lock{mMutex};
if (mDestroyed) {
return DEAD_OBJECT;
}
- return mBbq->setFrameTimelineInfo(frameTimelineInfo);
+ return mBbq->setFrameTimelineInfo(frameNumber, frameTimelineInfo);
}
void destroy() override {
@@ -901,9 +912,12 @@ status_t BLASTBufferQueue::setFrameRate(float frameRate, int8_t compatibility,
return t.setFrameRate(mSurfaceControl, frameRate, compatibility, shouldBeSeamless).apply();
}
-status_t BLASTBufferQueue::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t BLASTBufferQueue::setFrameTimelineInfo(uint64_t frameNumber,
+ const FrameTimelineInfo& frameTimelineInfo) {
+ ATRACE_FORMAT("%s(%s) frameNumber: %" PRIu64 " vsyncId: %" PRId64, __func__, mName.c_str(),
+ frameNumber, frameTimelineInfo.vsyncId);
std::unique_lock _lock{mMutex};
- mNextFrameTimelineInfoQueue.push(frameTimelineInfo);
+ mPendingFrameTimelines.push({frameNumber, frameTimelineInfo});
return OK;
}
diff --git a/libs/gui/Surface.cpp b/libs/gui/Surface.cpp
index 100e36e7b3..16edfd4267 100644
--- a/libs/gui/Surface.cpp
+++ b/libs/gui/Surface.cpp
@@ -1869,12 +1869,13 @@ int Surface::dispatchGetLastQueuedBuffer2(va_list args) {
int Surface::dispatchSetFrameTimelineInfo(va_list args) {
ATRACE_CALL();
+ auto frameNumber = static_cast<uint64_t>(va_arg(args, uint64_t));
auto frameTimelineVsyncId = static_cast<int64_t>(va_arg(args, int64_t));
auto inputEventId = static_cast<int32_t>(va_arg(args, int32_t));
auto startTimeNanos = static_cast<int64_t>(va_arg(args, int64_t));
ALOGV("Surface::%s", __func__);
- return setFrameTimelineInfo({frameTimelineVsyncId, inputEventId, startTimeNanos});
+ return setFrameTimelineInfo(frameNumber, {frameTimelineVsyncId, inputEventId, startTimeNanos});
}
bool Surface::transformToDisplayInverse() const {
@@ -2648,7 +2649,8 @@ status_t Surface::setFrameRate(float frameRate, int8_t compatibility,
changeFrameRateStrategy);
}
-status_t Surface::setFrameTimelineInfo(const FrameTimelineInfo& frameTimelineInfo) {
+status_t Surface::setFrameTimelineInfo(uint64_t /*frameNumber*/,
+ const FrameTimelineInfo& frameTimelineInfo) {
return composerService()->setFrameTimelineInfo(mGraphicBufferProducer, frameTimelineInfo);
}
diff --git a/libs/gui/SurfaceComposerClient.cpp b/libs/gui/SurfaceComposerClient.cpp
index 0f5192d41c..05beb07891 100644
--- a/libs/gui/SurfaceComposerClient.cpp
+++ b/libs/gui/SurfaceComposerClient.cpp
@@ -1274,8 +1274,11 @@ SurfaceComposerClient::Transaction& SurfaceComposerClient::Transaction::setAlpha
mStatus = BAD_INDEX;
return *this;
}
+ if (alpha < 0.0f || alpha > 1.0f) {
+ ALOGE("SurfaceComposerClient::Transaction::setAlpha: invalid alpha %f, clamping", alpha);
+ }
s->what |= layer_state_t::eAlphaChanged;
- s->alpha = alpha;
+ s->alpha = std::clamp(alpha, 0.f, 1.f);
registerSurfaceControlForCallback(sc);
return *this;
diff --git a/libs/gui/include/gui/BLASTBufferQueue.h b/libs/gui/include/gui/BLASTBufferQueue.h
index 1278931202..40ffea6809 100644
--- a/libs/gui/include/gui/BLASTBufferQueue.h
+++ b/libs/gui/include/gui/BLASTBufferQueue.h
@@ -111,7 +111,7 @@ public:
void update(const sp<SurfaceControl>& surface, uint32_t width, uint32_t height, int32_t format);
status_t setFrameRate(float frameRate, int8_t compatibility, bool shouldBeSeamless);
- status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
void setSidebandStream(const sp<NativeHandle>& stream);
@@ -141,12 +141,11 @@ private:
void resizeFrameEventHistory(size_t newSize);
- void acquireNextBufferLocked(
+ status_t acquireNextBufferLocked(
const std::optional<SurfaceComposerClient::Transaction*> transaction) REQUIRES(mMutex);
Rect computeCrop(const BufferItem& item) REQUIRES(mMutex);
// Return true if we need to reject the buffer based on the scaling mode and the buffer size.
bool rejectBuffer(const BufferItem& item) REQUIRES(mMutex);
- bool maxBuffersAcquired(bool includeExtraAcquire) const REQUIRES(mMutex);
static PixelFormat convertBufferFormat(PixelFormat& format);
void mergePendingTransactions(SurfaceComposerClient::Transaction* t, uint64_t frameNumber)
REQUIRES(mMutex);
@@ -155,7 +154,6 @@ private:
void acquireAndReleaseBuffer() REQUIRES(mMutex);
void releaseBuffer(const ReleaseCallbackId& callbackId, const sp<Fence>& releaseFence)
REQUIRES(mMutex);
- void flushAndWaitForFreeBuffer(std::unique_lock<std::mutex>& lock);
std::string mName;
// Represents the queued buffer count from buffer queue,
@@ -244,7 +242,7 @@ private:
std::vector<std::tuple<uint64_t /* framenumber */, SurfaceComposerClient::Transaction>>
mPendingTransactions GUARDED_BY(mMutex);
- std::queue<FrameTimelineInfo> mNextFrameTimelineInfoQueue GUARDED_BY(mMutex);
+ std::queue<std::pair<uint64_t, FrameTimelineInfo>> mPendingFrameTimelines GUARDED_BY(mMutex);
// Tracks the last acquired frame number
uint64_t mLastAcquiredFrameNumber GUARDED_BY(mMutex) = 0;
diff --git a/libs/gui/include/gui/Surface.h b/libs/gui/include/gui/Surface.h
index 77615fe4c1..4a552b6643 100644
--- a/libs/gui/include/gui/Surface.h
+++ b/libs/gui/include/gui/Surface.h
@@ -211,7 +211,7 @@ public:
virtual status_t setFrameRate(float frameRate, int8_t compatibility,
int8_t changeFrameRateStrategy);
- virtual status_t setFrameTimelineInfo(const FrameTimelineInfo& info);
+ virtual status_t setFrameTimelineInfo(uint64_t frameNumber, const FrameTimelineInfo& info);
protected:
virtual ~Surface();
diff --git a/libs/gui/include/gui/TraceUtils.h b/libs/gui/include/gui/TraceUtils.h
index e5d268445c..00096158e7 100644
--- a/libs/gui/include/gui/TraceUtils.h
+++ b/libs/gui/include/gui/TraceUtils.h
@@ -27,6 +27,8 @@
#define ATRACE_FORMAT_BEGIN(fmt, ...) TraceUtils::atraceFormatBegin(fmt, ##__VA_ARGS__)
+#define ATRACE_FORMAT_INSTANT(fmt, ...) TraceUtils::intantFormat(fmt, ##__VA_ARGS__)
+
namespace android {
class TraceUtils {
@@ -50,6 +52,20 @@ public:
ATRACE_BEGIN(buf);
}
+ static void intantFormat(const char* fmt, ...) {
+ if (CC_LIKELY(!ATRACE_ENABLED())) return;
+
+ const int BUFFER_SIZE = 256;
+ va_list ap;
+ char buf[BUFFER_SIZE];
+
+ va_start(ap, fmt);
+ vsnprintf(buf, BUFFER_SIZE, fmt, ap);
+ va_end(ap);
+
+ ATRACE_INSTANT(buf);
+ }
+
}; // class TraceUtils
} /* namespace android */
diff --git a/libs/nativewindow/include/system/window.h b/libs/nativewindow/include/system/window.h
index a54af1fa62..86e76c4f2c 100644
--- a/libs/nativewindow/include/system/window.h
+++ b/libs/nativewindow/include/system/window.h
@@ -1043,11 +1043,12 @@ static inline int native_window_set_frame_rate(struct ANativeWindow* window, flo
}
static inline int native_window_set_frame_timeline_info(struct ANativeWindow* window,
+ uint64_t frameNumber,
int64_t frameTimelineVsyncId,
int32_t inputEventId,
int64_t startTimeNanos) {
- return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameTimelineVsyncId,
- inputEventId, startTimeNanos);
+ return window->perform(window, NATIVE_WINDOW_SET_FRAME_TIMELINE_INFO, frameNumber,
+ frameTimelineVsyncId, inputEventId, startTimeNanos);
}
// ------------------------------------------------------------------------------------------------
diff --git a/libs/sensor/ISensorServer.cpp b/libs/sensor/ISensorServer.cpp
index a6cacad374..93c95b98c5 100644
--- a/libs/sensor/ISensorServer.cpp
+++ b/libs/sensor/ISensorServer.cpp
@@ -66,7 +66,11 @@ public:
v.setCapacity(n);
while (n) {
n--;
- reply.read(s);
+ if(reply.read(s) != OK) {
+ ALOGE("Failed to read reply from getSensorList");
+ v.clear();
+ break;
+ }
v.add(s);
}
return v;
@@ -84,7 +88,11 @@ public:
v.setCapacity(n);
while (n) {
n--;
- reply.read(s);
+ if(reply.read(s) != OK) {
+ ALOGE("Failed to read reply from getDynamicSensorList");
+ v.clear();
+ break;
+ }
v.add(s);
}
return v;
diff --git a/libs/sensor/Sensor.cpp b/libs/sensor/Sensor.cpp
index ec0ced8663..b865c4d5d6 100644
--- a/libs/sensor/Sensor.cpp
+++ b/libs/sensor/Sensor.cpp
@@ -632,7 +632,13 @@ bool Sensor::unflattenString8(void const*& buffer, size_t& size, String8& output
return false;
}
outputString8.setTo(static_cast<char const*>(buffer), len);
+
+ if (size < FlattenableUtils::align<4>(len)) {
+ ALOGE("Malformed Sensor String8 field. Should be in a 4-byte aligned buffer but is not.");
+ return false;
+ }
FlattenableUtils::advance(buffer, size, FlattenableUtils::align<4>(len));
+
return true;
}
diff --git a/libs/sensor/SensorManager.cpp b/libs/sensor/SensorManager.cpp
index 0ba9704263..40061cde61 100644
--- a/libs/sensor/SensorManager.cpp
+++ b/libs/sensor/SensorManager.cpp
@@ -92,6 +92,16 @@ SensorManager& SensorManager::getInstanceForPackage(const String16& packageName)
return *sensorManager;
}
+void SensorManager::removeInstanceForPackage(const String16& packageName) {
+ Mutex::Autolock _l(sLock);
+ auto iterator = sPackageInstances.find(packageName);
+ if (iterator != sPackageInstances.end()) {
+ SensorManager* sensorManager = iterator->second;
+ delete sensorManager;
+ sPackageInstances.erase(iterator);
+ }
+}
+
SensorManager::SensorManager(const String16& opPackageName)
: mSensorList(nullptr), mOpPackageName(opPackageName), mDirectConnectionHandle(1) {
Mutex::Autolock _l(mLock);
@@ -166,6 +176,11 @@ status_t SensorManager::assertStateLocked() {
mSensors = mSensorServer->getSensorList(mOpPackageName);
size_t count = mSensors.size();
+ if (count == 0) {
+ ALOGE("Failed to get Sensor list");
+ mSensorServer.clear();
+ return UNKNOWN_ERROR;
+ }
mSensorList =
static_cast<Sensor const**>(malloc(count * sizeof(Sensor*)));
LOG_ALWAYS_FATAL_IF(mSensorList == nullptr, "mSensorList NULL");
diff --git a/libs/sensor/include/sensor/SensorManager.h b/libs/sensor/include/sensor/SensorManager.h
index 8d0a8a45d9..7c9d604ff7 100644
--- a/libs/sensor/include/sensor/SensorManager.h
+++ b/libs/sensor/include/sensor/SensorManager.h
@@ -54,6 +54,7 @@ class SensorManager : public ASensorManager
{
public:
static SensorManager& getInstanceForPackage(const String16& packageName);
+ static void removeInstanceForPackage(const String16& packageName);
~SensorManager();
ssize_t getSensorList(Sensor const* const** list);