1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 //#define LOG_NDEBUG 0
18 #define LOG_TAG "TranscodingResourcePolicy"
19
20 #include <aidl/android/media/BnResourceObserver.h>
21 #include <aidl/android/media/IResourceObserverService.h>
22 #include <android/binder_manager.h>
23 #include <android/binder_process.h>
24 #include <map>
25 #include <media/TranscodingResourcePolicy.h>
26 #include <utils/Log.h>
27
28 namespace android {
29
30 using Status = ::ndk::ScopedAStatus;
31 using ::aidl::android::media::BnResourceObserver;
32 using ::aidl::android::media::IResourceObserverService;
33 using ::aidl::android::media::MediaObservableEvent;
34 using ::aidl::android::media::MediaObservableFilter;
35 using ::aidl::android::media::MediaObservableParcel;
36 using ::aidl::android::media::MediaObservableType;
37
toString(const MediaObservableParcel & observable)38 static std::string toString(const MediaObservableParcel& observable) {
39 return "{" + ::aidl::android::media::toString(observable.type) + ", " +
40 std::to_string(observable.value) + "}";
41 }
42
43 struct TranscodingResourcePolicy::ResourceObserver : public BnResourceObserver {
ResourceObserverandroid::TranscodingResourcePolicy::ResourceObserver44 explicit ResourceObserver(TranscodingResourcePolicy* owner) : mOwner(owner) {}
45
46 // IResourceObserver
onStatusChangedandroid::TranscodingResourcePolicy::ResourceObserver47 ::ndk::ScopedAStatus onStatusChanged(
48 MediaObservableEvent event, int32_t uid, int32_t pid,
49 const std::vector<MediaObservableParcel>& observables) override {
50 ALOGD("%s: %s, uid %d, pid %d, %s", __FUNCTION__,
51 ::aidl::android::media::toString(event).c_str(), uid, pid,
52 toString(observables[0]).c_str());
53
54 // Only report kIdle event.
55 if (((uint64_t)event & (uint64_t)MediaObservableEvent::kIdle) != 0) {
56 for (auto& observable : observables) {
57 if (observable.type == MediaObservableType::kVideoSecureCodec ||
58 observable.type == MediaObservableType::kVideoNonSecureCodec) {
59 mOwner->onResourceAvailable(pid);
60 break;
61 }
62 }
63 }
64 return ::ndk::ScopedAStatus::ok();
65 }
66
67 TranscodingResourcePolicy* mOwner;
68 };
69
70 // cookie used for death recipients. The TranscodingResourcePolicy
71 // that this cookie is associated with must outlive this cookie. It is
72 // either deleted by binderDied, or in unregisterSelf which is also called
73 // in the destructor of TranscodingResourcePolicy
74 class TranscodingResourcePolicyCookie {
75 public:
TranscodingResourcePolicyCookie(TranscodingResourcePolicy * policy)76 TranscodingResourcePolicyCookie(TranscodingResourcePolicy* policy) : mPolicy(policy) {}
77 TranscodingResourcePolicyCookie() = delete;
78 TranscodingResourcePolicy* mPolicy;
79 };
80
81 static std::map<uintptr_t, std::unique_ptr<TranscodingResourcePolicyCookie>> sCookies;
82 static uintptr_t sCookieKeyCounter;
83 static std::mutex sCookiesMutex;
84
85 // static
BinderDiedCallback(void * cookie)86 void TranscodingResourcePolicy::BinderDiedCallback(void* cookie) {
87 std::lock_guard<std::mutex> guard(sCookiesMutex);
88 if (auto it = sCookies.find(reinterpret_cast<uintptr_t>(cookie)); it != sCookies.end()) {
89 ALOGI("BinderDiedCallback unregistering TranscodingResourcePolicy");
90 auto policy = reinterpret_cast<TranscodingResourcePolicy*>(it->second->mPolicy);
91 if (policy) {
92 policy->unregisterSelf();
93 }
94 sCookies.erase(it);
95 }
96 // TODO(chz): retry to connecting to IResourceObserverService after failure.
97 // Also need to have back-up logic if IResourceObserverService is offline for
98 // Prolonged period of time. A possible alternative could be, during period where
99 // IResourceObserverService is not available, trigger onResourceAvailable() everytime
100 // when top uid changes (in hope that'll free up some codec instances that we could
101 // reclaim).
102 }
103
TranscodingResourcePolicy()104 TranscodingResourcePolicy::TranscodingResourcePolicy()
105 : mRegistered(false),
106 mResourceLostPid(-1),
107 mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
108 registerSelf();
109 }
110
~TranscodingResourcePolicy()111 TranscodingResourcePolicy::~TranscodingResourcePolicy() {
112 {
113 std::lock_guard<std::mutex> guard(sCookiesMutex);
114
115 // delete all of the cookies associated with this TranscodingResourcePolicy
116 // instance since they are holding pointers to this object that will no
117 // longer be valid.
118 std::erase_if(sCookies, [this](const auto& cookieEntry) {
119 auto const& [key, cookie] = cookieEntry;
120 std::lock_guard guard(mCookieKeysLock);
121 if (const auto& it = mCookieKeys.find(key); it != mCookieKeys.end()) {
122 // No longer need to track this cookie
123 mCookieKeys.erase(key);
124 return true;
125 }
126 return false;
127 });
128 }
129 unregisterSelf();
130 }
131
registerSelf()132 void TranscodingResourcePolicy::registerSelf() {
133 ALOGI("TranscodingResourcePolicy: registerSelf");
134
135 ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_observer"));
136
137 std::scoped_lock lock{mRegisteredLock};
138
139 if (mRegistered) {
140 return;
141 }
142
143 // TODO(chz): retry to connecting to IResourceObserverService after failure.
144 mService = IResourceObserverService::fromBinder(binder);
145 if (mService == nullptr) {
146 ALOGE("Failed to get IResourceObserverService");
147 return;
148 }
149
150 // Only register filters for codec resource available.
151 mObserver = ::ndk::SharedRefBase::make<ResourceObserver>(this);
152 std::vector<MediaObservableFilter> filters = {
153 {MediaObservableType::kVideoSecureCodec, MediaObservableEvent::kIdle},
154 {MediaObservableType::kVideoNonSecureCodec, MediaObservableEvent::kIdle}};
155
156 Status status = mService->registerObserver(mObserver, filters);
157 if (!status.isOk()) {
158 ALOGE("failed to register: error %d", status.getServiceSpecificError());
159 mService = nullptr;
160 mObserver = nullptr;
161 return;
162 }
163
164 std::unique_ptr<TranscodingResourcePolicyCookie> cookie =
165 std::make_unique<TranscodingResourcePolicyCookie>(this);
166 void* cookiePtr = static_cast<void*>(cookie.get());
167 uintptr_t cookieKey = sCookieKeyCounter++;
168 sCookies.emplace(cookieKey, std::move(cookie));
169 {
170 std::lock_guard guard(mCookieKeysLock);
171 mCookieKeys.insert(cookieKey);
172 }
173
174 AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(cookieKey));
175
176 ALOGD("@@@ registered observer");
177 mRegistered = true;
178 }
179
unregisterSelf()180 void TranscodingResourcePolicy::unregisterSelf() {
181 ALOGI("TranscodingResourcePolicy: unregisterSelf");
182
183 std::scoped_lock lock{mRegisteredLock};
184
185 if (!mRegistered) {
186 return;
187 }
188
189 ::ndk::SpAIBinder binder = mService->asBinder();
190 if (binder.get() != nullptr) {
191 Status status = mService->unregisterObserver(mObserver);
192 }
193
194 mService = nullptr;
195 mObserver = nullptr;
196 mRegistered = false;
197 }
198
setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface> & cb)199 void TranscodingResourcePolicy::setCallback(
200 const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) {
201 std::scoped_lock lock{mCallbackLock};
202 mResourcePolicyCallback = cb;
203 }
204
setPidResourceLost(pid_t pid)205 void TranscodingResourcePolicy::setPidResourceLost(pid_t pid) {
206 std::scoped_lock lock{mCallbackLock};
207 mResourceLostPid = pid;
208 }
209
onResourceAvailable(pid_t pid)210 void TranscodingResourcePolicy::onResourceAvailable(pid_t pid) {
211 std::shared_ptr<ResourcePolicyCallbackInterface> cb;
212 {
213 std::scoped_lock lock{mCallbackLock};
214 // Only callback if codec resource is released from other processes.
215 if (mResourceLostPid != -1 && mResourceLostPid != pid) {
216 cb = mResourcePolicyCallback.lock();
217 mResourceLostPid = -1;
218 }
219 }
220
221 if (cb != nullptr) {
222 cb->onResourceAvailable();
223 }
224 }
225 } // namespace android
226