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