Bug 1003712: extend MediaResourceManagerService to support video encoder and no-wait mode. r=rjesup,jhlin,sotaro
authorJohn Lin <jolin@mozilla.com>
Wed, 04 Jun 2014 14:52:21 -0400
changeset 206973 52f23831adbc4d6442a485014797e5b312ea9e74
parent 206972 af052494ad4edc374f795230c98b6073307b73b3
child 206974 eb336259b85f050b59a5cd516bdfa28cb2a8f65e
push id494
push userraliiev@mozilla.com
push dateMon, 25 Aug 2014 18:42:16 +0000
treeherdermozilla-release@a3cc3e46b571 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrjesup, jhlin, sotaro
bugs1003712
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1003712: extend MediaResourceManagerService to support video encoder and no-wait mode. r=rjesup,jhlin,sotaro
content/media/omx/OMXCodecProxy.cpp
content/media/omx/mediaresourcemanager/IMediaResourceManagerService.cpp
content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
content/media/omx/mediaresourcemanager/MediaResourceManagerClient.h
content/media/omx/mediaresourcemanager/MediaResourceManagerService.cpp
content/media/omx/mediaresourcemanager/MediaResourceManagerService.h
--- a/content/media/omx/OMXCodecProxy.cpp
+++ b/content/media/omx/OMXCodecProxy.cpp
@@ -75,17 +75,17 @@ OMXCodecProxy::~OMXCodecProxy()
         // this value come from stagefrigh's AwesomePlayer.
         usleep(1000);
     }
   }
   // Complete all pending Binder ipc transactions
   IPCThreadState::self()->flushCommands();
 
   if (mManagerService.get() && mClient.get()) {
-    mManagerService->cancelClient(mClient);
+    mManagerService->cancelClient(mClient, IMediaResourceManagerService::HW_VIDEO_DECODER);
   }
 
   mSource.clear();
   free(mComponentName);
   mComponentName = nullptr;
 }
 
 MediaResourceManagerClient::State OMXCodecProxy::getState()
@@ -121,17 +121,17 @@ void OMXCodecProxy::requestResource()
   mClient = new MediaResourceManagerClient(listener);
 
   mManagerService = mClient->getMediaResourceManagerService();
   if (!mManagerService.get()) {
     mClient = nullptr;
     return;
   }
 
-  mManagerService->requestMediaResource(mClient, MediaResourceManagerClient::HW_VIDEO_DECODER);
+  mManagerService->requestMediaResource(mClient, IMediaResourceManagerService::HW_VIDEO_DECODER, true /* will wait */);
 }
 
 bool OMXCodecProxy::IsWaitingResources()
 {
   Mutex::Autolock autoLock(mLock);
   return mState == MediaResourceManagerClient::CLIENT_STATE_WAIT_FOR_RESOURCE;
 }
 
--- a/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.cpp
+++ b/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.cpp
@@ -29,30 +29,33 @@ enum {
 class BpMediaResourceManagerService : public BpInterface<IMediaResourceManagerService>
 {
 public:
     BpMediaResourceManagerService(const sp<IBinder>& impl)
         : BpInterface<IMediaResourceManagerService>(impl)
     {
     }
 
-    virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType)
+    virtual status_t requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType, bool willWait)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaResourceManagerService::getInterfaceDescriptor());
         data.writeStrongBinder(client->asBinder());
         data.writeInt32(resourceType);
+        data.writeInt32(willWait ? 1 : 0);
         remote()->transact(REQUEST_MEDIA_RESOURCE, data, &reply);
+        return reply.readInt32();
     }
 
-    virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client)
+    virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client, int resourceType)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IMediaResourceManagerService::getInterfaceDescriptor());
         data.writeStrongBinder(client->asBinder());
+        data.writeInt32(resourceType);
         remote()->transact(DEREGISTER_CLIENT, data, &reply);
         return reply.readInt32();
     }
 };
 
 IMPLEMENT_META_INTERFACE(MediaResourceManagerService, "android.media.IMediaResourceManagerService");
 
 // ----------------------------------------------------------------------
@@ -61,24 +64,27 @@ status_t BnMediaResourceManagerService::
     uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
 {
     switch(code) {
 
         case REQUEST_MEDIA_RESOURCE: {
             CHECK_INTERFACE(IMediaResourceManagerService, data, reply);
             sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(data.readStrongBinder());
             int resourceType = data.readInt32();
-            requestMediaResource(client, resourceType);
+            bool willWait = (data.readInt32() == 1);
+            status_t result = requestMediaResource(client, resourceType, willWait);
+            reply->writeInt32(result);
             return NO_ERROR;
         } break;
         case DEREGISTER_CLIENT: {
             CHECK_INTERFACE(IMediaResourceManagerService, data, reply);
             sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(data.readStrongBinder());
-            cancelClient(client);
-            reply->writeInt32(NO_ERROR);
+            int resourceType = data.readInt32();
+            status_t result = cancelClient(client, resourceType);
+            reply->writeInt32(result);
             return NO_ERROR;
         } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
 }
 
 // ----------------------------------------------------------------------------
--- a/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
+++ b/content/media/omx/mediaresourcemanager/IMediaResourceManagerService.h
@@ -17,20 +17,46 @@ namespace android {
 
 // ----------------------------------------------------------------------------
 
 class IMediaResourceManagerService : public IInterface
 {
 public:
     DECLARE_META_INTERFACE(MediaResourceManagerService);
 
+    // Enumeration for the resource types
+    enum ResourceType {
+      HW_VIDEO_DECODER = 0,
+      HW_AUDIO_DECODER,  // Not supported currently.
+      HW_VIDEO_ENCODER,
+      HW_CAMERA,          // Not supported currently.
+      NUM_OF_RESOURCE_TYPES,
+      INVALID_RESOURCE_TYPE = -1
+    };
+
+    enum ErrorCode {
+        RESOURCE_NOT_AVAILABLE = -EAGAIN
+    };
+
     // Request a media resource for IMediaResourceManagerClient.
-    virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType) = 0;
+    // client is the binder that service will notify (through
+    // IMediaResourceManagerClient::statusChanged()) when request status changed.
+    // resourceType is type of resource that client would like to request.
+    // willWait indicates that, when the resource is not currently available
+    // (i.e., already in use by another client), if the client wants to wait. If
+    // true, client will be put into a (FIFO) waiting list and be notified when
+    // resource is available.
+    // For unsupported types, this function returns BAD_TYPE. For supported
+    // types, it always returns OK when willWait is true; otherwise it will
+    // return true when resouce is available, or RESOURCE_NOT_AVAILABLE when
+    // resouce is in use.
+    virtual status_t requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType, bool willWait) = 0;
     // Cancel a media resource request and a resource allocated to IMediaResourceManagerClient.
-    virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client) = 0;
+    // Client must call this function after it's done with the media resource requested.
+    virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client, int resourceType) = 0;
 };
 
 
 // ----------------------------------------------------------------------------
 
 class BnMediaResourceManagerService : public BnInterface<IMediaResourceManagerService>
 {
 public:
--- a/content/media/omx/mediaresourcemanager/MediaResourceManagerClient.h
+++ b/content/media/omx/mediaresourcemanager/MediaResourceManagerClient.h
@@ -17,22 +17,16 @@ class MediaResourceManagerClient: public
 {
 public:
   // Enumeration for the valid decoding states
   enum State {
     CLIENT_STATE_WAIT_FOR_RESOURCE,
     CLIENT_STATE_RESOURCE_ASSIGNED,
     CLIENT_STATE_SHUTDOWN
   };
-  // Enumeration for the resource types
-  enum ResourceType {
-    HW_VIDEO_DECODER,
-    HW_AUDIO_DECODER,
-    HW_CAMERA
-  };
 
   struct EventListener : public virtual RefBase {
     // Notifies a change of media resource request status.
     virtual void statusChanged(int event) = 0;
   };
 
   MediaResourceManagerClient(const wp<EventListener>& listener);
 
--- a/content/media/omx/mediaresourcemanager/MediaResourceManagerService.cpp
+++ b/content/media/omx/mediaresourcemanager/MediaResourceManagerService.cpp
@@ -2,33 +2,37 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 //#define LOG_NDEBUG 0
 #define LOG_TAG "MediaResourceManagerService"
 
+#include <mozilla/Assertions.h>
+
 #include <binder/IServiceManager.h>
 #include <media/stagefright/foundation/AMessage.h>
 #include <utils/Log.h>
 
 #include "MediaResourceManagerClient.h"
 #include "MediaResourceManagerService.h"
 
 namespace android {
 
+const char* MediaResourceManagerService::kMsgKeyResourceType = "res-type";
+
 /* static */
 void MediaResourceManagerService::instantiate() {
   defaultServiceManager()->addService(
-            String16("media.resource_manager"), new MediaResourceManagerService());
+            String16("media.resource_manager"),
+            new MediaResourceManagerService());
 }
 
 MediaResourceManagerService::MediaResourceManagerService()
-  : mVideoDecoderCount(VIDEO_DECODER_COUNT)
 {
   mLooper = new ALooper;
   mLooper->setName("MediaResourceManagerService");
 
   mReflector = new AHandlerReflector<MediaResourceManagerService>(this);
   // Register AMessage handler to ALooper.
   mLooper->registerHandler(mReflector);
   // Start ALooper thread.
@@ -44,104 +48,324 @@ MediaResourceManagerService::~MediaResou
 }
 
 void MediaResourceManagerService::binderDied(const wp<IBinder>& who)
 {
   if (who != NULL) {
     Mutex::Autolock autoLock(mLock);
     sp<IBinder> binder = who.promote();
     if (binder != NULL) {
-      cancelClientLocked(binder);
+      mResources.forgetClient(binder);
     }
   }
 }
 
-void MediaResourceManagerService::requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType)
+status_t MediaResourceManagerService::requestMediaResource(const sp<IMediaResourceManagerClient>& client,
+                                                           int resourceType, bool willWait)
 {
-  if (resourceType != MediaResourceManagerClient::HW_VIDEO_DECODER) {
-    // Support only HW_VIDEO_DECODER
-    return;
-  }
-
-  {
-    Mutex::Autolock autoLock(mLock);
-    sp<IBinder> binder = client->asBinder();
-    mVideoCodecRequestQueue.push_back(binder);
-    binder->linkToDeath(this);
+  ResourceType type = static_cast<ResourceType>(resourceType);
+  // Support only HW_VIDEO_DECODER and HW_VIDEO_ENCODER.
+  switch (type) {
+    case HW_VIDEO_DECODER:
+    case HW_VIDEO_ENCODER:
+      break;
+    default:
+      // Type not supported.
+      return BAD_TYPE;
   }
 
-  sp<AMessage> notify =
-            new AMessage(kNotifyRequest, mReflector->id());
-  // Post AMessage to MediaResourceManagerService via ALooper.
-  notify->post();
-}
+  Mutex::Autolock autoLock(mLock);
 
-status_t MediaResourceManagerService::cancelClient(const sp<IMediaResourceManagerClient>& client)
-{
-  {
-    Mutex::Autolock autoLock(mLock);
-    sp<IBinder> binder = client->asBinder();
-    cancelClientLocked(binder);
+  // Must know if it will be granted or not - if there are enough unfufilled requests to
+  // use up the resource, fail.  Otherwise we know that enqueuing under lock will succeed.
+  if (!willWait &&
+      (mResources.findAvailableResource(type, mResources.countRequests(type) + 1) ==
+       NAME_NOT_FOUND)) {
+    return RESOURCE_NOT_AVAILABLE;
   }
+  // We could early-return here without enqueuing IF we can do the rest of
+  // the allocation safely here.  However, enqueuing ensures there's only
+  // one copy of that code, and that any callbacks are made from the same
+  // context.
 
-  sp<AMessage> notify =
-            new AMessage(kNotifyRequest, mReflector->id());
+  sp<IBinder> binder = client->asBinder();
+  mResources.enqueueRequest(binder, type);
+  binder->linkToDeath(this);
+
+  sp<AMessage> notify = new AMessage(kNotifyRequest, mReflector->id());
+  notify->setInt32(kMsgKeyResourceType, resourceType);
   // Post AMessage to MediaResourceManagerService via ALooper.
   notify->post();
 
+  return OK;
+}
+
+status_t MediaResourceManagerService::cancelClient(const sp<IMediaResourceManagerClient>& client,
+                                                   int resourceType)
+{
+  Mutex::Autolock autoLock(mLock);
+
+  sp<IBinder> binder = client->asBinder();
+  cancelClientLocked(binder, static_cast<ResourceType>(resourceType));
+
+  sp<AMessage> notify = new AMessage(kNotifyRequest, mReflector->id());
+  notify->setInt32(kMsgKeyResourceType, resourceType);
+  // Next!
+  // Note: since we held the lock while releasing and then posting, if there is
+  // a queue, no willWait==false entries can jump into the queue thinking they'll
+  // get the resource.
+  notify->post();
+
   return NO_ERROR;
 }
 
+// Extract resource type from message.
+static int32_t getResourceType(const sp<AMessage>& message)
+{
+  int32_t resourceType = MediaResourceManagerService::INVALID_RESOURCE_TYPE;
+  return message->findInt32(MediaResourceManagerService::kMsgKeyResourceType, &resourceType) ?
+          resourceType : MediaResourceManagerService::INVALID_RESOURCE_TYPE;
+}
+
 // Called on ALooper thread.
 void MediaResourceManagerService::onMessageReceived(const sp<AMessage> &msg)
 {
   Mutex::Autolock autoLock(mLock);
+  ResourceType type = static_cast<ResourceType>(getResourceType(msg));
+
+  // Note: a message is sent both for "I added an entry to the queue"
+  // (which may succeed, typically if the queue is empty), and for "I gave
+  // up the resource", in which case it's "give to the next waiting client,
+  // or no one".
+
+  // Exit if no resource is available, but leave the client in the waiting
+  // list.
+  int found = mResources.findAvailableResource(type);
+  if (found == NAME_NOT_FOUND) {
+    return;
+  }
 
   // Exit if no request.
-  if (mVideoCodecRequestQueue.empty()) {
+  if (!mResources.hasRequest(type)) {
     return;
   }
 
-  // Check if resource is available
-  int found = -1;
-  for (int i=0 ; i<mVideoDecoderCount ; i++) {
-    if (!mVideoDecoderSlots[i].mClient.get()) {
+  const sp<IBinder>& req = mResources.nextRequest(type);
+  mResources.aquireResource(req, type, found);
+  // Notify resource assignment to the client.
+  sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(req);
+  client->statusChanged(MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED);
+  mResources.dequeueRequest(type);
+}
+
+void MediaResourceManagerService::cancelClientLocked(const sp<IBinder>& binder,
+                                                     ResourceType resourceType)
+{
+  mResources.forgetClient(binder, resourceType);
+  binder->unlinkToDeath(this);
+}
+
+MediaResourceManagerService::ResourceTable::ResourceTable()
+{
+  // Populate types of resources.
+  for (int type = 0; type < NUM_OF_RESOURCE_TYPES; type++) {
+    ssize_t index = mMap.add(static_cast<ResourceType>(type), Resources());
+    Resources& resources = mMap.editValueAt(index);
+    int available;
+    switch (type) {
+      case HW_VIDEO_DECODER:
+        available = VIDEO_DECODER_COUNT;
+        break;
+      case HW_VIDEO_ENCODER:
+        available = VIDEO_ENCODER_COUNT;
+        break;
+      default:
+        available = 0;
+        break;
+    }
+    resources.mSlots.insertAt(0, available);
+  }
+}
+
+MediaResourceManagerService::ResourceTable::~ResourceTable() {
+  // Remove resouces.
+  mMap.clear();
+}
+
+bool MediaResourceManagerService::ResourceTable::supportsType(ResourceType type)
+{
+  return mMap.indexOfKey(type) != NAME_NOT_FOUND;
+}
+
+ssize_t MediaResourceManagerService::ResourceTable::findAvailableResource(ResourceType type,
+                                                                          size_t numberNeeded)
+{
+  MOZ_ASSERT(numberNeeded > 0);
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return found;
+  }
+  const Slots& slots = mMap.valueAt(found).mSlots;
+
+  found = NAME_NOT_FOUND;
+  for (size_t i = 0; i < slots.size(); i++) {
+    if (slots[i].mClient != nullptr) {
+      // Already in use.
+      continue;
+    }
+    if (--numberNeeded == 0) {
       found = i;
+      break;
     }
   }
 
-  // Exit if no resource is available.
-  if (found == -1) {
-    return;
+  return found;
+}
+
+bool MediaResourceManagerService::ResourceTable::isOwnedByClient(const sp<IBinder>& client,
+                                                                 ResourceType type,
+                                                                 size_t index)
+{
+  ResourceSlot* slot = resourceOfTypeAt(type, index);
+  return slot && slot->mClient == client;
+}
+
+status_t MediaResourceManagerService::ResourceTable::aquireResource(const sp<IBinder>& client,
+                                                                    ResourceType type,
+                                                                    size_t index)
+{
+  ResourceSlot* slot = resourceOfTypeAt(type, index);
+  // Resouce should not be in use.
+  MOZ_ASSERT(slot && slot->mClient == nullptr);
+  if (!slot) {
+    return NAME_NOT_FOUND;
+  } else if (slot->mClient != nullptr) {
+    // Resource already in use by other client.
+    return PERMISSION_DENIED;
+  }
+
+  slot->mClient = client;
+
+
+  return OK;
+}
+
+MediaResourceManagerService::ResourceSlot*
+MediaResourceManagerService::ResourceTable::resourceOfTypeAt(ResourceType type,
+                                                             size_t index)
+{
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return nullptr;
+  }
+
+  Slots& slots = mMap.editValueAt(found).mSlots;
+  MOZ_ASSERT(index < slots.size());
+  if (index >= slots.size()) {
+    // Index out of range.
+    return nullptr;
+  }
+  return &(slots.editItemAt(index));
+}
+
+bool MediaResourceManagerService::ResourceTable::hasRequest(ResourceType type)
+{
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return nullptr;
   }
 
-  // Assign resource to IMediaResourceManagerClient
-  Fifo::iterator front(mVideoCodecRequestQueue.begin());
-  mVideoDecoderSlots[found].mClient = *front;
-  mVideoCodecRequestQueue.erase(front);
-  // Notify resource assignment to the client.
-  sp<IMediaResourceManagerClient> client = interface_cast<IMediaResourceManagerClient>(mVideoDecoderSlots[found].mClient);
-  client->statusChanged(MediaResourceManagerClient::CLIENT_STATE_RESOURCE_ASSIGNED);
+  const Fifo& queue = mMap.valueAt(found).mRequestQueue;
+  return !queue.empty();
+}
+
+uint32_t MediaResourceManagerService::ResourceTable::countRequests(ResourceType type)
+{
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return 0;
+  }
+
+  const Fifo& queue = mMap.valueAt(found).mRequestQueue;
+  return queue.size();
+}
+
+const sp<IBinder>& MediaResourceManagerService::ResourceTable::nextRequest(ResourceType type)
+{
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return nullptr;
+  }
+
+  const Fifo& queue = mMap.valueAt(found).mRequestQueue;
+  return *(queue.begin());
 }
 
-void MediaResourceManagerService::cancelClientLocked(const sp<IBinder>& binder)
+status_t MediaResourceManagerService::ResourceTable::enqueueRequest(const sp<IBinder>& client,
+                                                                    ResourceType type)
+{
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return found;
+  }
+
+  mMap.editValueAt(found).mRequestQueue.push_back(client);
+  return OK;
+}
+
+status_t MediaResourceManagerService::ResourceTable::dequeueRequest(ResourceType type)
 {
-  // Clear the request from request queue.
-  Fifo::iterator it(mVideoCodecRequestQueue.begin());
-  while (it != mVideoCodecRequestQueue.end()) {
-    if ((*it).get() == binder.get()) {
-      mVideoCodecRequestQueue.erase(it);
+  ssize_t found = mMap.indexOfKey(type);
+  if (found == NAME_NOT_FOUND) {
+    // Unsupported type.
+    return found;
+  }
+
+  Fifo& queue = mMap.editValueAt(found).mRequestQueue;
+  queue.erase(queue.begin());
+  return OK;
+}
+
+status_t MediaResourceManagerService::ResourceTable::forgetClient(const sp<IBinder>& client)
+{
+  // Traverse all resources.
+  for (int i = 0; i < mMap.size(); i++) {
+    forgetClient(client, mMap.keyAt(i));
+  }
+  return OK;
+}
+
+status_t MediaResourceManagerService::ResourceTable::forgetClient(const sp<IBinder>& client, ResourceType type)
+{
+  MOZ_ASSERT(supportsType(type));
+
+  Resources& resources = mMap.editValueFor(type);
+
+  // Remove pending requests for given client.
+  Fifo& queue = resources.mRequestQueue;
+  Fifo::iterator it(queue.begin());
+  while (it != queue.end()) {
+    if ((*it).get() == client.get()) {
+      queue.erase(it);
       break;
     }
     it++;
   }
 
-  // Clear the client from the resource
-  for (int i=0 ; i<mVideoDecoderCount ; i++) {
-    if (mVideoDecoderSlots[i].mClient.get() == binder.get()) {
-      mVideoDecoderSlots[i].mClient = NULL;
+  // Revoke ownership for given client.
+  Slots& slots = resources.mSlots;
+  for (int i = 0; i < slots.size(); i++) {
+    ResourceSlot& slot = slots.editItemAt(i);
+    if (client.get() == slot.mClient.get()) {
+      slot.mClient = nullptr;
     }
   }
-  binder->unlinkToDeath(this);
+
+  return OK;
 }
 
 }; // namespace android
-
--- a/content/media/omx/mediaresourcemanager/MediaResourceManagerService.h
+++ b/content/media/omx/mediaresourcemanager/MediaResourceManagerService.h
@@ -5,91 +5,131 @@
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef ANDROID_MEDIARESOURCEMANAGERSERVICE_H
 #define ANDROID_MEDIARESOURCEMANAGERSERVICE_H
 
 #include <media/stagefright/foundation/ABase.h>
 #include <media/stagefright/foundation/AHandlerReflector.h>
 #include <media/stagefright/foundation/ALooper.h>
+#include <utils/KeyedVector.h>
 #include <utils/List.h>
 #include <utils/RefBase.h>
+#include <utils/Vector.h>
 
 #include "IMediaResourceManagerClient.h"
 #include "IMediaResourceManagerService.h"
 
 namespace android {
 
 /**
  * Manage permissions of using media resources(hw decoder, hw encoder, camera)
- * XXX Current implementaion support only one hw video decoder.
+ * XXX Current implementation supports only one hw video codec.
  *     Need to extend to support multiple instance and other resources.
  */
 class MediaResourceManagerService: public BnMediaResourceManagerService,
                                    public IBinder::DeathRecipient
 {
 public:
-  // The maximum number of hardware decoders available.
-  enum { VIDEO_DECODER_COUNT = 1 };
+  // The maximum number of hardware resoureces available.
+  enum
+  {
+    VIDEO_DECODER_COUNT = 1,
+    VIDEO_ENCODER_COUNT = 1
+  };
 
-  enum {
-    kNotifyRequest = 'noti'
+  enum
+  {
+    kNotifyRequest = 'noti',
   };
 
+  static const char* kMsgKeyResourceType;
+
   // Instantiate MediaResourceManagerService and register to service manager.
   // If service manager is not present, wait until service manager becomes present.
   static  void instantiate();
 
   // DeathRecipient
   virtual void binderDied(const wp<IBinder>& who);
 
   // derived from IMediaResourceManagerService
-  virtual void requestMediaResource(const sp<IMediaResourceManagerClient>& client, int resourceType);
-  virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client);
+  virtual status_t requestMediaResource(const sp<IMediaResourceManagerClient>& client,
+                                        int resourceType, bool willWait);
+  virtual status_t cancelClient(const sp<IMediaResourceManagerClient>& client,
+                                int resourceType);
 
   // Receive a message from AHandlerReflector.
   // Called on ALooper thread.
   void onMessageReceived(const sp<AMessage> &msg);
 
 protected:
   MediaResourceManagerService();
   virtual ~MediaResourceManagerService();
 
-protected:
+private:
   // Represent a media resouce.
   // Hold a IMediaResourceManagerClient that got a media resource as IBinder.
-  struct ResourceSlot {
-    ResourceSlot ()
-      {
-      }
-      sp<IBinder> mClient;
-    };
+  struct ResourceSlot
+  {
+    sp<IBinder> mClient;
+  };
+  typedef Vector<ResourceSlot> Slots;
 
-  void cancelClientLocked(const sp<IBinder>& binder);
+  typedef List<sp<IBinder> > Fifo;
+  struct Resources
+  {
+    // Queue of media resource requests. Hold IMediaResourceManagerClient that
+    // requesting a media resource as IBinder.
+    Fifo mRequestQueue;
+    // All resources that can be requested. Hold |ResourceSlot|s that track
+    // their usage.
+    Slots mSlots;
+  };
 
-  // mVideoDecoderSlots is the array of slots that represent a media resource.
-  ResourceSlot mVideoDecoderSlots[VIDEO_DECODER_COUNT];
-  // The maximum number of hardware decoders available on the device.
-  int mVideoDecoderCount;
+  typedef KeyedVector<ResourceType, Resources> ResourcesMap;
+  // Manages requests from clients and availability of resources.
+  class ResourceTable
+  {
+    ResourceTable();
+    ~ResourceTable();
+    // Resource operations.
+    bool supportsType(ResourceType type);
+    ssize_t findAvailableResource(ResourceType type, size_t number_needed = 1);
+    bool isOwnedByClient(const sp<IBinder>& client, ResourceType type, size_t index);
+    status_t aquireResource(const sp<IBinder>& client, ResourceType type, size_t index);
+    ResourceSlot* resourceOfTypeAt(ResourceType type, size_t index);
+    // Request operations.
+    bool hasRequest(ResourceType type);
+    uint32_t countRequests(ResourceType type);
+    const sp<IBinder>& nextRequest(ResourceType type);
+    status_t enqueueRequest(const sp<IBinder>& client, ResourceType type);
+    status_t dequeueRequest(ResourceType type);
+    status_t forgetClient(const sp<IBinder>& client, ResourceType type);
+    status_t forgetClient(const sp<IBinder>& client);
 
-  // The lock protects mVideoDecoderSlots and mVideoCodecRequestQueue called
-  //  from multiple threads.
-  Mutex mLock;
-  typedef List<sp<IBinder> > Fifo;
-  // Queue of media resource requests.
-  // Hold IMediaResourceManagerClient that requesting a media resource as IBinder.
-  Fifo mVideoCodecRequestQueue;
+    friend class MediaResourceManagerService;
+
+    // A map for all types of supported resources.
+    ResourcesMap mMap;
+  };
+
+  void cancelClientLocked(const sp<IBinder>& binder, ResourceType resourceType);
 
   // ALooper is a message loop used in stagefright.
   // It creates a thread for messages and handles messages in the thread.
   // ALooper is a clone of Looper in android Java.
   // http://developer.android.com/reference/android/os/Looper.html
   sp<ALooper> mLooper;
   // deliver a message to a wrapped object(OmxDecoder).
   // AHandlerReflector is similar to Handler in android Java.
   // http://developer.android.com/reference/android/os/Handler.html
   sp<AHandlerReflector<MediaResourceManagerService> > mReflector;
 
+  // The lock protects manager operations called from multiple threads.
+  Mutex mLock;
+
+  // Keeps all the records.
+  ResourceTable mResources;
 };
 
 }; // namespace android
 
 #endif // ANDROID_MEDIARESOURCEMANAGERSERVICE_H