• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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