Merge "Link resourceobserver_aidl statically in tests"
diff --git a/drm/libmediadrm/DrmMetricsConsumer.cpp b/drm/libmediadrm/DrmMetricsConsumer.cpp
index b47b4ff..5f0b26e 100644
--- a/drm/libmediadrm/DrmMetricsConsumer.cpp
+++ b/drm/libmediadrm/DrmMetricsConsumer.cpp
@@ -37,8 +37,8 @@
template <> std::string GetAttributeName<KeyStatusType>(KeyStatusType type) {
static const char *type_names[] = {"USABLE", "EXPIRED",
"OUTPUT_NOT_ALLOWED", "STATUS_PENDING",
- "INTERNAL_ERROR"};
- if (((size_t)type) > arraysize(type_names)) {
+ "INTERNAL_ERROR", "USABLE_IN_FUTURE"};
+ if (((size_t)type) >= arraysize(type_names)) {
return "UNKNOWN_TYPE";
}
return type_names[(size_t)type];
@@ -48,7 +48,7 @@
static const char *type_names[] = {"PROVISION_REQUIRED", "KEY_NEEDED",
"KEY_EXPIRED", "VENDOR_DEFINED",
"SESSION_RECLAIMED"};
- if (((size_t)type) > arraysize(type_names)) {
+ if (((size_t)type) >= arraysize(type_names)) {
return "UNKNOWN_TYPE";
}
return type_names[(size_t)type];
diff --git a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
index f114046..c81a659 100644
--- a/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
+++ b/media/libmediaplayerservice/tests/DrmSessionManager_test.cpp
@@ -65,6 +65,14 @@
return true;
}
+ virtual bool overrideProcessInfo(
+ int /* pid */, int /* procState */, int /* oomScore */) {
+ return true;
+ }
+
+ virtual void removeProcessInfoOverride(int /* pid */) {
+ }
+
private:
DISALLOW_EVIL_CONSTRUCTORS(FakeProcessInfo);
};
diff --git a/media/libmediatranscoding/Android.bp b/media/libmediatranscoding/Android.bp
index b7bad7f..128d0d8 100644
--- a/media/libmediatranscoding/Android.bp
+++ b/media/libmediatranscoding/Android.bp
@@ -54,6 +54,7 @@
srcs: [
"TranscodingClientManager.cpp",
"TranscodingJobScheduler.cpp",
+ "TranscodingResourcePolicy.cpp",
"TranscodingUidPolicy.cpp",
"TranscoderWrapper.cpp",
],
@@ -67,11 +68,16 @@
"libbinder",
"libmediandk",
],
+ export_shared_lib_headers: [
+ "libmediandk",
+ ],
export_include_dirs: ["include"],
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
+ "resourcemanager_aidl_interface-ndk_platform",
+ "resourceobserver_aidl_interface-ndk_platform",
],
cflags: [
diff --git a/media/libmediatranscoding/TranscoderWrapper.cpp b/media/libmediatranscoding/TranscoderWrapper.cpp
index bd03671..8062fcf 100644
--- a/media/libmediatranscoding/TranscoderWrapper.cpp
+++ b/media/libmediatranscoding/TranscoderWrapper.cpp
@@ -89,26 +89,40 @@
}
//static
-const char* TranscoderWrapper::toString(Event::Type type) {
- switch (type) {
+std::string TranscoderWrapper::toString(const Event& event) {
+ std::string typeStr;
+ switch (event.type) {
case Event::Start:
- return "Start";
- case Event::Pause:
- return "Pause";
- case Event::Resume:
- return "Resume";
- case Event::Stop:
- return "Stop";
- case Event::Finish:
- return "Finish";
- case Event::Error:
- return "Error";
- case Event::Progress:
- return "Progress";
- default:
+ typeStr = "Start";
break;
+ case Event::Pause:
+ typeStr = "Pause";
+ break;
+ case Event::Resume:
+ typeStr = "Resume";
+ break;
+ case Event::Stop:
+ typeStr = "Stop";
+ break;
+ case Event::Finish:
+ typeStr = "Finish";
+ break;
+ case Event::Error:
+ typeStr = "Error";
+ break;
+ case Event::Progress:
+ typeStr = "Progress";
+ break;
+ default:
+ return "(unknown)";
}
- return "(unknown)";
+ std::string result;
+ result = "job {" + std::to_string(event.clientId) + "," + std::to_string(event.jobId) +
+ "}: " + typeStr;
+ if (event.type == Event::Error || event.type == Event::Progress) {
+ result += " " + std::to_string(event.arg);
+ }
+ return result;
}
class TranscoderWrapper::CallbackImpl : public MediaTranscoder::CallbackInterface {
@@ -128,7 +142,7 @@
media_status_t error) override {
auto owner = mOwner.lock();
if (owner != nullptr) {
- owner->onError(mClientId, mJobId, toTranscodingError(error));
+ owner->onError(mClientId, mJobId, error);
}
}
@@ -160,20 +174,41 @@
mCallback = cb;
}
+static bool isResourceError(media_status_t err) {
+ return err == AMEDIACODEC_ERROR_RECLAIMED || err == AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE;
+}
+
+void TranscoderWrapper::reportError(ClientIdType clientId, JobIdType jobId, media_status_t err) {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ if (isResourceError(err)) {
+ // Add a placeholder pause state to mPausedStateMap. This is required when resuming.
+ // TODO: remove this when transcoder pause/resume logic is ready. New logic will
+ // no longer use the pause states.
+ auto it = mPausedStateMap.find(JobKeyType(clientId, jobId));
+ if (it == mPausedStateMap.end()) {
+ mPausedStateMap.emplace(JobKeyType(clientId, jobId),
+ std::shared_ptr<const Parcel>());
+ }
+
+ callback->onResourceLost();
+ } else {
+ callback->onError(clientId, jobId, toTranscodingError(err));
+ }
+ }
+}
+
void TranscoderWrapper::start(ClientIdType clientId, JobIdType jobId,
const TranscodingRequestParcel& request,
const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
queueEvent(Event::Start, clientId, jobId, [=] {
- TranscodingErrorCode err = handleStart(clientId, jobId, request, clientCb);
+ media_status_t err = handleStart(clientId, jobId, request, clientCb);
- auto callback = mCallback.lock();
- if (err != TranscodingErrorCode::kNoError) {
+ if (err != AMEDIA_OK) {
cleanup();
-
- if (callback != nullptr) {
- callback->onError(clientId, jobId, err);
- }
+ reportError(clientId, jobId, err);
} else {
+ auto callback = mCallback.lock();
if (callback != nullptr) {
callback->onStarted(clientId, jobId);
}
@@ -183,15 +218,15 @@
void TranscoderWrapper::pause(ClientIdType clientId, JobIdType jobId) {
queueEvent(Event::Pause, clientId, jobId, [=] {
- TranscodingErrorCode err = handlePause(clientId, jobId);
+ media_status_t err = handlePause(clientId, jobId);
cleanup();
- auto callback = mCallback.lock();
- if (callback != nullptr) {
- if (err != TranscodingErrorCode::kNoError) {
- callback->onError(clientId, jobId, err);
- } else {
+ if (err != AMEDIA_OK) {
+ reportError(clientId, jobId, err);
+ } else {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
callback->onPaused(clientId, jobId);
}
}
@@ -202,16 +237,13 @@
const TranscodingRequestParcel& request,
const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
queueEvent(Event::Resume, clientId, jobId, [=] {
- TranscodingErrorCode err = handleResume(clientId, jobId, request, clientCb);
+ media_status_t err = handleResume(clientId, jobId, request, clientCb);
- auto callback = mCallback.lock();
- if (err != TranscodingErrorCode::kNoError) {
+ if (err != AMEDIA_OK) {
cleanup();
-
- if (callback != nullptr) {
- callback->onError(clientId, jobId, err);
- }
+ reportError(clientId, jobId, err);
} else {
+ auto callback = mCallback.lock();
if (callback != nullptr) {
callback->onResumed(clientId, jobId);
}
@@ -225,7 +257,7 @@
// Cancelling the currently running job.
media_status_t err = mTranscoder->cancel();
if (err != AMEDIA_OK) {
- ALOGE("failed to stop transcoder: %d", err);
+ ALOGW("failed to stop transcoder: %d", err);
} else {
ALOGI("transcoder stopped");
}
@@ -251,41 +283,43 @@
});
}
-void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId,
- TranscodingErrorCode error) {
- queueEvent(Event::Error, clientId, jobId, [=] {
- if (mTranscoder != nullptr && clientId == mCurrentClientId && jobId == mCurrentJobId) {
- cleanup();
- }
-
- auto callback = mCallback.lock();
- if (callback != nullptr) {
- callback->onError(clientId, jobId, error);
- }
- });
+void TranscoderWrapper::onError(ClientIdType clientId, JobIdType jobId, media_status_t error) {
+ queueEvent(
+ Event::Error, clientId, jobId,
+ [=] {
+ if (mTranscoder != nullptr && clientId == mCurrentClientId &&
+ jobId == mCurrentJobId) {
+ cleanup();
+ }
+ reportError(clientId, jobId, error);
+ },
+ error);
}
void TranscoderWrapper::onProgress(ClientIdType clientId, JobIdType jobId, int32_t progress) {
- queueEvent(Event::Progress, clientId, jobId, [=] {
- auto callback = mCallback.lock();
- if (callback != nullptr) {
- callback->onProgressUpdate(clientId, jobId, progress);
- }
- });
+ queueEvent(
+ Event::Progress, clientId, jobId,
+ [=] {
+ auto callback = mCallback.lock();
+ if (callback != nullptr) {
+ callback->onProgressUpdate(clientId, jobId, progress);
+ }
+ },
+ progress);
}
-TranscodingErrorCode TranscoderWrapper::setupTranscoder(
+media_status_t TranscoderWrapper::setupTranscoder(
ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
const std::shared_ptr<ITranscodingClientCallback>& clientCb,
const std::shared_ptr<const Parcel>& pausedState) {
if (clientCb == nullptr) {
ALOGE("client callback is null");
- return TranscodingErrorCode::kInvalidParameter;
+ return AMEDIA_ERROR_INVALID_PARAMETER;
}
if (mTranscoder != nullptr) {
ALOGE("transcoder already running");
- return TranscodingErrorCode::kInvalidOperation;
+ return AMEDIA_ERROR_INVALID_OPERATION;
}
Status status;
@@ -293,7 +327,7 @@
status = clientCb->openFileDescriptor(request.sourceFilePath, "r", &srcFd);
if (!status.isOk() || srcFd.get() < 0) {
ALOGE("failed to open source");
- return TranscodingErrorCode::kErrorIO;
+ return AMEDIA_ERROR_IO;
}
// Open dest file with "rw", as the transcoder could potentially reuse part of it
@@ -302,7 +336,7 @@
status = clientCb->openFileDescriptor(request.destinationFilePath, "rw", &dstFd);
if (!status.isOk() || dstFd.get() < 0) {
ALOGE("failed to open destination");
- return TranscodingErrorCode::kErrorIO;
+ return AMEDIA_ERROR_IO;
}
mCurrentClientId = clientId;
@@ -311,19 +345,19 @@
mTranscoder = MediaTranscoder::create(mTranscoderCb, pausedState);
if (mTranscoder == nullptr) {
ALOGE("failed to create transcoder");
- return TranscodingErrorCode::kUnknown;
+ return AMEDIA_ERROR_UNKNOWN;
}
media_status_t err = mTranscoder->configureSource(srcFd.get());
if (err != AMEDIA_OK) {
ALOGE("failed to configure source: %d", err);
- return toTranscodingError(err);
+ return err;
}
std::vector<std::shared_ptr<AMediaFormat>> trackFormats = mTranscoder->getTrackFormats();
if (trackFormats.size() == 0) {
ALOGE("failed to get track formats!");
- return TranscodingErrorCode::kMalformed;
+ return AMEDIA_ERROR_MALFORMED;
}
for (int i = 0; i < trackFormats.size(); ++i) {
@@ -341,43 +375,43 @@
}
if (err != AMEDIA_OK) {
ALOGE("failed to configure track format for track %d: %d", i, err);
- return toTranscodingError(err);
+ return err;
}
}
err = mTranscoder->configureDestination(dstFd.get());
if (err != AMEDIA_OK) {
ALOGE("failed to configure dest: %d", err);
- return toTranscodingError(err);
+ return err;
}
- return TranscodingErrorCode::kNoError;
+ return AMEDIA_OK;
}
-TranscodingErrorCode TranscoderWrapper::handleStart(
+media_status_t TranscoderWrapper::handleStart(
ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
- ALOGI("setting up transcoder for start");
- TranscodingErrorCode err = setupTranscoder(clientId, jobId, request, clientCb);
- if (err != TranscodingErrorCode::kNoError) {
+ ALOGI("%s: setting up transcoder for start", __FUNCTION__);
+ media_status_t err = setupTranscoder(clientId, jobId, request, clientCb);
+ if (err != AMEDIA_OK) {
ALOGI("%s: failed to setup transcoder", __FUNCTION__);
return err;
}
- media_status_t status = mTranscoder->start();
- if (status != AMEDIA_OK) {
+ err = mTranscoder->start();
+ if (err != AMEDIA_OK) {
ALOGE("%s: failed to start transcoder: %d", __FUNCTION__, err);
- return toTranscodingError(status);
+ return err;
}
ALOGI("%s: transcoder started", __FUNCTION__);
- return TranscodingErrorCode::kNoError;
+ return AMEDIA_OK;
}
-TranscodingErrorCode TranscoderWrapper::handlePause(ClientIdType clientId, JobIdType jobId) {
+media_status_t TranscoderWrapper::handlePause(ClientIdType clientId, JobIdType jobId) {
if (mTranscoder == nullptr) {
ALOGE("%s: transcoder is not running", __FUNCTION__);
- return TranscodingErrorCode::kInvalidOperation;
+ return AMEDIA_ERROR_INVALID_OPERATION;
}
if (clientId != mCurrentClientId || jobId != mCurrentJobId) {
@@ -385,19 +419,21 @@
(long long)clientId, jobId, (long long)mCurrentClientId, mCurrentJobId);
}
+ ALOGI("%s: pausing transcoder", __FUNCTION__);
+
std::shared_ptr<const Parcel> pauseStates;
media_status_t err = mTranscoder->pause(&pauseStates);
if (err != AMEDIA_OK) {
ALOGE("%s: failed to pause transcoder: %d", __FUNCTION__, err);
- return toTranscodingError(err);
+ return err;
}
mPausedStateMap[JobKeyType(clientId, jobId)] = pauseStates;
ALOGI("%s: transcoder paused", __FUNCTION__);
- return TranscodingErrorCode::kNoError;
+ return AMEDIA_OK;
}
-TranscodingErrorCode TranscoderWrapper::handleResume(
+media_status_t TranscoderWrapper::handleResume(
ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
const std::shared_ptr<ITranscodingClientCallback>& clientCb) {
std::shared_ptr<const Parcel> pausedState;
@@ -407,24 +443,24 @@
mPausedStateMap.erase(it);
} else {
ALOGE("%s: can't find paused state", __FUNCTION__);
- return TranscodingErrorCode::kInvalidOperation;
+ return AMEDIA_ERROR_INVALID_OPERATION;
}
- ALOGI("setting up transcoder for resume");
- TranscodingErrorCode err = setupTranscoder(clientId, jobId, request, clientCb, pausedState);
- if (err != TranscodingErrorCode::kNoError) {
- ALOGE("%s: failed to setup transcoder", __FUNCTION__);
+ ALOGI("%s: setting up transcoder for resume", __FUNCTION__);
+ media_status_t err = setupTranscoder(clientId, jobId, request, clientCb, pausedState);
+ if (err != AMEDIA_OK) {
+ ALOGE("%s: failed to setup transcoder: %d", __FUNCTION__, err);
return err;
}
- media_status_t status = mTranscoder->resume();
- if (status != AMEDIA_OK) {
+ err = mTranscoder->resume();
+ if (err != AMEDIA_OK) {
ALOGE("%s: failed to resume transcoder: %d", __FUNCTION__, err);
- return toTranscodingError(status);
+ return err;
}
ALOGI("%s: transcoder resumed", __FUNCTION__);
- return TranscodingErrorCode::kNoError;
+ return AMEDIA_OK;
}
void TranscoderWrapper::cleanup() {
@@ -435,12 +471,10 @@
}
void TranscoderWrapper::queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
- const std::function<void()> runnable) {
- ALOGV("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)clientId, jobId, toString(type));
-
+ const std::function<void()> runnable, int32_t arg) {
std::scoped_lock lock{mLock};
- mQueue.push_back({type, clientId, jobId, runnable});
+ mQueue.push_back({type, clientId, jobId, runnable, arg});
mCondition.notify_one();
}
@@ -457,8 +491,7 @@
Event event = *mQueue.begin();
mQueue.pop_front();
- ALOGD("%s: job {%lld, %d}: %s", __FUNCTION__, (long long)event.clientId, event.jobId,
- toString(event.type));
+ ALOGD("%s: %s", __FUNCTION__, toString(event).c_str());
lock.unlock();
event.runnable();
diff --git a/media/libmediatranscoding/TranscodingJobScheduler.cpp b/media/libmediatranscoding/TranscodingJobScheduler.cpp
index 3e4f319..24ac682 100644
--- a/media/libmediatranscoding/TranscodingJobScheduler.cpp
+++ b/media/libmediatranscoding/TranscodingJobScheduler.cpp
@@ -38,8 +38,13 @@
TranscodingJobScheduler::TranscodingJobScheduler(
const std::shared_ptr<TranscoderInterface>& transcoder,
- const std::shared_ptr<UidPolicyInterface>& uidPolicy)
- : mTranscoder(transcoder), mUidPolicy(uidPolicy), mCurrentJob(nullptr), mResourceLost(false) {
+ const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+ const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy)
+ : mTranscoder(transcoder),
+ mUidPolicy(uidPolicy),
+ mResourcePolicy(resourcePolicy),
+ mCurrentJob(nullptr),
+ mResourceLost(false) {
// Only push empty offline queue initially. Realtime queues are added when requests come in.
mUidSortedList.push_back(OFFLINE_UID);
mOfflineUidIterator = mUidSortedList.begin();
@@ -398,15 +403,24 @@
}
void TranscodingJobScheduler::onResourceLost() {
- ALOGV("%s", __FUNCTION__);
+ ALOGI("%s", __FUNCTION__);
std::scoped_lock lock{mLock};
+ if (mResourceLost) {
+ return;
+ }
+
// If we receive a resource loss event, the TranscoderLibrary already paused
// the transcoding, so we don't need to call onPaused to notify it to pause.
// Only need to update the job state here.
if (mCurrentJob != nullptr && mCurrentJob->state == Job::RUNNING) {
mCurrentJob->state = Job::PAUSED;
+ // Notify the client as a paused event.
+ auto clientCallback = mCurrentJob->callback.lock();
+ if (clientCallback != nullptr) {
+ clientCallback->onTranscodingPaused(mCurrentJob->key.second);
+ }
}
mResourceLost = true;
@@ -439,10 +453,14 @@
}
void TranscodingJobScheduler::onResourceAvailable() {
- ALOGV("%s", __FUNCTION__);
-
std::scoped_lock lock{mLock};
+ if (!mResourceLost) {
+ return;
+ }
+
+ ALOGI("%s", __FUNCTION__);
+
mResourceLost = false;
updateCurrentJob_l();
diff --git a/media/libmediatranscoding/TranscodingResourcePolicy.cpp b/media/libmediatranscoding/TranscodingResourcePolicy.cpp
new file mode 100644
index 0000000..4fd8338
--- /dev/null
+++ b/media/libmediatranscoding/TranscodingResourcePolicy.cpp
@@ -0,0 +1,169 @@
+/*
+ * Copyright (C) 2020 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_NDEBUG 0
+#define LOG_TAG "TranscodingResourcePolicy"
+
+#include <aidl/android/media/BnResourceObserver.h>
+#include <aidl/android/media/IResourceObserverService.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <binder/IServiceManager.h>
+#include <media/TranscodingResourcePolicy.h>
+#include <utils/Log.h>
+
+namespace android {
+
+using Status = ::ndk::ScopedAStatus;
+using ::aidl::android::media::BnResourceObserver;
+using ::aidl::android::media::IResourceObserverService;
+using ::aidl::android::media::MediaObservableEvent;
+using ::aidl::android::media::MediaObservableFilter;
+using ::aidl::android::media::MediaObservableParcel;
+using ::aidl::android::media::MediaObservableType;
+
+static std::string toString(const MediaObservableParcel& observable) {
+ return "{" + ::aidl::android::media::toString(observable.type) + ", " +
+ std::to_string(observable.value) + "}";
+}
+
+struct TranscodingResourcePolicy::ResourceObserver : public BnResourceObserver {
+ explicit ResourceObserver(TranscodingResourcePolicy* owner) : mOwner(owner), mPid(getpid()) {}
+
+ // IResourceObserver
+ ::ndk::ScopedAStatus onStatusChanged(
+ MediaObservableEvent event, int32_t uid, int32_t pid,
+ const std::vector<MediaObservableParcel>& observables) override {
+ ALOGD("%s: %s, uid %d, pid %d, %s", __FUNCTION__,
+ ::aidl::android::media::toString(event).c_str(), uid, pid,
+ toString(observables[0]).c_str());
+
+ // Only report kIdle event for codec resources from other processes.
+ if (((uint64_t)event & (uint64_t)MediaObservableEvent::kIdle) != 0 && (pid != mPid)) {
+ for (auto& observable : observables) {
+ if (observable.type == MediaObservableType::kVideoSecureCodec ||
+ observable.type == MediaObservableType::kVideoNonSecureCodec) {
+ mOwner->onResourceAvailable();
+ break;
+ }
+ }
+ }
+ return ::ndk::ScopedAStatus::ok();
+ }
+
+ TranscodingResourcePolicy* mOwner;
+ const pid_t mPid;
+};
+
+// static
+void TranscodingResourcePolicy::BinderDiedCallback(void* cookie) {
+ TranscodingResourcePolicy* owner = reinterpret_cast<TranscodingResourcePolicy*>(cookie);
+ if (owner != nullptr) {
+ owner->unregisterSelf();
+ }
+ // TODO(chz): retry to connecting to IResourceObserverService after failure.
+ // Also need to have back-up logic if IResourceObserverService is offline for
+ // Prolonged period of time. A possible alternative could be, during period where
+ // IResourceObserverService is not available, trigger onResourceAvailable() everytime
+ // when top uid changes (in hope that'll free up some codec instances that we could
+ // reclaim).
+}
+
+TranscodingResourcePolicy::TranscodingResourcePolicy()
+ : mRegistered(false), mDeathRecipient(AIBinder_DeathRecipient_new(BinderDiedCallback)) {
+ registerSelf();
+}
+
+TranscodingResourcePolicy::~TranscodingResourcePolicy() {
+ unregisterSelf();
+}
+
+void TranscodingResourcePolicy::registerSelf() {
+ ALOGI("TranscodingResourcePolicy: registerSelf");
+
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_observer"));
+
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (mRegistered) {
+ return;
+ }
+
+ // TODO(chz): retry to connecting to IResourceObserverService after failure.
+ mService = IResourceObserverService::fromBinder(binder);
+ if (mService == nullptr) {
+ ALOGE("Failed to get IResourceObserverService");
+ return;
+ }
+
+ // Only register filters for codec resource available.
+ mObserver = ::ndk::SharedRefBase::make<ResourceObserver>(this);
+ std::vector<MediaObservableFilter> filters = {
+ {MediaObservableType::kVideoSecureCodec, MediaObservableEvent::kIdle},
+ {MediaObservableType::kVideoNonSecureCodec, MediaObservableEvent::kIdle}};
+
+ Status status = mService->registerObserver(mObserver, filters);
+ if (!status.isOk()) {
+ ALOGE("failed to register: error %d", status.getServiceSpecificError());
+ mService = nullptr;
+ mObserver = nullptr;
+ return;
+ }
+
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(this));
+
+ ALOGD("@@@ registered observer");
+ mRegistered = true;
+}
+
+void TranscodingResourcePolicy::unregisterSelf() {
+ ALOGI("TranscodingResourcePolicy: unregisterSelf");
+
+ std::scoped_lock lock{mRegisteredLock};
+
+ if (!mRegistered) {
+ return;
+ }
+
+ ::ndk::SpAIBinder binder = mService->asBinder();
+ if (binder.get() != nullptr) {
+ Status status = mService->unregisterObserver(mObserver);
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), reinterpret_cast<void*>(this));
+ }
+
+ mService = nullptr;
+ mObserver = nullptr;
+ mRegistered = false;
+}
+
+void TranscodingResourcePolicy::setCallback(
+ const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) {
+ std::scoped_lock lock{mCallbackLock};
+ mResourcePolicyCallback = cb;
+}
+
+void TranscodingResourcePolicy::onResourceAvailable() {
+ std::shared_ptr<ResourcePolicyCallbackInterface> cb;
+ {
+ std::scoped_lock lock{mCallbackLock};
+ cb = mResourcePolicyCallback.lock();
+ }
+
+ if (cb != nullptr) {
+ cb->onResourceAvailable();
+ }
+}
+} // namespace android
diff --git a/media/libmediatranscoding/TranscodingUidPolicy.cpp b/media/libmediatranscoding/TranscodingUidPolicy.cpp
index b72a2b9..fd41f65 100644
--- a/media/libmediatranscoding/TranscodingUidPolicy.cpp
+++ b/media/libmediatranscoding/TranscodingUidPolicy.cpp
@@ -17,6 +17,10 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "TranscodingUidPolicy"
+#include <aidl/android/media/BnResourceManagerClient.h>
+#include <aidl/android/media/IResourceManagerService.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
#include <binder/ActivityManager.h>
#include <cutils/misc.h> // FIRST_APPLICATION_UID
#include <inttypes.h>
@@ -30,6 +34,43 @@
constexpr static uid_t OFFLINE_UID = -1;
constexpr static const char* kTranscodingTag = "transcoding";
+/*
+ * The OOM score we're going to ask ResourceManager to use for our native transcoding
+ * service. ResourceManager issues reclaims based on these scores. It gets the scores
+ * from ActivityManagerService, which doesn't track native services. The values of the
+ * OOM scores are defined in:
+ * frameworks/base/services/core/java/com/android/server/am/ProcessList.java
+ * We use SERVICE_ADJ which is lower priority than an app possibly visible to the
+ * user, but higher priority than a cached app (which could be killed without disruption
+ * to the user).
+ */
+constexpr static int32_t SERVICE_ADJ = 500;
+
+using Status = ::ndk::ScopedAStatus;
+using aidl::android::media::BnResourceManagerClient;
+using aidl::android::media::IResourceManagerService;
+
+/*
+ * Placeholder ResourceManagerClient for registering process info override
+ * with the IResourceManagerService. This is only used as a token by the service
+ * to get notifications about binder death, not used for reclaiming resources.
+ */
+struct TranscodingUidPolicy::ResourceManagerClient : public BnResourceManagerClient {
+ explicit ResourceManagerClient() = default;
+
+ Status reclaimResource(bool* _aidl_return) override {
+ *_aidl_return = false;
+ return Status::ok();
+ }
+
+ Status getName(::std::string* _aidl_return) override {
+ _aidl_return->clear();
+ return Status::ok();
+ }
+
+ virtual ~ResourceManagerClient() = default;
+};
+
struct TranscodingUidPolicy::UidObserver : public BnUidObserver,
public virtual IBinder::DeathRecipient {
explicit UidObserver(TranscodingUidPolicy* owner) : mOwner(owner) {}
@@ -74,6 +115,7 @@
mRegistered(false),
mTopUidState(ActivityManager::PROCESS_STATE_UNKNOWN) {
registerSelf();
+ setProcessInfoOverride();
}
TranscodingUidPolicy::~TranscodingUidPolicy() {
@@ -109,6 +151,22 @@
ALOGI("TranscodingUidPolicy: Unregistered with ActivityManager");
}
+void TranscodingUidPolicy::setProcessInfoOverride() {
+ ::ndk::SpAIBinder binder(AServiceManager_getService("media.resource_manager"));
+ std::shared_ptr<IResourceManagerService> service = IResourceManagerService::fromBinder(binder);
+ if (service == nullptr) {
+ ALOGE("Failed to get IResourceManagerService");
+ return;
+ }
+
+ mProcInfoOverrideClient = ::ndk::SharedRefBase::make<ResourceManagerClient>();
+ Status status = service->overrideProcessInfo(
+ mProcInfoOverrideClient, getpid(), ActivityManager::PROCESS_STATE_SERVICE, SERVICE_ADJ);
+ if (!status.isOk()) {
+ ALOGW("Failed to setProcessInfoOverride.");
+ }
+}
+
void TranscodingUidPolicy::setUidObserverRegistered(bool registered) {
Mutex::Autolock _l(mUidLock);
diff --git a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
index 83ea707..14d19ba 100644
--- a/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
+++ b/media/libmediatranscoding/aidl/android/media/TranscodingRequestParcel.aidl
@@ -39,6 +39,20 @@
@utf8InCpp String destinationFilePath;
/**
+ * The UID of the client that this transcoding request is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ int clientUid = -1;
+
+ /**
+ * The PID of the client that this transcoding request is for. Only privileged caller could
+ * set this Uid as only they could do the transcoding on behalf of the client.
+ * -1 means not available.
+ */
+ int clientPid = -1;
+
+ /**
* Type of the transcoding.
*/
TranscodingType transcodingType;
diff --git a/media/libmediatranscoding/include/media/ResourcePolicyInterface.h b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
new file mode 100644
index 0000000..8bd7d6b
--- /dev/null
+++ b/media/libmediatranscoding/include/media/ResourcePolicyInterface.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
+#define ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
+#include <memory>
+namespace android {
+
+class ResourcePolicyCallbackInterface;
+
+// Interface for the JobScheduler to control the resource status updates.
+class ResourcePolicyInterface {
+public:
+ // Set the associated callback interface to send the events when resource
+ // status changes. (Set to nullptr will stop the updates.)
+ virtual void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) = 0;
+
+protected:
+ virtual ~ResourcePolicyInterface() = default;
+};
+
+// Interface for notifying the JobScheduler of a change in resource status.
+class ResourcePolicyCallbackInterface {
+public:
+ // Called when codec resources become available. The scheduler may use this
+ // as a signal to attempt restart transcoding jobs that were previously
+ // paused due to temporary resource loss.
+ virtual void onResourceAvailable() = 0;
+
+protected:
+ virtual ~ResourcePolicyCallbackInterface() = default;
+};
+
+} // namespace android
+#endif // ANDROID_MEDIA_RESOURCE_POLICY_INTERFACE_H
diff --git a/media/libmediatranscoding/include/media/TranscoderWrapper.h b/media/libmediatranscoding/include/media/TranscoderWrapper.h
index a4c92c5..c956042 100644
--- a/media/libmediatranscoding/include/media/TranscoderWrapper.h
+++ b/media/libmediatranscoding/include/media/TranscoderWrapper.h
@@ -18,6 +18,7 @@
#define ANDROID_TRANSCODER_WRAPPER_H
#include <android-base/thread_annotations.h>
+#include <media/NdkMediaError.h>
#include <media/TranscoderInterface.h>
#include <list>
@@ -55,6 +56,7 @@
ClientIdType clientId;
JobIdType jobId;
std::function<void()> runnable;
+ int32_t arg;
};
using JobKeyType = std::pair<ClientIdType, JobIdType>;
@@ -68,26 +70,27 @@
ClientIdType mCurrentClientId;
JobIdType mCurrentJobId;
- static const char* toString(Event::Type type);
+ static std::string toString(const Event& event);
void onFinish(ClientIdType clientId, JobIdType jobId);
- void onError(ClientIdType clientId, JobIdType jobId, TranscodingErrorCode error);
+ void onError(ClientIdType clientId, JobIdType jobId, media_status_t status);
void onProgress(ClientIdType clientId, JobIdType jobId, int32_t progress);
- TranscodingErrorCode handleStart(ClientIdType clientId, JobIdType jobId,
- const TranscodingRequestParcel& request,
- const std::shared_ptr<ITranscodingClientCallback>& callback);
- TranscodingErrorCode handlePause(ClientIdType clientId, JobIdType jobId);
- TranscodingErrorCode handleResume(ClientIdType clientId, JobIdType jobId,
- const TranscodingRequestParcel& request,
- const std::shared_ptr<ITranscodingClientCallback>& callback);
- TranscodingErrorCode setupTranscoder(
- ClientIdType clientId, JobIdType jobId, const TranscodingRequestParcel& request,
- const std::shared_ptr<ITranscodingClientCallback>& callback,
- const std::shared_ptr<const Parcel>& pausedState = nullptr);
+ media_status_t handleStart(ClientIdType clientId, JobIdType jobId,
+ const TranscodingRequestParcel& request,
+ const std::shared_ptr<ITranscodingClientCallback>& callback);
+ media_status_t handlePause(ClientIdType clientId, JobIdType jobId);
+ media_status_t handleResume(ClientIdType clientId, JobIdType jobId,
+ const TranscodingRequestParcel& request,
+ const std::shared_ptr<ITranscodingClientCallback>& callback);
+ media_status_t setupTranscoder(ClientIdType clientId, JobIdType jobId,
+ const TranscodingRequestParcel& request,
+ const std::shared_ptr<ITranscodingClientCallback>& callback,
+ const std::shared_ptr<const Parcel>& pausedState = nullptr);
void cleanup();
+ void reportError(ClientIdType clientId, JobIdType jobId, media_status_t err);
void queueEvent(Event::Type type, ClientIdType clientId, JobIdType jobId,
- const std::function<void()> runnable);
+ const std::function<void()> runnable, int32_t arg = 0);
void threadLoop();
};
diff --git a/media/libmediatranscoding/include/media/TranscodingJobScheduler.h b/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
index 5ccadad..8f5e2aa 100644
--- a/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
+++ b/media/libmediatranscoding/include/media/TranscodingJobScheduler.h
@@ -18,6 +18,7 @@
#define ANDROID_MEDIA_TRANSCODING_JOB_SCHEDULER_H
#include <aidl/android/media/TranscodingJobPriority.h>
+#include <media/ResourcePolicyInterface.h>
#include <media/SchedulerClientInterface.h>
#include <media/TranscoderInterface.h>
#include <media/TranscodingRequest.h>
@@ -34,7 +35,8 @@
class TranscodingJobScheduler : public UidPolicyCallbackInterface,
public SchedulerClientInterface,
- public TranscoderCallbackInterface {
+ public TranscoderCallbackInterface,
+ public ResourcePolicyCallbackInterface {
public:
virtual ~TranscodingJobScheduler();
@@ -58,9 +60,12 @@
// UidPolicyCallbackInterface
void onTopUidsChanged(const std::unordered_set<uid_t>& uids) override;
- void onResourceAvailable() override;
// ~UidPolicyCallbackInterface
+ // ResourcePolicyCallbackInterface
+ void onResourceAvailable() override;
+ // ~ResourcePolicyCallbackInterface
+
private:
friend class MediaTranscodingService;
friend class TranscodingJobSchedulerTest;
@@ -96,13 +101,15 @@
std::shared_ptr<TranscoderInterface> mTranscoder;
std::shared_ptr<UidPolicyInterface> mUidPolicy;
+ std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
Job* mCurrentJob;
bool mResourceLost;
// Only allow MediaTranscodingService and unit tests to instantiate.
TranscodingJobScheduler(const std::shared_ptr<TranscoderInterface>& transcoder,
- const std::shared_ptr<UidPolicyInterface>& uidPolicy);
+ const std::shared_ptr<UidPolicyInterface>& uidPolicy,
+ const std::shared_ptr<ResourcePolicyInterface>& resourcePolicy);
Job* getTopJob_l();
void updateCurrentJob_l();
diff --git a/media/libmediatranscoding/include/media/TranscodingRequest.h b/media/libmediatranscoding/include/media/TranscodingRequest.h
index 63de1fb..a6cfed2 100644
--- a/media/libmediatranscoding/include/media/TranscodingRequest.h
+++ b/media/libmediatranscoding/include/media/TranscodingRequest.h
@@ -37,6 +37,8 @@
void setTo(const TranscodingRequestParcel& parcel) {
sourceFilePath = parcel.sourceFilePath;
destinationFilePath = parcel.destinationFilePath;
+ clientUid = parcel.clientUid;
+ clientPid = parcel.clientPid;
transcodingType = parcel.transcodingType;
requestedVideoTrackFormat = parcel.requestedVideoTrackFormat;
priority = parcel.priority;
diff --git a/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h b/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h
new file mode 100644
index 0000000..0836eda
--- /dev/null
+++ b/media/libmediatranscoding/include/media/TranscodingResourcePolicy.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
+#define ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
+
+#include <android/binder_auto_utils.h>
+#include <media/ResourcePolicyInterface.h>
+#include <utils/Condition.h>
+
+#include <mutex>
+namespace aidl {
+namespace android {
+namespace media {
+class IResourceObserverService;
+}
+} // namespace android
+} // namespace aidl
+
+namespace android {
+
+using ::aidl::android::media::IResourceObserverService;
+
+class TranscodingResourcePolicy : public ResourcePolicyInterface {
+public:
+ explicit TranscodingResourcePolicy();
+ ~TranscodingResourcePolicy();
+
+ void setCallback(const std::shared_ptr<ResourcePolicyCallbackInterface>& cb) override;
+
+private:
+ struct ResourceObserver;
+ mutable std::mutex mRegisteredLock;
+ bool mRegistered GUARDED_BY(mRegisteredLock);
+ std::shared_ptr<IResourceObserverService> mService GUARDED_BY(mRegisteredLock);
+ std::shared_ptr<ResourceObserver> mObserver;
+
+ mutable std::mutex mCallbackLock;
+ std::weak_ptr<ResourcePolicyCallbackInterface> mResourcePolicyCallback
+ GUARDED_BY(mCallbackLock);
+
+ ::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+
+ static void BinderDiedCallback(void* cookie);
+
+ void registerSelf();
+ void unregisterSelf();
+ void onResourceAvailable();
+}; // class TranscodingUidPolicy
+
+} // namespace android
+#endif // ANDROID_MEDIA_TRANSCODING_RESOURCE_POLICY_H
diff --git a/media/libmediatranscoding/include/media/TranscodingUidPolicy.h b/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
index 27dadd2..8319eee 100644
--- a/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
+++ b/media/libmediatranscoding/include/media/TranscodingUidPolicy.h
@@ -53,10 +53,12 @@
void setUidObserverRegistered(bool registerd);
void registerSelf();
void unregisterSelf();
+ void setProcessInfoOverride();
int32_t getProcState_l(uid_t uid) NO_THREAD_SAFETY_ANALYSIS;
void updateTopUid_l() NO_THREAD_SAFETY_ANALYSIS;
struct UidObserver;
+ struct ResourceManagerClient;
mutable Mutex mUidLock;
std::shared_ptr<ActivityManager> mAm;
sp<UidObserver> mUidObserver;
@@ -65,6 +67,7 @@
std::unordered_map<uid_t, int32_t> mUidStateMap GUARDED_BY(mUidLock);
std::map<int32_t, std::unordered_set<uid_t>> mStateUidMap GUARDED_BY(mUidLock);
std::weak_ptr<UidPolicyCallbackInterface> mUidPolicyCallback;
+ std::shared_ptr<ResourceManagerClient> mProcInfoOverrideClient;
}; // class TranscodingUidPolicy
} // namespace android
diff --git a/media/libmediatranscoding/include/media/UidPolicyInterface.h b/media/libmediatranscoding/include/media/UidPolicyInterface.h
index dc28027..f88c1ed 100644
--- a/media/libmediatranscoding/include/media/UidPolicyInterface.h
+++ b/media/libmediatranscoding/include/media/UidPolicyInterface.h
@@ -41,19 +41,13 @@
virtual ~UidPolicyInterface() = default;
};
-// Interface for notifying the scheduler of a change in uid states or
-// transcoding resource availability.
+// Interface for notifying the scheduler of a change in uid states.
class UidPolicyCallbackInterface {
public:
// Called when the set of uids that's top priority among the uids of interest
// has changed. The receiver of this callback should adjust accordingly.
virtual void onTopUidsChanged(const std::unordered_set<uid_t>& uids) = 0;
- // Called when resources become available for transcoding use. The scheduler
- // may use this as a signal to attempt restart transcoding activity that
- // were previously paused due to temporary resource loss.
- virtual void onResourceAvailable() = 0;
-
protected:
virtual ~UidPolicyCallbackInterface() = default;
};
diff --git a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
index d21b595..9b9df87 100644
--- a/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
+++ b/media/libmediatranscoding/tests/TranscodingJobScheduler_tests.cpp
@@ -213,7 +213,8 @@
ALOGI("TranscodingJobSchedulerTest set up");
mTranscoder.reset(new TestTranscoder());
mUidPolicy.reset(new TestUidPolicy());
- mScheduler.reset(new TranscodingJobScheduler(mTranscoder, mUidPolicy));
+ mScheduler.reset(
+ new TranscodingJobScheduler(mTranscoder, mUidPolicy, nullptr /*resourcePolicy*/));
mUidPolicy->setCallback(mScheduler);
// Set priority only, ignore other fields for now.
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index b5b5774..86372e3 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1009,6 +1009,12 @@
return err;
}
+void MediaCodec::PostReplyWithError(const sp<AMessage> &msg, int32_t err) {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+ PostReplyWithError(replyID, err);
+}
+
void MediaCodec::PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err) {
int32_t finalErr = err;
if (mReleasedByResourceManager) {
@@ -1512,7 +1518,6 @@
mStickyError = OK;
// reset state not reset by setState(UNINITIALIZED)
- mReplyID = 0;
mDequeueInputReplyID = 0;
mDequeueOutputReplyID = 0;
mDequeueInputTimeoutGeneration = 0;
@@ -2165,7 +2170,7 @@
if (mState == RELEASING) {
mComponentName.clear();
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
sendErrorResponse = false;
}
break;
@@ -2191,7 +2196,7 @@
case FLUSHED:
case STARTED:
{
- sendErrorResponse = false;
+ sendErrorResponse = (mReplyID != nullptr);
setStickyError(err);
postActivityNotificationIfPossible();
@@ -2221,7 +2226,7 @@
default:
{
- sendErrorResponse = false;
+ sendErrorResponse = (mReplyID != nullptr);
setStickyError(err);
postActivityNotificationIfPossible();
@@ -2248,7 +2253,15 @@
}
if (sendErrorResponse) {
- PostReplyWithError(mReplyID, err);
+ // TRICKY: replicate PostReplyWithError logic for
+ // err code override
+ int32_t finalErr = err;
+ if (mReleasedByResourceManager) {
+ // override the err code if MediaCodec has been
+ // released by ResourceManager.
+ finalErr = DEAD_OBJECT;
+ }
+ postPendingRepliesAndDeferredMessages(finalErr);
}
break;
}
@@ -2296,7 +2309,7 @@
MediaResource::CodecResource(mFlags & kFlagIsSecure, mIsVideo));
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2335,7 +2348,7 @@
mFlags |= kFlagUsesSoftwareRenderer;
}
setState(CONFIGURED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
// augment our media metrics info, now that we know more things
// such as what the codec extracted from any CSD passed in.
@@ -2380,6 +2393,12 @@
case kWhatInputSurfaceCreated:
{
+ if (mState != CONFIGURED) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatInputSurfaceCreated message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to initiateCreateInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage;
@@ -2398,12 +2417,18 @@
} else {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(response);
break;
}
case kWhatInputSurfaceAccepted:
{
+ if (mState != CONFIGURED) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatInputSurfaceAccepted message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to initiateSetInputSurface()
status_t err = NO_ERROR;
sp<AMessage> response = new AMessage();
@@ -2414,19 +2439,25 @@
} else {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(response);
break;
}
case kWhatSignaledInputEOS:
{
+ if (!isExecuting()) {
+ // state transitioned unexpectedly; we should have replied already.
+ ALOGD("received kWhatSignaledInputEOS message in state %s",
+ stateString(mState).c_str());
+ break;
+ }
// response to signalEndOfInputStream()
sp<AMessage> response = new AMessage;
status_t err;
if (msg->findInt32("err", &err)) {
response->setInt32("err", err);
}
- response->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages(response);
break;
}
@@ -2446,7 +2477,7 @@
MediaResource::GraphicMemoryResource(getGraphicBufferSize()));
}
setState(STARTED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2583,7 +2614,7 @@
break;
}
setState(INITIALIZED);
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2608,7 +2639,7 @@
mReleaseSurface.reset();
if (mReplyID != nullptr) {
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
}
if (mAsyncReleaseCompleteNotification != nullptr) {
flushMediametrics();
@@ -2633,7 +2664,7 @@
mCodec->signalResume();
}
- (new AMessage)->postReply(mReplyID);
+ postPendingRepliesAndDeferredMessages();
break;
}
@@ -2645,14 +2676,18 @@
case kWhatInit:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState != UNINITIALIZED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
setState(INITIALIZING);
@@ -2714,14 +2749,18 @@
case kWhatConfigure:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState != INITIALIZED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
sp<RefBase> obj;
CHECK(msg->findObject("surface", &obj));
@@ -2859,15 +2898,19 @@
case kWhatCreateInputSurface:
case kWhatSetInputSurface:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
// Must be configured, but can't have been started yet.
if (mState != CONFIGURED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
if (msg->what() == kWhatCreateInputSurface) {
mCodec->initiateCreateInputSurface();
@@ -2882,9 +2925,6 @@
}
case kWhatStart:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (mState == FLUSHED) {
setState(STARTED);
if (mHavePendingInputBuffers) {
@@ -2892,13 +2932,20 @@
mHavePendingInputBuffers = false;
}
mCodec->signalResume();
- PostReplyWithError(replyID, OK);
+ PostReplyWithError(msg, OK);
break;
} else if (mState != CONFIGURED) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
setState(STARTING);
@@ -2906,15 +2953,42 @@
break;
}
- case kWhatStop:
+ case kWhatStop: {
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ [[fallthrough]];
+ }
case kWhatRelease:
{
State targetState =
(msg->what() == kWhatStop) ? INITIALIZED : UNINITIALIZED;
+ if ((mState == RELEASING && targetState == UNINITIALIZED)
+ || (mState == STOPPING && targetState == INITIALIZED)) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
+ sp<AMessage> asyncNotify;
+ (void)msg->findMessage("async", &asyncNotify);
+ // post asyncNotify if going out of scope.
+ struct AsyncNotifyPost {
+ AsyncNotifyPost(const sp<AMessage> &asyncNotify) : mAsyncNotify(asyncNotify) {}
+ ~AsyncNotifyPost() {
+ if (mAsyncNotify) {
+ mAsyncNotify->post();
+ }
+ }
+ void clear() { mAsyncNotify.clear(); }
+ private:
+ sp<AMessage> mAsyncNotify;
+ } asyncNotifyPost{asyncNotify};
+
// already stopped/released
if (mState == UNINITIALIZED && mReleasedByResourceManager) {
sp<AMessage> response = new AMessage;
@@ -2980,12 +3054,14 @@
// after this, and we'll no longer be able to reply.
if (mState == FLUSHING || mState == STOPPING
|| mState == CONFIGURING || mState == STARTING) {
- (new AMessage)->postReply(mReplyID);
+ // mReply is always set if in these states.
+ postPendingRepliesAndDeferredMessages();
}
if (mFlags & kFlagSawMediaServerDie) {
// It's dead, Jim. Don't expect initiateShutdown to yield
// any useful results now...
+ // Any pending reply would have been handled at kWhatError.
setState(UNINITIALIZED);
if (targetState == UNINITIALIZED) {
mComponentName.clear();
@@ -2999,12 +3075,12 @@
// reply now with an error to unblock the client, client can
// release after the failure (instead of ANR).
if (msg->what() == kWhatStop && (mFlags & kFlagStickyError)) {
+ // Any pending reply would have been handled at kWhatError.
PostReplyWithError(replyID, getStickyError());
break;
}
- sp<AMessage> asyncNotify;
- if (msg->findMessage("async", &asyncNotify) && asyncNotify != nullptr) {
+ if (asyncNotify != nullptr) {
if (mSurface != NULL) {
if (!mReleaseSurface) {
mReleaseSurface.reset(new ReleaseSurface);
@@ -3024,6 +3100,12 @@
}
}
+ if (mReplyID) {
+ // State transition replies are handled above, so this reply
+ // would not be related to state transition. As we are
+ // shutting down the component, just fail the operation.
+ postPendingRepliesAndDeferredMessages(UNKNOWN_ERROR);
+ }
mReplyID = replyID;
setState(msg->what() == kWhatStop ? STOPPING : RELEASING);
@@ -3038,8 +3120,8 @@
if (asyncNotify != nullptr) {
mResourceManagerProxy->markClientForPendingRemoval();
- (new AMessage)->postReply(mReplyID);
- mReplyID = 0;
+ postPendingRepliesAndDeferredMessages();
+ asyncNotifyPost.clear();
mAsyncReleaseCompleteNotification = asyncNotify;
}
@@ -3210,17 +3292,21 @@
case kWhatSignalEndOfInputStream:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (!isExecuting() || !mHaveInputSurface) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
- PostReplyWithError(replyID, getStickyError());
+ PostReplyWithError(msg, getStickyError());
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
mCodec->signalEndOfInputStream();
break;
@@ -3262,17 +3348,21 @@
case kWhatFlush:
{
- sp<AReplyToken> replyID;
- CHECK(msg->senderAwaitsResponse(&replyID));
-
if (!isExecuting()) {
- PostReplyWithError(replyID, INVALID_OPERATION);
+ PostReplyWithError(msg, INVALID_OPERATION);
break;
} else if (mFlags & kFlagStickyError) {
- PostReplyWithError(replyID, getStickyError());
+ PostReplyWithError(msg, getStickyError());
break;
}
+ if (mReplyID) {
+ mDeferredMessages.push_back(msg);
+ break;
+ }
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
mReplyID = replyID;
// TODO: skip flushing if already FLUSHED
setState(FLUSHING);
@@ -4217,6 +4307,26 @@
return OK;
}
+void MediaCodec::postPendingRepliesAndDeferredMessages(status_t err /* = OK */) {
+ sp<AMessage> response{new AMessage};
+ if (err != OK) {
+ response->setInt32("err", err);
+ }
+ postPendingRepliesAndDeferredMessages(response);
+}
+
+void MediaCodec::postPendingRepliesAndDeferredMessages(const sp<AMessage> &response) {
+ CHECK(mReplyID);
+ response->postReply(mReplyID);
+ mReplyID.clear();
+ ALOGV_IF(!mDeferredMessages.empty(),
+ "posting %zu deferred messages", mDeferredMessages.size());
+ for (sp<AMessage> msg : mDeferredMessages) {
+ msg->post();
+ }
+ mDeferredMessages.clear();
+}
+
std::string MediaCodec::stateString(State state) {
const char *rval = NULL;
char rawbuffer[16]; // room for "%d"
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index d67874f..2422e65 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -769,6 +769,7 @@
{ "sei", kKeySEI },
{ "text-format-data", kKeyTextFormatData },
{ "thumbnail-csd-hevc", kKeyThumbnailHVCC },
+ { "slow-motion-markers", kKeySlowMotionMarkers },
}
};
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index c4026ec..46cff28 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -373,6 +373,7 @@
AString mOwnerName;
sp<MediaCodecInfo> mCodecInfo;
sp<AReplyToken> mReplyID;
+ std::vector<sp<AMessage>> mDeferredMessages;
uint32_t mFlags;
status_t mStickyError;
sp<Surface> mSurface;
@@ -442,6 +443,7 @@
static status_t PostAndAwaitResponse(
const sp<AMessage> &msg, sp<AMessage> *response);
+ void PostReplyWithError(const sp<AMessage> &msg, int32_t err);
void PostReplyWithError(const sp<AReplyToken> &replyID, int32_t err);
status_t init(const AString &name);
@@ -493,6 +495,9 @@
bool hasPendingBuffer(int portIndex);
bool hasPendingBuffer();
+ void postPendingRepliesAndDeferredMessages(status_t err = OK);
+ void postPendingRepliesAndDeferredMessages(const sp<AMessage> &response);
+
/* called to get the last codec error when the sticky flag is set.
* if no such codec error is found, returns UNKNOWN_ERROR.
*/
diff --git a/media/libstagefright/include/media/stagefright/MetaDataBase.h b/media/libstagefright/include/media/stagefright/MetaDataBase.h
index 2f34094..6b0d28f 100644
--- a/media/libstagefright/include/media/stagefright/MetaDataBase.h
+++ b/media/libstagefright/include/media/stagefright/MetaDataBase.h
@@ -257,6 +257,10 @@
kKeyRtpCvoDegrees = 'cvod', // int32_t, rtp cvo degrees as per 3GPP 26.114.
kKeyRtpDscp = 'dscp', // int32_t, DSCP(Differentiated services codepoint) of RFC 2474.
kKeySocketNetwork = 'sNet', // int64_t, socket will be bound to network handle.
+
+ // Slow-motion markers
+ kKeySlowMotionMarkers = 'slmo', // raw data, byte array following spec for
+ // MediaFormat#KEY_SLOW_MOTION_MARKERS
};
enum {
diff --git a/media/libstagefright/include/media/stagefright/ProcessInfo.h b/media/libstagefright/include/media/stagefright/ProcessInfo.h
index 0be1a52..b8a3c10 100644
--- a/media/libstagefright/include/media/stagefright/ProcessInfo.h
+++ b/media/libstagefright/include/media/stagefright/ProcessInfo.h
@@ -20,6 +20,9 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/ProcessInfoInterface.h>
+#include <map>
+#include <mutex>
+#include <utils/Condition.h>
namespace android {
@@ -28,11 +31,20 @@
virtual bool getPriority(int pid, int* priority);
virtual bool isValidPid(int pid);
+ virtual bool overrideProcessInfo(int pid, int procState, int oomScore);
+ virtual void removeProcessInfoOverride(int pid);
protected:
virtual ~ProcessInfo();
private:
+ struct ProcessInfoOverride {
+ int procState;
+ int oomScore;
+ };
+ std::mutex mOverrideLock;
+ std::map<int, ProcessInfoOverride> mOverrideMap GUARDED_BY(mOverrideLock);
+
DISALLOW_EVIL_CONSTRUCTORS(ProcessInfo);
};
diff --git a/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h b/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h
index b39112a..9260181 100644
--- a/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h
+++ b/media/libstagefright/include/media/stagefright/ProcessInfoInterface.h
@@ -24,6 +24,8 @@
struct ProcessInfoInterface : public RefBase {
virtual bool getPriority(int pid, int* priority) = 0;
virtual bool isValidPid(int pid) = 0;
+ virtual bool overrideProcessInfo(int pid, int procState, int oomScore);
+ virtual void removeProcessInfoOverride(int pid);
protected:
virtual ~ProcessInfoInterface() {}
diff --git a/media/mtp/Android.bp b/media/mtp/Android.bp
index 66a3139..e572249 100644
--- a/media/mtp/Android.bp
+++ b/media/mtp/Android.bp
@@ -52,5 +52,6 @@
"liblog",
"libusbhost",
],
+ header_libs: ["libcutils_headers"],
}
diff --git a/media/ndk/NdkMediaFormat.cpp b/media/ndk/NdkMediaFormat.cpp
index 8680641..73c52a9 100644
--- a/media/ndk/NdkMediaFormat.cpp
+++ b/media/ndk/NdkMediaFormat.cpp
@@ -364,6 +364,7 @@
EXPORT const char* AMEDIAFORMAT_KEY_SAR_WIDTH = "sar-width";
EXPORT const char* AMEDIAFORMAT_KEY_SEI = "sei";
EXPORT const char* AMEDIAFORMAT_KEY_SLICE_HEIGHT = "slice-height";
+EXPORT const char* AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS = "slow-motion-markers";
EXPORT const char* AMEDIAFORMAT_KEY_STRIDE = "stride";
EXPORT const char* AMEDIAFORMAT_KEY_TARGET_TIME = "target-time";
EXPORT const char* AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT = "temporal-layer-count";
diff --git a/media/ndk/include/media/NdkMediaFormat.h b/media/ndk/include/media/NdkMediaFormat.h
index 6371de4..394b972 100644
--- a/media/ndk/include/media/NdkMediaFormat.h
+++ b/media/ndk/include/media/NdkMediaFormat.h
@@ -322,6 +322,10 @@
extern const char* AMEDIAFORMAT_KEY_LOW_LATENCY __INTRODUCED_IN(30);
#endif /* __ANDROID_API__ >= 30 */
+#if __ANDROID_API__ >= 31
+extern const char* AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS __INTRODUCED_IN(31);
+#endif /* __ANDROID_API__ >= 31 */
+
__END_DECLS
#endif // _NDK_MEDIA_FORMAT_H
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 29f1da8..bd3337e 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -131,6 +131,7 @@
AMEDIAFORMAT_KEY_SAR_WIDTH; # var introduced=29
AMEDIAFORMAT_KEY_SEI; # var introduced=28
AMEDIAFORMAT_KEY_SLICE_HEIGHT; # var introduced=28
+ AMEDIAFORMAT_KEY_SLOW_MOTION_MARKERS; # var introduced=31
AMEDIAFORMAT_KEY_STRIDE; # var introduced=21
AMEDIAFORMAT_KEY_TARGET_TIME; # var introduced=29
AMEDIAFORMAT_KEY_TEMPORAL_LAYER_COUNT; # var introduced=29
diff --git a/media/utils/ProcessInfo.cpp b/media/utils/ProcessInfo.cpp
index 113e4a7..19225d3 100644
--- a/media/utils/ProcessInfo.cpp
+++ b/media/utils/ProcessInfo.cpp
@@ -27,6 +27,9 @@
namespace android {
+static constexpr int32_t INVALID_ADJ = -10000;
+static constexpr int32_t NATIVE_ADJ = -1000;
+
ProcessInfo::ProcessInfo() {}
bool ProcessInfo::getPriority(int pid, int* priority) {
@@ -35,8 +38,6 @@
size_t length = 1;
int32_t state;
- static const int32_t INVALID_ADJ = -10000;
- static const int32_t NATIVE_ADJ = -1000;
int32_t score = INVALID_ADJ;
status_t err = service->getProcessStatesAndOomScoresFromPids(length, &pid, &state, &score);
if (err != OK) {
@@ -45,8 +46,17 @@
}
ALOGV("pid %d state %d score %d", pid, state, score);
if (score <= NATIVE_ADJ) {
- ALOGE("pid %d invalid OOM adjustments value %d", pid, score);
- return false;
+ std::scoped_lock lock{mOverrideLock};
+
+ // If this process if not tracked by ActivityManagerService, look for overrides.
+ auto it = mOverrideMap.find(pid);
+ if (it != mOverrideMap.end()) {
+ ALOGI("pid %d invalid OOM score %d, override to %d", pid, score, it->second.oomScore);
+ score = it->second.oomScore;
+ } else {
+ ALOGE("pid %d invalid OOM score %d", pid, score);
+ return false;
+ }
}
// Use OOM adjustments value as the priority. Lower the value, higher the priority.
@@ -61,6 +71,26 @@
return (callingPid == getpid()) || (callingPid == pid) || (callingUid == AID_MEDIA);
}
+bool ProcessInfo::overrideProcessInfo(int pid, int procState, int oomScore) {
+ std::scoped_lock lock{mOverrideLock};
+
+ mOverrideMap.erase(pid);
+
+ // Disable the override if oomScore is set to NATIVE_ADJ or below.
+ if (oomScore <= NATIVE_ADJ) {
+ return false;
+ }
+
+ mOverrideMap.emplace(pid, ProcessInfoOverride{procState, oomScore});
+ return true;
+}
+
+void ProcessInfo::removeProcessInfoOverride(int pid) {
+ std::scoped_lock lock{mOverrideLock};
+
+ mOverrideMap.erase(pid);
+}
+
ProcessInfo::~ProcessInfo() {}
} // namespace android
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 7ee52c5..90a04ac 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -41,14 +41,49 @@
namespace android {
+//static
+std::mutex ResourceManagerService::sCookieLock;
+//static
+uintptr_t ResourceManagerService::sCookieCounter = 0;
+//static
+std::map<uintptr_t, sp<DeathNotifier> > ResourceManagerService::sCookieToDeathNotifierMap;
+
+class DeathNotifier : public RefBase {
+public:
+ DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
+ int pid, int64_t clientId);
+
+ virtual ~DeathNotifier() {}
+
+ // Implement death recipient
+ static void BinderDiedCallback(void* cookie);
+ virtual void binderDied();
+
+protected:
+ std::weak_ptr<ResourceManagerService> mService;
+ int mPid;
+ int64_t mClientId;
+};
+
DeathNotifier::DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
int pid, int64_t clientId)
: mService(service), mPid(pid), mClientId(clientId) {}
//static
void DeathNotifier::BinderDiedCallback(void* cookie) {
- auto thiz = static_cast<DeathNotifier*>(cookie);
- thiz->binderDied();
+ sp<DeathNotifier> notifier;
+ {
+ std::scoped_lock lock{ResourceManagerService::sCookieLock};
+ auto it = ResourceManagerService::sCookieToDeathNotifierMap.find(
+ reinterpret_cast<uintptr_t>(cookie));
+ if (it == ResourceManagerService::sCookieToDeathNotifierMap.end()) {
+ return;
+ }
+ notifier = it->second;
+ }
+ if (notifier.get() != nullptr) {
+ notifier->binderDied();
+ }
}
void DeathNotifier::binderDied() {
@@ -62,7 +97,27 @@
service->overridePid(mPid, -1);
// thiz is freed in the call below, so it must be last call referring thiz
service->removeResource(mPid, mClientId, false);
+}
+class OverrideProcessInfoDeathNotifier : public DeathNotifier {
+public:
+ OverrideProcessInfoDeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
+ int pid) : DeathNotifier(service, pid, 0) {}
+
+ virtual ~OverrideProcessInfoDeathNotifier() {}
+
+ virtual void binderDied();
+};
+
+void OverrideProcessInfoDeathNotifier::binderDied() {
+ // Don't check for pid validity since we know it's already dead.
+ std::shared_ptr<ResourceManagerService> service = mService.lock();
+ if (service == nullptr) {
+ ALOGW("ResourceManagerService is dead as well.");
+ return;
+ }
+
+ service->removeProcessInfoOverride(mPid);
}
template <typename T>
@@ -117,6 +172,7 @@
info.uid = uid;
info.clientId = clientId;
info.client = client;
+ info.cookie = 0;
info.pendingRemoval = false;
index = infos.add(clientId, info);
@@ -401,10 +457,9 @@
mergeResources(it->second, res);
}
}
- if (info.deathNotifier == nullptr && client != nullptr) {
- info.deathNotifier = new DeathNotifier(ref<ResourceManagerService>(), pid, clientId);
- AIBinder_linkToDeath(client->asBinder().get(),
- mDeathRecipient.get(), info.deathNotifier.get());
+ if (info.cookie == 0 && client != nullptr) {
+ info.cookie = addCookieAndLink_l(client->asBinder(),
+ new DeathNotifier(ref<ResourceManagerService>(), pid, clientId));
}
if (mObserverService != nullptr && !resourceAdded.empty()) {
mObserverService->onResourceAdded(uid, pid, resourceAdded);
@@ -509,8 +564,7 @@
onLastRemoved(it->second, info);
}
- AIBinder_unlinkToDeath(info.client->asBinder().get(),
- mDeathRecipient.get(), info.deathNotifier.get());
+ removeCookieAndUnlink_l(info.client->asBinder(), info.cookie);
if (mObserverService != nullptr && !info.resources.empty()) {
mObserverService->onResourceRemoved(info.uid, pid, info.resources);
@@ -692,6 +746,83 @@
return Status::ok();
}
+Status ResourceManagerService::overrideProcessInfo(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) {
+ String8 log = String8::format("overrideProcessInfo(pid %d, procState %d, oomScore %d)",
+ pid, procState, oomScore);
+ mServiceLog->add(log);
+
+ // Only allow the override if the caller already can access process state and oom scores.
+ int callingPid = AIBinder_getCallingPid();
+ if (callingPid != getpid() && (callingPid != pid || !checkCallingPermission(String16(
+ "android.permission.GET_PROCESS_STATE_AND_OOM_SCORE")))) {
+ ALOGE("Permission Denial: overrideProcessInfo method from pid=%d", callingPid);
+ return Status::fromServiceSpecificError(PERMISSION_DENIED);
+ }
+
+ if (client == nullptr) {
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ Mutex::Autolock lock(mLock);
+ removeProcessInfoOverride_l(pid);
+
+ if (!mProcessInfo->overrideProcessInfo(pid, procState, oomScore)) {
+ // Override value is rejected by ProcessInfo.
+ return Status::fromServiceSpecificError(BAD_VALUE);
+ }
+
+ uintptr_t cookie = addCookieAndLink_l(client->asBinder(),
+ new OverrideProcessInfoDeathNotifier(ref<ResourceManagerService>(), pid));
+
+ mProcessInfoOverrideMap.emplace(pid, ProcessInfoOverride{cookie, client});
+
+ return Status::ok();
+}
+
+uintptr_t ResourceManagerService::addCookieAndLink_l(
+ ::ndk::SpAIBinder binder, const sp<DeathNotifier>& notifier) {
+ std::scoped_lock lock{sCookieLock};
+
+ uintptr_t cookie;
+ // Need to skip cookie 0 (if it wraps around). ResourceInfo has cookie initialized to 0
+ // indicating the death notifier is not created yet.
+ while ((cookie = ++sCookieCounter) == 0);
+ AIBinder_linkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie);
+ sCookieToDeathNotifierMap.emplace(cookie, notifier);
+
+ return cookie;
+}
+
+void ResourceManagerService::removeCookieAndUnlink_l(
+ ::ndk::SpAIBinder binder, uintptr_t cookie) {
+ std::scoped_lock lock{sCookieLock};
+ AIBinder_unlinkToDeath(binder.get(), mDeathRecipient.get(), (void*)cookie);
+ sCookieToDeathNotifierMap.erase(cookie);
+}
+
+void ResourceManagerService::removeProcessInfoOverride(int pid) {
+ Mutex::Autolock lock(mLock);
+
+ removeProcessInfoOverride_l(pid);
+}
+
+void ResourceManagerService::removeProcessInfoOverride_l(int pid) {
+ auto it = mProcessInfoOverrideMap.find(pid);
+ if (it == mProcessInfoOverrideMap.end()) {
+ return;
+ }
+
+ mProcessInfo->removeProcessInfoOverride(pid);
+
+ removeCookieAndUnlink_l(it->second.client->asBinder(), it->second.cookie);
+
+ mProcessInfoOverrideMap.erase(pid);
+}
+
Status ResourceManagerService::markClientForPendingRemoval(int32_t pid, int64_t clientId) {
String8 log = String8::format(
"markClientForPendingRemoval(pid %d, clientId %lld)",
diff --git a/services/mediaresourcemanager/ResourceManagerService.h b/services/mediaresourcemanager/ResourceManagerService.h
index 2b3dab3..1aa1e09 100644
--- a/services/mediaresourcemanager/ResourceManagerService.h
+++ b/services/mediaresourcemanager/ResourceManagerService.h
@@ -19,6 +19,7 @@
#define ANDROID_MEDIA_RESOURCEMANAGERSERVICE_H
#include <map>
+#include <mutex>
#include <aidl/android/media/BnResourceManagerService.h>
#include <arpa/inet.h>
@@ -51,7 +52,7 @@
int64_t clientId;
uid_t uid;
std::shared_ptr<IResourceManagerClient> client;
- sp<DeathNotifier> deathNotifier;
+ uintptr_t cookie{0};
ResourceList resources;
bool pendingRemoval{false};
};
@@ -60,22 +61,6 @@
typedef KeyedVector<int64_t, ResourceInfo> ResourceInfos;
typedef KeyedVector<int, ResourceInfos> PidResourceInfosMap;
-class DeathNotifier : public RefBase {
-public:
- DeathNotifier(const std::shared_ptr<ResourceManagerService> &service,
- int pid, int64_t clientId);
-
- ~DeathNotifier() {}
-
- // Implement death recipient
- static void BinderDiedCallback(void* cookie);
- void binderDied();
-
-private:
- std::weak_ptr<ResourceManagerService> mService;
- int mPid;
- int64_t mClientId;
-};
class ResourceManagerService : public BnResourceManagerService {
public:
struct SystemCallbackInterface : public RefBase {
@@ -128,12 +113,20 @@
int originalPid,
int newPid) override;
+ Status overrideProcessInfo(
+ const std::shared_ptr<IResourceManagerClient>& client,
+ int pid,
+ int procState,
+ int oomScore) override;
+
Status markClientForPendingRemoval(int32_t pid, int64_t clientId) override;
Status removeResource(int pid, int64_t clientId, bool checkValid);
private:
friend class ResourceManagerServiceTest;
+ friend class DeathNotifier;
+ friend class OverrideProcessInfoDeathNotifier;
// Gets the list of all the clients who own the specified resource type.
// Returns false if any client belongs to a process with higher priority than the
@@ -173,6 +166,12 @@
// Get priority from process's pid
bool getPriority_l(int pid, int* priority);
+ void removeProcessInfoOverride(int pid);
+
+ void removeProcessInfoOverride_l(int pid);
+ uintptr_t addCookieAndLink_l(::ndk::SpAIBinder binder, const sp<DeathNotifier>& notifier);
+ void removeCookieAndUnlink_l(::ndk::SpAIBinder binder, uintptr_t cookie);
+
mutable Mutex mLock;
sp<ProcessInfoInterface> mProcessInfo;
sp<SystemCallbackInterface> mSystemCB;
@@ -182,7 +181,16 @@
bool mSupportsSecureWithNonSecureCodec;
int32_t mCpuBoostCount;
::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
+ struct ProcessInfoOverride {
+ uintptr_t cookie;
+ std::shared_ptr<IResourceManagerClient> client;
+ };
std::map<int, int> mOverridePidMap;
+ std::map<pid_t, ProcessInfoOverride> mProcessInfoOverrideMap;
+ static std::mutex sCookieLock;
+ static uintptr_t sCookieCounter GUARDED_BY(sCookieLock);
+ static std::map<uintptr_t, sp<DeathNotifier> > sCookieToDeathNotifierMap
+ GUARDED_BY(sCookieLock);
std::shared_ptr<ResourceObserverService> mObserverService;
};
diff --git a/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl b/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl
index 1b2d522..5cf8686 100644
--- a/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl
+++ b/services/mediaresourcemanager/aidl/android/media/IResourceManagerService.aidl
@@ -96,6 +96,28 @@
void overridePid(int originalPid, int newPid);
/**
+ * Override the process state and OOM score of the calling process with the
+ * the specified values. This is used by native service processes to specify
+ * these values for ResourceManagerService to use. ResourceManagerService usually
+ * gets these values from ActivityManagerService, however, ActivityManagerService
+ * doesn't track native service processes.
+ *
+ * @param client a token for the ResourceManagerService to link to the caller and
+ * receive notification if it goes away. This is needed for clearing
+ * the overrides.
+ * @param pid pid of the calling process.
+ * @param procState the process state value that ResourceManagerService should
+ * use for this pid.
+ * @param oomScore the oom score value that ResourceManagerService should
+ * use for this pid.
+ */
+ void overrideProcessInfo(
+ IResourceManagerClient client,
+ int pid,
+ int procState,
+ int oomScore);
+
+ /**
* Mark a client for pending removal
*
* @param pid pid from which the client's resources will be removed.
diff --git a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
index 84c320d..4cf5f0a 100644
--- a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
+++ b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
@@ -59,6 +59,14 @@
return true;
}
+ virtual bool overrideProcessInfo(
+ int /* pid */, int /* procState */, int /* oomScore */) {
+ return true;
+ }
+
+ virtual void removeProcessInfoOverride(int /* pid */) {
+ }
+
private:
DISALLOW_EVIL_CONSTRUCTORS(TestProcessInfo);
};
diff --git a/services/mediatranscoding/MediaTranscodingService.cpp b/services/mediatranscoding/MediaTranscodingService.cpp
index ef7d6d2..977995a 100644
--- a/services/mediatranscoding/MediaTranscodingService.cpp
+++ b/services/mediatranscoding/MediaTranscodingService.cpp
@@ -25,6 +25,7 @@
#include <media/TranscoderWrapper.h>
#include <media/TranscodingClientManager.h>
#include <media/TranscodingJobScheduler.h>
+#include <media/TranscodingResourcePolicy.h>
#include <media/TranscodingUidPolicy.h>
#include <private/android_filesystem_config.h>
#include <utils/Log.h>
@@ -57,11 +58,13 @@
MediaTranscodingService::MediaTranscodingService(
const std::shared_ptr<TranscoderInterface>& transcoder)
: mUidPolicy(new TranscodingUidPolicy()),
- mJobScheduler(new TranscodingJobScheduler(transcoder, mUidPolicy)),
+ mResourcePolicy(new TranscodingResourcePolicy()),
+ mJobScheduler(new TranscodingJobScheduler(transcoder, mUidPolicy, mResourcePolicy)),
mClientManager(new TranscodingClientManager(mJobScheduler)) {
ALOGV("MediaTranscodingService is created");
transcoder->setCallback(mJobScheduler);
mUidPolicy->setCallback(mJobScheduler);
+ mResourcePolicy->setCallback(mJobScheduler);
}
MediaTranscodingService::~MediaTranscodingService() {
diff --git a/services/mediatranscoding/MediaTranscodingService.h b/services/mediatranscoding/MediaTranscodingService.h
index 505239c..2ba3d55 100644
--- a/services/mediatranscoding/MediaTranscodingService.h
+++ b/services/mediatranscoding/MediaTranscodingService.h
@@ -32,6 +32,7 @@
class TranscodingJobScheduler;
class TranscoderInterface;
class UidPolicyInterface;
+class ResourcePolicyInterface;
class MediaTranscodingService : public BnMediaTranscodingService {
public:
@@ -60,6 +61,7 @@
mutable std::mutex mServiceLock;
std::shared_ptr<UidPolicyInterface> mUidPolicy;
+ std::shared_ptr<ResourcePolicyInterface> mResourcePolicy;
std::shared_ptr<TranscodingJobScheduler> mJobScheduler;
std::shared_ptr<TranscodingClientManager> mClientManager;
};
diff --git a/services/mediatranscoding/tests/Android.bp b/services/mediatranscoding/tests/Android.bp
index 364a198..6497685 100644
--- a/services/mediatranscoding/tests/Android.bp
+++ b/services/mediatranscoding/tests/Android.bp
@@ -25,14 +25,6 @@
static_libs: [
"mediatranscoding_aidl_interface-ndk_platform",
],
-}
-
-// MediaTranscodingService unit test using simulated transcoder
-cc_test {
- name: "mediatranscodingservice_simulated_tests",
- defaults: ["mediatranscodingservice_test_defaults"],
-
- srcs: ["mediatranscodingservice_simulated_tests.cpp"],
required: [
"TranscodingUidPolicy_TestAppA",
@@ -41,6 +33,14 @@
],
}
+// MediaTranscodingService unit test using simulated transcoder
+cc_test {
+ name: "mediatranscodingservice_simulated_tests",
+ defaults: ["mediatranscodingservice_test_defaults"],
+
+ srcs: ["mediatranscodingservice_simulated_tests.cpp"],
+}
+
// MediaTranscodingService unit test using real transcoder
cc_test {
name: "mediatranscodingservice_real_tests",
@@ -48,3 +48,11 @@
srcs: ["mediatranscodingservice_real_tests.cpp"],
}
+
+// MediaTranscodingService unit test related to resource management
+cc_test {
+ name: "mediatranscodingservice_resource_tests",
+ defaults: ["mediatranscodingservice_test_defaults"],
+
+ srcs: ["mediatranscodingservice_resource_tests.cpp"],
+}
diff --git a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
index 53fd7ec..b325c5c 100644
--- a/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
+++ b/services/mediatranscoding/tests/MediaTranscodingServiceTestHelper.h
@@ -168,6 +168,32 @@
return mPoppedEvent;
}
+ bool waitForSpecificEventAndPop(const Event& target, std::list<Event>* outEvents,
+ int64_t timeoutUs = 0) {
+ std::unique_lock lock(mLock);
+
+ auto startTime = std::chrono::system_clock::now();
+
+ std::list<Event>::iterator it;
+ while (((it = std::find(mEventQueue.begin(), mEventQueue.end(), target)) ==
+ mEventQueue.end()) &&
+ timeoutUs > 0) {
+ std::cv_status status = mCondition.wait_for(lock, std::chrono::microseconds(timeoutUs));
+ if (status == std::cv_status::timeout) {
+ break;
+ }
+ std::chrono::microseconds elapsedTime = std::chrono::system_clock::now() - startTime;
+ timeoutUs -= elapsedTime.count();
+ }
+
+ if (it == mEventQueue.end()) {
+ return false;
+ }
+ *outEvents = std::list<Event>(mEventQueue.begin(), std::next(it));
+ mEventQueue.erase(mEventQueue.begin(), std::next(it));
+ return true;
+ }
+
// Push 1 event to back.
void append(const Event& event,
const TranscodingErrorCode err = TranscodingErrorCode::kNoError) {
@@ -186,7 +212,7 @@
mUpdateCount++;
}
- int getUpdateCount(int *lastProgress) {
+ int getUpdateCount(int* lastProgress) {
std::unique_lock lock(mLock);
*lastProgress = mLastProgress;
return mUpdateCount;
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
index 91c14a5..0dff171 100644
--- a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/TestAppA.xml
@@ -28,6 +28,14 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
+ <activity android:name="com.android.tests.transcoding.ResourcePolicyTestActivity"
+ android:exported="true">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.DEFAULT"/>
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
</application>
</manifest>
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
index 7295073..b79164d 100644
--- a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/MainActivity.java
@@ -46,7 +46,7 @@
// Called before subsequent visible lifetimes
// for an activity process.
@Override
- public void onRestart(){
+ public void onRestart() {
super.onRestart();
// Load changes knowing that the Activity has already
// been visible within this process.
@@ -54,14 +54,14 @@
// Called at the start of the visible lifetime.
@Override
- public void onStart(){
+ public void onStart() {
super.onStart();
// Apply any required UI change now that the Activity is visible.
}
// Called at the start of the active lifetime.
@Override
- public void onResume(){
+ public void onResume() {
super.onResume();
// Resume any paused UI updates, threads, or processes required
// by the Activity but suspended when it was inactive.
@@ -80,7 +80,7 @@
// Called at the end of the active lifetime.
@Override
- public void onPause(){
+ public void onPause() {
// Suspend UI updates, threads, or CPU intensive processes
// that don't need to be updated when the Activity isn't
// the active foreground Activity.
@@ -89,7 +89,7 @@
// Called at the end of the visible lifetime.
@Override
- public void onStop(){
+ public void onStop() {
// Suspend remaining UI updates, threads, or processing
// that aren't required when the Activity isn't visible.
// Persist all edits or state changes
@@ -99,10 +99,9 @@
// Sometimes called at the end of the full lifetime.
@Override
- public void onDestroy(){
+ public void onDestroy() {
// Clean up any resources including ending threads,
// closing database connections etc.
super.onDestroy();
}
-
}
diff --git a/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/ResourcePolicyTestActivity.java b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/ResourcePolicyTestActivity.java
new file mode 100644
index 0000000..c9e2ddb
--- /dev/null
+++ b/services/mediatranscoding/tests/TranscodingUidPolicyTestApp/src/com/android/tests/transcoding/ResourcePolicyTestActivity.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2020 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.
+ */
+
+package com.android.tests.transcoding;
+
+import android.app.Activity;
+import android.media.MediaCodec;
+import android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.VideoCapabilities;
+import android.media.MediaCodecList;
+import android.media.MediaFormat;
+import android.os.Bundle;
+import android.util.Log;
+import java.io.IOException;
+import java.util.Vector;
+
+public class ResourcePolicyTestActivity extends Activity {
+ public static final int TYPE_NONSECURE = 0;
+ public static final int TYPE_SECURE = 1;
+ public static final int TYPE_MIX = 2;
+
+ protected String TAG;
+ private static final int FRAME_RATE = 10;
+ private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
+ private static final String MIME = MediaFormat.MIMETYPE_VIDEO_AVC;
+ private static final int TIMEOUT_MS = 5000;
+
+ private Vector<MediaCodec> mCodecs = new Vector<MediaCodec>();
+
+ private class TestCodecCallback extends MediaCodec.Callback {
+ @Override
+ public void onInputBufferAvailable(MediaCodec codec, int index) {
+ Log.d(TAG, "onInputBufferAvailable " + codec.toString());
+ }
+
+ @Override
+ public void onOutputBufferAvailable(
+ MediaCodec codec, int index, MediaCodec.BufferInfo info) {
+ Log.d(TAG, "onOutputBufferAvailable " + codec.toString());
+ }
+
+ @Override
+ public void onError(MediaCodec codec, MediaCodec.CodecException e) {
+ Log.d(TAG, "onError " + codec.toString() + " errorCode " + e.getErrorCode());
+ }
+
+ @Override
+ public void onOutputFormatChanged(MediaCodec codec, MediaFormat format) {
+ Log.d(TAG, "onOutputFormatChanged " + codec.toString());
+ }
+ }
+
+ private MediaCodec.Callback mCallback = new TestCodecCallback();
+
+ private MediaFormat getTestFormat(CodecCapabilities caps, boolean securePlayback) {
+ VideoCapabilities vcaps = caps.getVideoCapabilities();
+ int width = vcaps.getSupportedWidths().getLower();
+ int height = vcaps.getSupportedHeightsFor(width).getLower();
+ int bitrate = vcaps.getBitrateRange().getLower();
+
+ MediaFormat format = MediaFormat.createVideoFormat(MIME, width, height);
+ format.setInteger(MediaFormat.KEY_COLOR_FORMAT, caps.colorFormats[0]);
+ format.setInteger(MediaFormat.KEY_BIT_RATE, bitrate);
+ format.setInteger(MediaFormat.KEY_FRAME_RATE, FRAME_RATE);
+ format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, securePlayback);
+ return format;
+ }
+
+ private MediaCodecInfo getTestCodecInfo(boolean securePlayback) {
+ // Use avc decoder for testing.
+ boolean isEncoder = false;
+
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
+ for (MediaCodecInfo info : mcl.getCodecInfos()) {
+ if (info.isEncoder() != isEncoder) {
+ continue;
+ }
+ CodecCapabilities caps;
+ try {
+ caps = info.getCapabilitiesForType(MIME);
+ boolean securePlaybackSupported =
+ caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
+ boolean securePlaybackRequired =
+ caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback);
+ if ((securePlayback && securePlaybackSupported)
+ || (!securePlayback && !securePlaybackRequired)) {
+ Log.d(TAG, "securePlayback " + securePlayback + " will use " + info.getName());
+ } else {
+ Log.d(TAG, "securePlayback " + securePlayback + " skip " + info.getName());
+ continue;
+ }
+ } catch (IllegalArgumentException e) {
+ // mime is not supported
+ continue;
+ }
+ return info;
+ }
+
+ return null;
+ }
+
+ protected int allocateCodecs(int max) {
+ Bundle extras = getIntent().getExtras();
+ int type = TYPE_NONSECURE;
+ if (extras != null) {
+ type = extras.getInt("test-type", type);
+ Log.d(TAG, "type is: " + type);
+ }
+
+ boolean shouldSkip = false;
+ boolean securePlayback;
+ if (type == TYPE_NONSECURE || type == TYPE_MIX) {
+ securePlayback = false;
+ MediaCodecInfo info = getTestCodecInfo(securePlayback);
+ if (info != null) {
+ allocateCodecs(max, info, securePlayback);
+ } else {
+ shouldSkip = true;
+ }
+ }
+
+ if (!shouldSkip) {
+ if (type == TYPE_SECURE || type == TYPE_MIX) {
+ securePlayback = true;
+ MediaCodecInfo info = getTestCodecInfo(securePlayback);
+ if (info != null) {
+ allocateCodecs(max, info, securePlayback);
+ } else {
+ shouldSkip = true;
+ }
+ }
+ }
+
+ if (shouldSkip) {
+ Log.d(TAG, "test skipped as there's no supported codec.");
+ finishWithResult(RESULT_OK);
+ }
+
+ Log.d(TAG, "allocateCodecs returned " + mCodecs.size());
+ return mCodecs.size();
+ }
+
+ protected void allocateCodecs(int max, MediaCodecInfo info, boolean securePlayback) {
+ String name = info.getName();
+ CodecCapabilities caps = info.getCapabilitiesForType(MIME);
+ MediaFormat format = getTestFormat(caps, securePlayback);
+ MediaCodec codec = null;
+ for (int i = mCodecs.size(); i < max; ++i) {
+ try {
+ Log.d(TAG, "Create codec " + name + " #" + i);
+ codec = MediaCodec.createByCodecName(name);
+ codec.setCallback(mCallback);
+ Log.d(TAG, "Configure codec " + format);
+ codec.configure(format, null, null, 0);
+ Log.d(TAG, "Start codec " + format);
+ codec.start();
+ mCodecs.add(codec);
+ codec = null;
+ } catch (IllegalArgumentException e) {
+ Log.d(TAG, "IllegalArgumentException " + e.getMessage());
+ break;
+ } catch (IOException e) {
+ Log.d(TAG, "IOException " + e.getMessage());
+ break;
+ } catch (MediaCodec.CodecException e) {
+ Log.d(TAG, "CodecException 0x" + Integer.toHexString(e.getErrorCode()));
+ break;
+ } finally {
+ if (codec != null) {
+ Log.d(TAG, "release codec");
+ codec.release();
+ codec = null;
+ }
+ }
+ }
+ }
+
+ protected void finishWithResult(int result) {
+ for (int i = 0; i < mCodecs.size(); ++i) {
+ Log.d(TAG, "release codec #" + i);
+ mCodecs.get(i).release();
+ }
+ mCodecs.clear();
+ setResult(result);
+ finish();
+ Log.d(TAG, "activity finished");
+ }
+
+ private void doUseCodecs() {
+ int current = 0;
+ try {
+ for (current = 0; current < mCodecs.size(); ++current) {
+ mCodecs.get(current).getName();
+ }
+ } catch (MediaCodec.CodecException e) {
+ Log.d(TAG, "useCodecs got CodecException 0x" + Integer.toHexString(e.getErrorCode()));
+ if (e.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) {
+ Log.d(TAG, "Remove codec " + current + " from the list");
+ mCodecs.get(current).release();
+ mCodecs.remove(current);
+ mGotReclaimedException = true;
+ mUseCodecs = false;
+ }
+ return;
+ }
+ }
+
+ private Thread mWorkerThread;
+ private volatile boolean mUseCodecs = true;
+ private volatile boolean mGotReclaimedException = false;
+ protected void useCodecs() {
+ mWorkerThread = new Thread(new Runnable() {
+ @Override
+ public void run() {
+ long start = System.currentTimeMillis();
+ long timeSinceStartedMs = 0;
+ while (mUseCodecs && (timeSinceStartedMs < TIMEOUT_MS)) {
+ doUseCodecs();
+ try {
+ Thread.sleep(50 /* millis */);
+ } catch (InterruptedException e) {
+ }
+ timeSinceStartedMs = System.currentTimeMillis() - start;
+ }
+ if (mGotReclaimedException) {
+ Log.d(TAG, "Got expected reclaim exception.");
+ }
+ finishWithResult(RESULT_OK);
+ }
+ });
+ mWorkerThread.start();
+ }
+
+ private static final int MAX_INSTANCES = 32;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ TAG = "ResourcePolicyTestActivity";
+
+ Log.d(TAG, "onCreate called.");
+ super.onCreate(savedInstanceState);
+
+ if (allocateCodecs(MAX_INSTANCES) == MAX_INSTANCES) {
+ // haven't reached the limit with MAX_INSTANCES, no need to wait for reclaim exception.
+ //mWaitForReclaim = false;
+ Log.d(TAG, "Didn't hit resource limitation");
+ }
+
+ useCodecs();
+ }
+
+ @Override
+ protected void onDestroy() {
+ Log.d(TAG, "onDestroy called.");
+ super.onDestroy();
+ }
+}
diff --git a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
index d66b340..1b42a22 100755
--- a/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
+++ b/services/mediatranscoding/tests/build_and_run_all_unit_tests.sh
@@ -37,6 +37,11 @@
#adb shell /data/nativetest64/mediatranscodingservice_real_tests/mediatranscodingservice_real_tests
adb shell /data/nativetest/mediatranscodingservice_real_tests/mediatranscodingservice_real_tests
+echo "[==========] running resource tests"
+adb shell kill -9 `pid media.transcoding`
+#adb shell /data/nativetest64/mediatranscodingservice_resource_tests/mediatranscodingservice_resource_tests
+adb shell /data/nativetest/mediatranscodingservice_resource_tests/mediatranscodingservice_resource_tests
+
echo "[==========] removing debug properties"
adb shell setprop debug.transcoding.simulated_transcoder \"\"
adb shell kill -9 `pid media.transcoding`
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
index 1def4b9..45057e2 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_real_tests.cpp
@@ -45,9 +45,11 @@
class MediaTranscodingServiceRealTest : public MediaTranscodingServiceTestBase {
public:
- MediaTranscodingServiceRealTest() {}
+ MediaTranscodingServiceRealTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
- void deleteFile(const char* path) { unlink(path); }
+ virtual ~MediaTranscodingServiceRealTest() {
+ ALOGI("MediaTranscodingServiceResourceTest destroyed");
+ }
};
TEST_F(MediaTranscodingServiceRealTest, TestInvalidSource) {
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
new file mode 100644
index 0000000..fe2cfa9
--- /dev/null
+++ b/services/mediatranscoding/tests/mediatranscodingservice_resource_tests.cpp
@@ -0,0 +1,134 @@
+/*
+ * Copyright (C) 2020 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.
+ */
+
+// Unit Test for MediaTranscodingService.
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaTranscodingServiceRealTest"
+
+#include "MediaTranscodingServiceTestHelper.h"
+
+/*
+ * Tests media transcoding service with real transcoder.
+ *
+ * Uses the same test assets as the MediaTranscoder unit tests. Before running the test,
+ * please make sure to push the test assets to /sdcard:
+ *
+ * adb push $TOP/frameworks/av/media/libmediatranscoding/transcoder/tests/assets /data/local/tmp/TranscodingTestAssets
+ */
+namespace android {
+
+namespace media {
+
+constexpr int64_t kPaddingUs = 400000;
+constexpr int32_t kBitRate = 8 * 1000 * 1000; // 8Mbs
+
+constexpr const char* kLongSrcPath = "/data/local/tmp/TranscodingTestAssets/longtest_15s.mp4";
+
+constexpr const char* kResourcePolicyTestActivity =
+ "/com.android.tests.transcoding.ResourcePolicyTestActivity";
+
+#define OUTPATH(name) "/data/local/tmp/MediaTranscodingService_" #name ".MP4"
+
+class MediaTranscodingServiceResourceTest : public MediaTranscodingServiceTestBase {
+public:
+ MediaTranscodingServiceResourceTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
+
+ virtual ~MediaTranscodingServiceResourceTest() {
+ ALOGI("MediaTranscodingServiceResourceTest destroyed");
+ }
+};
+
+/**
+ * Basic testing for handling resource lost.
+ *
+ * This test starts a transcoding job (that's somewhat long and takes several seconds),
+ * then launches an activity that allocates video codec instances until it hits insufficient
+ * resource error. Because the activity is running in foreground,
+ * ResourceManager would reclaim codecs from transcoding service which should
+ * cause the job to be paused. The activity will hold the codecs for a few seconds
+ * before releasing them, and the transcoding service should be able to resume
+ * and complete the job.
+ */
+TEST_F(MediaTranscodingServiceResourceTest, TestResourceLost) {
+ ALOGD("TestResourceLost starting...");
+
+ EXPECT_TRUE(ShellHelper::RunCmd("input keyevent KEYCODE_WAKEUP"));
+ EXPECT_TRUE(ShellHelper::RunCmd("wm dismiss-keyguard"));
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+
+ registerMultipleClients();
+
+ const char* srcPath0 = kLongSrcPath;
+ const char* dstPath0 = OUTPATH(TestPauseResumeMultiClients_Client0);
+ deleteFile(dstPath0);
+
+ ALOGD("Moving app A to top...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kTestActivityName));
+
+ // Submit job to Client1.
+ ALOGD("Submitting job to client1 (app A) ...");
+ EXPECT_TRUE(submit(mClient1, 0, srcPath0, dstPath0, TranscodingJobPriority::kNormal, kBitRate));
+
+ // Client1's job should start immediately.
+ EXPECT_EQ(mClientCallback1->pop(kPaddingUs), EventTracker::Start(CLIENT(1), 0));
+
+ // Launch ResourcePolicyTestActivity, which will try to allocate up to 32
+ // instances, which should trigger insufficient resources on most devices.
+ // (Note that it's possible that the device supports a very high number of
+ // resource instances, in which case we'll simply require that the job completes.)
+ ALOGD("Launch ResourcePolicyTestActivity...");
+ EXPECT_TRUE(ShellHelper::Start(kClientPackageA, kResourcePolicyTestActivity));
+
+ // The basic requirement is that the job should complete. Wait for finish
+ // event to come and pop up all events received.
+ std::list<EventTracker::Event> events;
+ EXPECT_TRUE(mClientCallback1->waitForSpecificEventAndPop(EventTracker::Finished(CLIENT(1), 0),
+ &events, 15000000));
+
+ // If there is only 1 event, it must be finish (otherwise waitForSpecificEventAndPop
+ // woudldn't pop up anything), and we're ok.
+ //
+ // TODO: If there is only 1 event (finish), and no pause/resume happened, we need
+ // to verify that the ResourcePolicyTestActivity actually was able to allocate
+ // all 32 instances without hitting insufficient resources. Otherwise, it could
+ // be that ResourceManager was not able to reclaim codecs from the transcoding
+ // service at all, which means the resource management is broken.
+ if (events.size() > 1) {
+ EXPECT_TRUE(events.size() >= 3);
+ size_t i = 0;
+ for (auto& event : events) {
+ if (i == 0) {
+ EXPECT_EQ(event, EventTracker::Pause(CLIENT(1), 0));
+ } else if (i == events.size() - 2) {
+ EXPECT_EQ(event, EventTracker::Resume(CLIENT(1), 0));
+ } else if (i == events.size() - 1) {
+ EXPECT_EQ(event, EventTracker::Finished(CLIENT(1), 0));
+ } else {
+ EXPECT_TRUE(event == EventTracker::Pause(CLIENT(1), 0) ||
+ event == EventTracker::Resume(CLIENT(1), 0));
+ }
+ i++;
+ }
+ }
+
+ unregisterMultipleClients();
+
+ EXPECT_TRUE(ShellHelper::Stop(kClientPackageA));
+}
+
+} // namespace media
+} // namespace android
diff --git a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
index 42b5877..e9fedd9 100644
--- a/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
+++ b/services/mediatranscoding/tests/mediatranscodingservice_simulated_tests.cpp
@@ -59,7 +59,11 @@
class MediaTranscodingServiceSimulatedTest : public MediaTranscodingServiceTestBase {
public:
- MediaTranscodingServiceSimulatedTest() {}
+ MediaTranscodingServiceSimulatedTest() { ALOGI("MediaTranscodingServiceResourceTest created"); }
+
+ virtual ~MediaTranscodingServiceSimulatedTest() {
+ ALOGI("MediaTranscodingServiceResourceTest destroyed");
+ }
};
TEST_F(MediaTranscodingServiceSimulatedTest, TestRegisterNullClient) {