1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15 #undef LOG_TAG
16 #define LOG_TAG "AudioSessionTimer"
17
18 #include "audio_session_timer.h"
19
20 #include "audio_log.h"
21 #include "audio_errors.h"
22
23 namespace OHOS {
24 namespace AudioStandard {
25 constexpr time_t SECONDS_OF_ONE_MINUTE = 60;
26
AudioSessionTimer()27 AudioSessionTimer::AudioSessionTimer()
28 {
29 state_ = TimerState::TIMER_NEW;
30 }
31
~AudioSessionTimer()32 AudioSessionTimer::~AudioSessionTimer()
33 {
34 if (timerThread_ != nullptr && timerThread_->joinable()) {
35 timerThread_->join();
36 timerThread_ = nullptr;
37 }
38 }
39
StartTimer(const int32_t callerPid)40 void AudioSessionTimer::StartTimer(const int32_t callerPid)
41 {
42 AUDIO_INFO_LOG("Audio session state change: StartTimer for pid %{public}d", callerPid);
43 std::unique_lock<std::mutex> lock(sessionTimerMutex_);
44 if (timerMap_.count(callerPid) != 0) {
45 AUDIO_INFO_LOG("StartTimer: timer of callerPid %{public}d is already running", callerPid);
46 // the time point will not be updated.
47 return;
48 }
49 std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
50 timerMap_[callerPid] = now;
51 if (!timerMap_.empty()) {
52 std::lock_guard<std::mutex> loopLock(timerLoopMutex_);
53 state_ = TimerState::TIMER_RUNNING;
54 }
55
56 if (!isThreadRunning_.load() && timerThread_ != nullptr && timerThread_->joinable()) {
57 // the old thread has been used and can be reset.
58 lock.unlock();
59 timerThread_->join();
60 timerThread_ = nullptr;
61 }
62 if (timerThread_ == nullptr) {
63 timerThread_ = std::make_shared<std::thread>([this] { TimerLoopFunc(); });
64 }
65 }
66
StopTimer(const int32_t callerPid)67 void AudioSessionTimer::StopTimer(const int32_t callerPid)
68 {
69 AUDIO_INFO_LOG("Audio session state change: StopTimer for pid %{public}d", callerPid);
70 std::unique_lock<std::mutex> lock(sessionTimerMutex_);
71 if (timerMap_.count(callerPid) == 0) {
72 AUDIO_WARNING_LOG("StopTimer: timer of callerPid %{public}d is already stopped", callerPid);
73 }
74 timerMap_.erase(callerPid);
75 if (timerMap_.empty()) {
76 {
77 std::lock_guard<std::mutex> loopLock(timerLoopMutex_);
78 state_ = TimerState::TIMER_STOPPED;
79 timerCond_.notify_all();
80 }
81 if (!isThreadRunning_.load() && timerThread_ != nullptr && timerThread_->joinable()) {
82 lock.unlock();
83 timerThread_->join();
84 timerThread_ = nullptr;
85 }
86 }
87 }
88
IsSessionTimerRunning(const int32_t callerPid)89 bool AudioSessionTimer::IsSessionTimerRunning(const int32_t callerPid)
90 {
91 std::lock_guard<std::mutex> lock(sessionTimerMutex_);
92 bool isRunning = (timerMap_.count(callerPid) > 0);
93 AUDIO_INFO_LOG("IsSessionTimerRunning: callerPid %{public}d, result %{public}d", callerPid, isRunning);
94 return isRunning;
95 }
96
TimerLoopFunc()97 void AudioSessionTimer::TimerLoopFunc()
98 {
99 AUDIO_INFO_LOG("Start the session timer loop");
100 isThreadRunning_.store(true);
101 for (;;) {
102 std::unique_lock<std::mutex> lock(sessionTimerMutex_);
103 if (timerMap_.empty()) {
104 AUDIO_INFO_LOG("The audio session timer map is empty. Exit.");
105 break;
106 }
107
108 std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
109 auto iter = timerMap_.begin();
110 while (iter != timerMap_.end()) {
111 if (now - iter->second >= SECONDS_OF_ONE_MINUTE) {
112 SendSessionTimeOutCallback(iter->first);
113 iter = timerMap_.erase(iter);
114 } else {
115 ++iter;
116 }
117 }
118 lock.unlock();
119
120 // Sleep for one second. Then enter the next cycle.
121 std::unique_lock<std::mutex> loopLock(timerLoopMutex_);
122 bool waitResult = timerCond_.wait_for(loopLock, std::chrono::seconds(1),
123 [this]() { return (state_ == TimerState::TIMER_STOPPED); });
124 if (!waitResult) {
125 AUDIO_DEBUG_LOG("sleep 1s. continue.");
126 }
127 if (state_ == TimerState::TIMER_STOPPED) {
128 AUDIO_INFO_LOG("The audio session timer has been stopped!");
129 break;
130 }
131 }
132 isThreadRunning_.store(false);
133 }
134
SendSessionTimeOutCallback(const int32_t callerPid)135 void AudioSessionTimer::SendSessionTimeOutCallback(const int32_t callerPid)
136 {
137 std::shared_ptr<AudioSessionTimerCallback> cb = timerCallback_.lock();
138 if (cb == nullptr) {
139 AUDIO_ERR_LOG("The audio session timer callback is nullptr!");
140 return;
141 }
142 cb->OnAudioSessionTimeOut(callerPid);
143 }
144
SetAudioSessionTimerCallback(const std::shared_ptr<AudioSessionTimerCallback> sessionTimerCallback)145 int32_t AudioSessionTimer::SetAudioSessionTimerCallback(
146 const std::shared_ptr<AudioSessionTimerCallback> sessionTimerCallback)
147 {
148 AUDIO_INFO_LOG("SetAudioSessionTimerCallback in");
149 if (sessionTimerCallback == nullptr) {
150 AUDIO_ERR_LOG("sessionTimerCallback is nullptr!");
151 return ERR_INVALID_PARAM;
152 }
153
154 std::lock_guard<std::mutex> lock(sessionTimerMutex_);
155 timerCallback_ = sessionTimerCallback;
156 return SUCCESS;
157 }
158 } // namespace AudioStandard
159 } // namespace OHOS
160