| /* |
| * Copyright (C) 2013 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 "Camera3-Status" |
| #define ATRACE_TAG ATRACE_TAG_CAMERA |
| //#define LOG_NDEBUG 0 |
| |
| #include <utils/Log.h> |
| #include <utils/Trace.h> |
| #include <ui/Fence.h> |
| |
| #include "device3/StatusTracker.h" |
| #include "device3/Camera3Device.h" |
| |
| namespace android { |
| |
| namespace camera3 { |
| |
| StatusTracker::StatusTracker(wp<Camera3Device> parent) : |
| mComponentsChanged(false), |
| mParent(parent), |
| mNextComponentId(0), |
| mIdleFence(new Fence()), |
| mDeviceState(IDLE), |
| mFlushed(true) { |
| } |
| |
| StatusTracker::~StatusTracker() { |
| } |
| |
| int StatusTracker::addComponent(std::string componentName) { |
| int id; |
| ssize_t err; |
| { |
| Mutex::Autolock l(mLock); |
| id = mNextComponentId++; |
| ALOGV("%s: Adding new component %d", __FUNCTION__, id); |
| |
| err = mStates.add(id, IDLE); |
| if (componentName.empty()) { |
| componentName = std::to_string(id); |
| } |
| mComponentNames.add(id, componentName); |
| ALOGE_IF(err < 0, "%s: Can't add new component %d (%s): %s (%zd)", |
| __FUNCTION__, id, componentName.c_str(), strerror(-err), err); |
| } |
| |
| if (err >= 0) { |
| Mutex::Autolock pl(mPendingLock); |
| mComponentsChanged = true; |
| mPendingChangeSignal.signal(); |
| } |
| |
| return err < 0 ? err : id; |
| } |
| |
| void StatusTracker::removeComponent(int id) { |
| ssize_t idx; |
| { |
| Mutex::Autolock l(mLock); |
| ALOGV("%s: Removing component %d", __FUNCTION__, id); |
| idx = mStates.removeItem(id); |
| mComponentNames.removeItem(id); |
| } |
| |
| if (idx >= 0) { |
| Mutex::Autolock pl(mPendingLock); |
| mComponentsChanged = true; |
| mPendingChangeSignal.signal(); |
| } |
| |
| return; |
| } |
| |
| |
| void StatusTracker::dumpActiveComponents() { |
| Mutex::Autolock l(mLock); |
| if (mDeviceState == IDLE) { |
| ALOGI("%s: all components are IDLE", __FUNCTION__); |
| return; |
| } |
| for (size_t i = 0; i < mStates.size(); i++) { |
| if (mStates.valueAt(i) == ACTIVE) { |
| ALOGI("%s: component %d (%s) is active", __FUNCTION__, mStates.keyAt(i), |
| mComponentNames.valueAt(i).c_str()); |
| } |
| } |
| } |
| |
| void StatusTracker::markComponentIdle(int id, const sp<Fence>& componentFence) { |
| markComponent(id, IDLE, componentFence); |
| } |
| |
| void StatusTracker::markComponentActive(int id) { |
| markComponent(id, ACTIVE, Fence::NO_FENCE); |
| } |
| |
| void StatusTracker::markComponent(int id, ComponentState state, |
| const sp<Fence>& componentFence) { |
| ALOGV("%s: Component %d is now %s", __FUNCTION__, id, |
| state == IDLE ? "idle" : "active"); |
| |
| // If any component state changes, the status tracker is considered |
| // not flushed. |
| { |
| Mutex::Autolock l(mFlushLock); |
| mFlushed = false; |
| } |
| |
| { |
| Mutex::Autolock l(mPendingLock); |
| |
| StateChange newState = { |
| id, |
| state, |
| componentFence |
| }; |
| |
| mPendingChangeQueue.add(newState); |
| mPendingChangeSignal.signal(); |
| } |
| } |
| |
| void StatusTracker::flushPendingStates() { |
| Mutex::Autolock l(mFlushLock); |
| while (!mFlushed && isRunning()) { |
| mFlushCondition.waitRelative(mFlushLock, kWaitDuration); |
| } |
| } |
| |
| void StatusTracker::requestExit() { |
| // First mark thread dead |
| Thread::requestExit(); |
| // Then exit any waits |
| mPendingChangeSignal.signal(); |
| mFlushCondition.signal(); |
| } |
| |
| StatusTracker::ComponentState StatusTracker::getDeviceStateLocked() { |
| for (size_t i = 0; i < mStates.size(); i++) { |
| if (mStates.valueAt(i) == ACTIVE) { |
| ALOGV("%s: Component %d not idle", __FUNCTION__, |
| mStates.keyAt(i)); |
| return ACTIVE; |
| } |
| } |
| // - If not yet signaled, getSignalTime returns INT64_MAX |
| // - If invalid fence or error, returns -1 |
| // - Otherwise returns time of signalling. |
| // Treat -1 as 'signalled', since HAL may not be using fences, and want |
| // to be able to idle in case of errors. |
| nsecs_t signalTime = mIdleFence->getSignalTime(); |
| bool fencesDone = signalTime != INT64_MAX; |
| |
| ALOGV_IF(!fencesDone, "%s: Fences still to wait on", __FUNCTION__); |
| |
| return fencesDone ? IDLE : ACTIVE; |
| } |
| |
| bool StatusTracker::threadLoop() { |
| status_t res; |
| |
| // Wait for state updates |
| { |
| Mutex::Autolock pl(mPendingLock); |
| while (mPendingChangeQueue.size() == 0 && !mComponentsChanged) { |
| res = mPendingChangeSignal.waitRelative(mPendingLock, |
| kWaitDuration); |
| if (exitPending()) return false; |
| if (res != OK) { |
| if (res != TIMED_OUT) { |
| ALOGE("%s: Error waiting on state changes: %s (%d)", |
| __FUNCTION__, strerror(-res), res); |
| } |
| // TIMED_OUT is expected |
| break; |
| } |
| } |
| } |
| |
| bool waitForIdleFence = false; |
| // After new pending states appear, or timeout, check if we're idle. Even |
| // with timeout, need to check to account for fences that may still be |
| // clearing out |
| sp<Camera3Device> parent; |
| { |
| Mutex::Autolock pl(mPendingLock); |
| Mutex::Autolock l(mLock); |
| |
| // Collect all pending state updates and see if the device |
| // collectively transitions between idle and active for each one |
| |
| // First pass for changed components or fence completions |
| ComponentState prevState = getDeviceStateLocked(); |
| if (prevState != mDeviceState) { |
| // Only collect changes to overall device state |
| mStateTransitions.add(prevState); |
| } |
| // For each pending component state update, check if we've transitioned |
| // to a new overall device state |
| for (size_t i = 0; i < mPendingChangeQueue.size(); i++) { |
| const StateChange &newState = mPendingChangeQueue[i]; |
| ssize_t idx = mStates.indexOfKey(newState.id); |
| // Ignore notices for unknown components |
| if (idx >= 0) { |
| bool validFence = newState.fence != Fence::NO_FENCE; |
| // Update single component state |
| mStates.replaceValueAt(idx, newState.state); |
| mIdleFence = Fence::merge(String8("idleFence"), |
| mIdleFence, newState.fence); |
| // .. and see if overall device state has changed |
| ComponentState newState = getDeviceStateLocked(); |
| if (newState != prevState) { |
| mStateTransitions.add(newState); |
| } else if (validFence && !waitForIdleFence) { |
| waitForIdleFence = true; |
| } |
| prevState = newState; |
| } |
| } |
| mPendingChangeQueue.clear(); |
| mComponentsChanged = false; |
| |
| // Store final state after all pending state changes are done with |
| |
| mDeviceState = prevState; |
| parent = mParent.promote(); |
| } |
| |
| // Notify parent for all intermediate transitions |
| if (mStateTransitions.size() > 0 && parent.get()) { |
| for (size_t i = 0; i < mStateTransitions.size(); i++) { |
| bool idle = (mStateTransitions[i] == IDLE); |
| ALOGV("Camera device is now %s", idle ? "idle" : "active"); |
| parent->notifyStatus(idle); |
| } |
| } |
| mStateTransitions.clear(); |
| |
| // After all pending changes are cleared and notified, mark the tracker |
| // as flushed. |
| { |
| Mutex::Autolock fl(mFlushLock); |
| Mutex::Autolock pl(mPendingLock); |
| if (mPendingChangeQueue.size() == 0) { |
| mFlushed = true; |
| mFlushCondition.signal(); |
| } |
| } |
| |
| if (waitForIdleFence) { |
| auto ret = mIdleFence->wait(kWaitDuration); |
| if (ret == NO_ERROR) { |
| mComponentsChanged = true; |
| } |
| } |
| |
| return true; |
| } |
| |
| } // namespace android |
| |
| } // namespace camera3 |