[mediacontroller] abilities and subscriptions 79/210579/23
authorDawid Juszczak <d.juszczak@samsung.com>
Mon, 22 Jul 2019 15:17:19 +0000 (17:17 +0200)
committerDawid Juszczak <d.juszczak@samsung.com>
Tue, 17 Sep 2019 11:18:58 +0000 (13:18 +0200)
[ACR]
http://suprem.sec.samsung.net/jira/browse/TWDAPI-227

[Verification]
tested manually on chrome console
tct-mediacontroller-tizen-tests 100% PASS

Change-Id: I839c1f825e61caa828ad06d2c6b4a779ee3da320
Signed-off-by: Dawid Juszczak <d.juszczak@samsung.com>
src/mediacontroller/mediacontroller_api.js
src/mediacontroller/mediacontroller_client.cc
src/mediacontroller/mediacontroller_client.h
src/mediacontroller/mediacontroller_instance.cc
src/mediacontroller/mediacontroller_instance.h
src/mediacontroller/mediacontroller_server.cc
src/mediacontroller/mediacontroller_server.h
src/mediacontroller/mediacontroller_utils.cc
src/mediacontroller/mediacontroller_utils.h

index aef8c56..9261af3 100755 (executable)
@@ -241,6 +241,28 @@ var ServerInfoPlaylistUpdatedListener = new ListenerManager(
     }
 );
 
+var ClientAbilityChangeListener = new ListenerManager(
+    native_,
+    '_ClientAbilityChangeListener',
+    function(msg, listener) {
+        if (msg.action === 'onplaybackabilitychanged') {
+            native_.callIfPossible(
+                listener[msg.action],
+                new MediaControllerServerInfo(msg),
+                new MediaControllerPlaybackAbilitiesInfo(msg.name)
+            );
+        }
+        if (msg.action === 'onsimpleabilitychanged') {
+            native_.callIfPossible(
+                listener[msg.action],
+                new MediaControllerServerInfo(msg),
+                msg.type,
+                msg.ability
+            );
+        }
+    }
+);
+
 var EditManager = function() {
     this.isAllowed = false;
 };
@@ -316,6 +338,21 @@ var MediaControllerContentType = {
     UNDECIDED: 'UNDECIDED'
 };
 
+var MediaControllerAbilitySupport = {
+    YES: 'YES',
+    NO: 'NO',
+    UNDECIDED: 'UNDECIDED'
+};
+
+var MediaControllerSimpleAbility = {
+    PLAYBACK_POSITION: 'PLAYBACK_POSITION',
+    SHUFFLE: 'SHUFFLE',
+    REPEAT: 'REPEAT',
+    PLAYLIST: 'PLAYLIST',
+    CLIENT_CUSTOM: 'CLIENT_CUSTOM',
+    SEARCH: 'SEARCH'
+};
+
 function MediaControllerManager() {}
 
 MediaControllerManager.prototype.getClient = function() {
@@ -575,6 +612,345 @@ var MediaControllerPlaybackInfo = function(data) {
     }
 };
 
+var MediaControllerAbilities = function() {
+    var _playbackPosition = MediaControllerAbilitySupport.UNDECIDED,
+        _shuffle = MediaControllerAbilitySupport.UNDECIDED,
+        _repeat = MediaControllerAbilitySupport.UNDECIDED,
+        _playlist = MediaControllerAbilitySupport.UNDECIDED,
+        _clientCustom = MediaControllerAbilitySupport.UNDECIDED,
+        _search = MediaControllerAbilitySupport.UNDECIDED;
+
+    Object.defineProperties(this, {
+        playback: {
+            value: new MediaControllerPlaybackAbilities(),
+            enumerable: true
+        },
+        playbackPosition: {
+            get: function() {
+                return _playbackPosition;
+            },
+            set: function(val) {
+                setSimpleAbility(MediaControllerSimpleAbility.PLAYBACK_POSITION, val);
+                _playbackPosition = val;
+            },
+            enumerable: true
+        },
+        shuffle: {
+            get: function() {
+                return _shuffle;
+            },
+            set: function(val) {
+                setSimpleAbility(MediaControllerSimpleAbility.SHUFFLE, val);
+                _shuffle = val;
+            },
+            enumerable: true
+        },
+        repeat: {
+            get: function() {
+                return _repeat;
+            },
+            set: function(val) {
+                setSimpleAbility(MediaControllerSimpleAbility.REPEAT, val);
+                _repeat = val;
+            },
+            enumerable: true
+        },
+        playlist: {
+            get: function() {
+                return _playlist;
+            },
+            set: function(val) {
+                setSimpleAbility(MediaControllerSimpleAbility.PLAYLIST, val);
+                _playlist = val;
+            },
+            enumerable: true
+        },
+        clientCustom: {
+            get: function() {
+                return _clientCustom;
+            },
+            set: function(val) {
+                setSimpleAbility(MediaControllerSimpleAbility.CLIENT_CUSTOM, val);
+                _clientCustom = val;
+            },
+            enumerable: true
+        },
+        search: {
+            get: function() {
+                return _search;
+            },
+            set: function(val) {
+                setSimpleAbility(MediaControllerSimpleAbility.SEARCH, val);
+                _search = val;
+            },
+            enumerable: true
+        }
+    });
+};
+
+var MediaControllerPlaybackAbilities = function() {
+    var _play = MediaControllerAbilitySupport.UNDECIDED,
+        _pause = MediaControllerAbilitySupport.UNDECIDED,
+        _stop = MediaControllerAbilitySupport.UNDECIDED,
+        _next = MediaControllerAbilitySupport.UNDECIDED,
+        _prev = MediaControllerAbilitySupport.UNDECIDED,
+        _forward = MediaControllerAbilitySupport.UNDECIDED,
+        _rewind = MediaControllerAbilitySupport.UNDECIDED,
+        _togglePlayPause = MediaControllerAbilitySupport.UNDECIDED;
+
+    Object.defineProperties(this, {
+        play: {
+            get: function() {
+                return _play;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _play = val;
+            },
+            enumerable: true
+        },
+        pause: {
+            get: function() {
+                return _pause;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _pause = val;
+            },
+            enumerable: true
+        },
+        stop: {
+            get: function() {
+                return _stop;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _stop = val;
+            },
+            enumerable: true
+        },
+        next: {
+            get: function() {
+                return _next;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _next = val;
+            },
+            enumerable: true
+        },
+        prev: {
+            get: function() {
+                return _prev;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _prev = val;
+            },
+            enumerable: true
+        },
+        forward: {
+            get: function() {
+                return _forward;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _forward = val;
+            },
+            enumerable: true
+        },
+        rewind: {
+            get: function() {
+                return _rewind;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _rewind = val;
+            },
+            enumerable: true
+        },
+        togglePlayPause: {
+            get: function() {
+                return _togglePlayPause;
+            },
+            set: function(val) {
+                checkSupportValue(val);
+                _togglePlayPause = val;
+            },
+            enumerable: true
+        }
+    });
+
+    this.saveAbilities = function() {
+        var data = {
+            PLAY: _play,
+            PAUSE: _pause,
+            STOP: _stop,
+            NEXT: _next,
+            PREV: _prev,
+            FORWARD: _forward,
+            REWIND: _rewind,
+            TOGGLE_PLAY_PAUSE: _togglePlayPause
+        };
+
+        var result = native_.callSync(
+            'MediaControllerServer_savePlaybackAbilities',
+            data
+        );
+        if (native_.isFailure(result)) {
+            throw new native_.getErrorObject(result);
+        }
+    };
+};
+
+var MediaControllerAbilitiesInfo = function(serverName) {
+    Object.defineProperties(this, {
+        playback: {
+            value: new MediaControllerPlaybackAbilitiesInfo(serverName),
+            enumerable: true,
+            writable: false
+        },
+        playbackPosition: {
+            get: function() {
+                return getSimpleAbility(
+                    serverName,
+                    MediaControllerSimpleAbility.PLAYBACK_POSITION
+                );
+            },
+            set: function() {},
+            enumerable: true
+        },
+        shuffle: {
+            get: function() {
+                return getSimpleAbility(serverName, MediaControllerSimpleAbility.SHUFFLE);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        repeat: {
+            get: function() {
+                return getSimpleAbility(serverName, MediaControllerSimpleAbility.REPEAT);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        playlist: {
+            get: function() {
+                return getSimpleAbility(
+                    serverName,
+                    MediaControllerSimpleAbility.PLAYLIST
+                );
+            },
+            set: function() {},
+            enumerable: true
+        },
+        clientCustom: {
+            get: function() {
+                return getSimpleAbility(
+                    serverName,
+                    MediaControllerSimpleAbility.CLIENT_CUSTOM
+                );
+            },
+            set: function() {},
+            enumerable: true
+        },
+        search: {
+            get: function() {
+                return getSimpleAbility(serverName, MediaControllerSimpleAbility.SEARCH);
+            },
+            set: function() {},
+            enumerable: true
+        }
+    });
+
+    this.subscribe = function() {
+        var data = {
+            serverName: serverName
+        };
+        var result = native_.callSync('MediaControllerAbilitiesInfo_subscribe', data);
+        if (native_.isFailure(result)) {
+            throw new native_.getErrorObject(result);
+        }
+    };
+
+    this.unsubscribe = function() {
+        var data = {
+            serverName: serverName
+        };
+        var result = native_.callSync('MediaControllerAbilitiesInfo_unsubscribe', data);
+        if (native_.isFailure(result)) {
+            throw new native_.getErrorObject(result);
+        }
+    };
+};
+
+var MediaControllerPlaybackAbilitiesInfo = function(serverName) {
+    Object.defineProperties(this, {
+        play: {
+            get: function() {
+                return getPlaybackAbility(serverName, MediaControllerPlaybackState.PLAY);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        pause: {
+            get: function() {
+                return getPlaybackAbility(serverName, MediaControllerPlaybackState.PAUSE);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        stop: {
+            get: function() {
+                return getPlaybackAbility(serverName, MediaControllerPlaybackState.STOP);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        next: {
+            get: function() {
+                return getPlaybackAbility(serverName, MediaControllerPlaybackState.NEXT);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        prev: {
+            get: function() {
+                return getPlaybackAbility(serverName, MediaControllerPlaybackState.PREV);
+            },
+            set: function() {},
+            enumerable: true
+        },
+        forward: {
+            get: function() {
+                return getPlaybackAbility(
+                    serverName,
+                    MediaControllerPlaybackState.FORWARD
+                );
+            },
+            set: function() {},
+            enumerable: true
+        },
+        rewind: {
+            get: function() {
+                return getPlaybackAbility(
+                    serverName,
+                    MediaControllerPlaybackState.REWIND
+                );
+            },
+            set: function() {},
+            enumerable: true
+        },
+        togglePlayPause: {
+            get: function() {
+                return getPlaybackAbility(serverName, 'TOGGLE_PLAY_PAUSE');
+            },
+            set: function() {},
+            enumerable: true
+        }
+    });
+};
+
 function MediaControllerServer(data) {
     var _iconURI = null;
 
@@ -592,6 +968,10 @@ function MediaControllerServer(data) {
                 _iconURI = edit_.isAllowed ? (v ? v : null) : _iconURI;
             },
             enumerable: true
+        },
+        abilities: {
+            value: new MediaControllerAbilities(),
+            enumerable: true
         }
     });
 }
@@ -1094,6 +1474,83 @@ MediaControllerServer.prototype.unsetSearchRequestListener = function() {
     native_.removeListener('SearchRequestListener');
 };
 
+var getPlaybackAbility = function(serverName, action) {
+    var data = {
+        serverName: serverName,
+        action: action
+    };
+
+    var result = native_.callSync('MediaControllerClient_getPlaybackAbility', data);
+    if (native_.isFailure(result)) {
+        throw new native_.getErrorObject(result);
+    }
+    var ability = native_.getResultObject(result);
+    return ability.value;
+};
+
+var getSimpleAbility = function(name, type) {
+    var data = {
+        serverName: name,
+        abilityType: type
+    };
+    var result = native_.callSync('MediaControllerClient_getSimpleAbility', data);
+    if (native_.isFailure(result)) {
+        throw new native_.getErrorObject(result);
+    }
+    var ability = native_.getResultObject(result);
+    return ability.value;
+};
+
+var setSimpleAbility = function(abilityType, support) {
+    var args = validator_.validateArgs(arguments, [
+        {
+            name: 'abilityType',
+            type: types_.ENUM,
+            values: Object.keys(MediaControllerSimpleAbility)
+        },
+        {
+            name: 'support',
+            type: types_.ENUM,
+            values: Object.keys(MediaControllerAbilitySupport)
+        }
+    ]);
+
+    if (MediaControllerAbilitySupport.UNDECIDED === args.support) {
+        throw new WebAPIException(
+            WebAPIException.INVALID_VALUES_ERR,
+            'Cannot set ability value to ' + args.support
+        );
+    }
+
+    var data = {
+        abilityType: args.abilityType,
+        support: args.support
+    };
+
+    var result = native_.callSync('MediaControllerServer_setSimpleAbility', data);
+
+    if (native_.isFailure(result)) {
+        throw native_.getErrorObject(result);
+    }
+};
+
+var checkSupportValue = function(support) {
+    var args = validator_.validateArgs(arguments, [
+        {
+            name: 'support',
+            type: types_.ENUM,
+            values: Object.keys(MediaControllerAbilitySupport)
+        }
+    ]);
+
+    if (MediaControllerAbilitySupport.UNDECIDED === args.support) {
+        throw new WebAPIException(
+            WebAPIException.INVALID_VALUES_ERR,
+            'Cannot set ability value to ' + args.support
+        );
+    }
+};
+
 function MediaControllerClient() {}
 
 MediaControllerClient.prototype.findServers = function(successCallback, errorCallback) {
@@ -1261,6 +1718,85 @@ function SearchFilter(contentType, category, keyword, extraData) {
     });
 }
 
+MediaControllerClient.prototype.addAbilityChangeListener = function(callback) {
+    var args = validator_.validateArgs(arguments, [
+        {
+            name: 'callback',
+            type: types_.LISTENER,
+            values: ['onplaybackabilitychanged', 'onsimpleabilitychanged']
+        }
+    ]);
+
+    var nativeData = {
+        listenerId: ClientAbilityChangeListener.listenerName
+    };
+
+    if (type_.isEmptyObject(ClientAbilityChangeListener.listeners)) {
+        var result = native_.callSync(
+            'MediaControllerClient_addAbilityChangeListener',
+            nativeData
+        );
+        if (native_.isFailure(result)) {
+            throw native_.getErrorObject(result);
+        }
+    }
+
+    return ClientAbilityChangeListener.addListener(args.callback);
+};
+
+MediaControllerClient.prototype.removeAbilityChangeListener = function(callbackId) {
+    var args = validator_.validateArgs(arguments, [
+        {
+            name: 'callbackId',
+            type: types_.LONG
+        }
+    ]);
+
+    ClientAbilityChangeListener.removeListener(args.callbackId);
+
+    if (type_.isEmptyObject(ClientAbilityChangeListener.listeners)) {
+        var result = native_.callSync(
+            'MediaControllerClient_removeAbilityChangeListener'
+        );
+        if (native_.isFailure(result)) {
+            throw native_.getErrorObject(result);
+        }
+    }
+};
+
+MediaControllerClient.prototype.findSubscribedServers = function(
+    successCallback,
+    errorCallback
+) {
+    var args = validator_.validateArgs(arguments, [
+        {
+            name: 'successCallback',
+            type: types_.FUNCTION
+        },
+        {
+            name: 'errorCallback',
+            type: types_.FUNCTION,
+            optional: true,
+            nullable: true
+        }
+    ]);
+
+    var callback = function(result) {
+        if (native_.isFailure(result)) {
+            native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
+            return;
+        }
+        var info = [];
+        var data = native_.getResultObject(result);
+        for (var i = 0; i < data.length; i++) {
+            info.push(new MediaControllerServerInfo(data[i]));
+        }
+        native_.callIfPossible(args.successCallback, info);
+    };
+
+    native_.call('MediaControllerClient_findSubscribedServers', {}, callback);
+};
+
 function MediaControllerServerInfo(data) {
     Object.defineProperties(this, {
         name: {
@@ -1304,6 +1840,11 @@ function MediaControllerServerInfo(data) {
             }.bind(this),
             set: function() {},
             enumerable: true
+        },
+        abilities: {
+            value: new MediaControllerAbilitiesInfo(data.name),
+            enumerable: true,
+            writable: false
         }
     });
 }
@@ -1738,7 +2279,6 @@ MediaControllerServerInfo.prototype.removePlaylistUpdatedListener = function(wat
         var result = native_.callSync(
             'MediaControllerServerInfo_removePlaylistUpdatedListener'
         );
-
         if (native_.isFailure(result)) {
             throw native_.getErrorObject(result);
         }
index cce9ce4..7cf7cf3 100644 (file)
@@ -34,7 +34,7 @@ using common::tools::ReportError;
 using common::tools::ReportSuccess;
 using common::JsonToBundle;
 
-MediaControllerClient::MediaControllerClient() : handle_(nullptr) {
+MediaControllerClient::MediaControllerClient() : handle_(nullptr), subscribed_servers{} {
   ScopeLogger();
 }
 
@@ -59,6 +59,10 @@ MediaControllerClient::~MediaControllerClient() {
     LoggerE("Failed to unset playlist update listener");
   }
 
+  if (nullptr != ability_listener_ && !UnsetAbilityChangeListener()) {
+    LoggerE("Failed to unset ability listener");
+  }
+
   if (nullptr != handle_ && MEDIA_CONTROLLER_ERROR_NONE != mc_client_destroy(handle_)) {
     LoggerE("Unable to destroy media controller client");
   }
@@ -173,6 +177,74 @@ PlatformResult MediaControllerClient::GetLatestServerInfo(picojson::value* serve
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
+PlatformResult MediaControllerClient::GetPlaybackAbility(const std::string& server_name,
+                                                         const std::string& action,
+                                                         picojson::value* abilities) {
+  ScopeLogger();
+  mc_playback_ability_h ability_h = nullptr;
+
+  SCOPE_EXIT {
+    mc_playback_ability_destroy(ability_h);
+  };
+
+  int ret = mc_client_get_server_playback_ability(handle_, server_name.c_str(), &ability_h);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting playback ability",
+                              ("mc_client_get_server_playback_ability() error: %d, message: %s",
+                               ret, get_error_message(ret)));
+  }
+
+  std::string ability_str;
+  PlatformResult result = types::ConvertPlaybackAbility(ability_h, action, &ability_str);
+  if (!result) {
+    return LogAndCreateResult(
+        ErrorCode::UNKNOWN_ERR, "Error getting playback ability",
+        ("types::ConvertPlaybackAbility() error: %d, message: %s", ret, get_error_message(ret)));
+  }
+
+  picojson::object& abilities_obj = abilities->get<picojson::object>();
+  abilities_obj["value"] = picojson::value(ability_str);
+
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+PlatformResult MediaControllerClient::GetSimpleAbility(const std::string& server_name,
+                                                       const std::string& ability_type,
+                                                       picojson::value* ability_val) {
+  ScopeLogger();
+  mc_ability_support_e support_e = MC_ABILITY_SUPPORTED_YES;
+  mc_ability_e ability_e = MC_ABILITY_SHUFFLE;
+  PlatformResult result =
+      types::MediaControllerSimpleAbilityEnum.getValue(ability_type, &ability_e);
+  if (!result) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability",
+                              ("MediaControllerSimpleAbilityEnum.getValue() failed, error: %s",
+                               result.message().c_str()));
+  }
+
+  int ret =
+      mc_client_get_server_ability_support(handle_, server_name.c_str(), ability_e, &support_e);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(
+        ErrorCode::UNKNOWN_ERR, "Error getting ability",
+        ("mc_client_get_ability_support() error: %d, message: %s", ret, get_error_message(ret)));
+  }
+
+  std::string ability_str;
+  result = types::MediaControllerAbilitySupportEnum.getName(support_e, &ability_str);
+  if (!result) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting name of ability",
+                              ("MediaControllerAbilitySupportEnum.getName() failed, error: %s",
+                               result.message().c_str()));
+  }
+
+  *ability_val = picojson::value(picojson::object());
+  picojson::object& obj = ability_val->get<picojson::object>();
+  obj["value"] = picojson::value(ability_str);
+
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
 PlatformResult MediaControllerClient::GetPlaybackInfo(const std::string& server_name,
                                                       picojson::object* playback_info) {
   ScopeLogger();
@@ -443,6 +515,156 @@ PlatformResult MediaControllerClient::UnsetPlaybackInfoListener() {
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
+PlatformResult MediaControllerClient::SetAbilityChangeListener(const JsonCallback& callback) {
+  ScopeLogger();
+  int failed_setters = 0;
+  SCOPE_EXIT {
+    int (*unsetters[])(mc_client_h) = {mc_client_unset_playback_ability_updated_cb,
+                                       mc_client_unset_ability_support_updated_cb};
+
+    for (int i = 0; i < failed_setters; ++i) {
+      auto ret = unsetters[i](handle_);
+      if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+        LoggerE("Fail (%d) returned by the [%d] unsetter", ret, i);
+      }
+    }
+  };
+
+  int ret = mc_client_set_playback_ability_updated_cb(handle_, OnPlaybackAbilityUpdate, this);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR,
+                              "Unable to register playback ability listener",
+                              ("mc_client_set_playback_ability_updated_cb() error: %d, message: %s",
+                               ret, get_error_message(ret)));
+  }
+
+  ret = mc_client_set_ability_support_updated_cb(handle_, OnSimpleAbilityUpdate, this);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    failed_setters = 1;
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to register simple ability listener",
+                              ("mc_client_set_ability_support_updated_cb() error: %d, message: %s",
+                               ret, get_error_message(ret)));
+  }
+
+  ability_listener_ = callback;
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+void UnsubscribeAllTypes(mc_client_h handle_, const std::string& server_name) {
+  int ret;
+  for (auto type : types::MediaControllerSubscriptionTypeEnum) {
+    ret = mc_client_unsubscribe(handle_, type.second, server_name.c_str());
+    if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+      LoggerE("mc_client_unsubscribe() failed with type %s, error: %d, message: %s",
+              type.first.c_str(), ret, get_error_message(ret));
+    }
+  }
+}
+
+PlatformResult MediaControllerClient::UnsetAbilityChangeListener() {
+  ScopeLogger();
+
+  // we have to unsubscribe all servers before unsetting callback to avoid
+  // calling callback twice if the callback will be set again
+  for (auto srv : subscribed_servers) {
+    UnsubscribeAllTypes(handle_, srv);
+  }
+  subscribed_servers.clear();
+
+  int ret = mc_client_unset_playback_ability_updated_cb(handle_);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    LoggerW("mc_client_unset_playback_ability_updated_cb() failed with error %d, message: %s", ret,
+            get_error_message(ret));
+  }
+
+  ret = mc_client_unset_ability_support_updated_cb(handle_);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    LoggerW("mc_client_unset_ability_support_updated_cb() failed with error %d, message: %s", ret,
+            get_error_message(ret));
+  }
+
+  ability_listener_ = nullptr;
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+PlatformResult MediaControllerClient::SubscribeServer(const std::string& server_name) {
+  ScopeLogger();
+  int ret = 0;
+  int to_unsubscribe = 0;
+  if (nullptr == ability_listener_) {
+    return PlatformResult(ErrorCode::INVALID_STATE_ERR,
+                          "Unable to subscribe server, AbilityChangeListener is not set");
+  }
+
+  SCOPE_EXIT {
+    if (to_unsubscribe > 0) {
+      for (auto type : types::MediaControllerSubscriptionTypeEnum) {
+        if (to_unsubscribe > 0) {
+          ret = mc_client_unsubscribe(handle_, type.second, server_name.c_str());
+          if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+            LoggerW("mc_client_unsubscribe() failed on type: %s, with error: %d, message: %s",
+                    type.first.c_str(), ret, get_error_message(ret));
+          }
+        }
+        --to_unsubscribe;
+      }
+    }
+  };
+
+  for (auto type : types::MediaControllerSubscriptionTypeEnum) {
+    ret = mc_client_subscribe(handle_, type.second, server_name.c_str());
+    if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+      return LogAndCreateResult(
+          ErrorCode::UNKNOWN_ERR, "Unable to subscribe server",
+          ("mc_client_subscribe() error: %d, message: %s", ret, get_error_message(ret)));
+    }
+    ++to_unsubscribe;
+  }
+
+  to_unsubscribe = 0;
+  subscribed_servers.push_back(server_name);
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+PlatformResult MediaControllerClient::UnsubscribeServer(const std::string& server_name) {
+  ScopeLogger();
+
+  UnsubscribeAllTypes(handle_, server_name);
+  subscribed_servers.remove(server_name);
+
+  /**
+   * After unsubscribing last media controller server, unsetting and setting callback
+   * is needed to receive again callbacks from all media controller servers.
+  */
+  if (subscribed_servers.size() <= 0 && nullptr != ability_listener_) {
+    JsonCallback callback = ability_listener_;
+    UnsetAbilityChangeListener();
+    auto result = SetAbilityChangeListener(callback);
+    if (!result) {
+      return result;
+    }
+  }
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+PlatformResult MediaControllerClient::FindSubscribedServers(picojson::array* servers) {
+  ScopeLogger();
+
+  /**
+   * If subscription is successful, then servers are subscribed to every subscription type,
+   * so we can check only the one type of subscription to receive all subscribed servers.
+  */
+  int ret = mc_client_foreach_server_subscribed(handle_, MC_SUBSCRIPTION_TYPE_ABILITY_SUPPORT,
+                                                FindServersCallback, servers);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(
+        ErrorCode::UNKNOWN_ERR, "Unable to fetch active servers.",
+        ("mc_client_foreach_server() error: %d, message: %s", ret, get_error_message(ret)));
+  }
+
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
 void MediaControllerClient::OnPlaybackUpdate(const char* server_name, mc_playback_h playback,
                                              void* user_data) {
   ScopeLogger();
@@ -475,6 +697,23 @@ void MediaControllerClient::OnPlaybackUpdate(const char* server_name, mc_playbac
   client->playback_info_listener_(&data);
 }
 
+void MediaControllerClient::OnPlaybackAbilityUpdate(const char* server_name,
+                                                    mc_playback_ability_h ability,
+                                                    void* user_data) {
+  ScopeLogger();
+  MediaControllerClient* client = static_cast<MediaControllerClient*>(user_data);
+
+  picojson::value data = picojson::value(picojson::object());
+  picojson::object& data_o = data.get<picojson::object>();
+
+  data_o["action"] = picojson::value(std::string("onplaybackabilitychanged"));
+  data_o["name"] = picojson::value(std::string(server_name));
+  // only active servers can trigger listener, thus state is always "ACTIVE"
+  data_o["state"] = picojson::value(std::string("ACTIVE"));
+
+  client->ability_listener_(&data);
+}
+
 void MediaControllerClient::OnShuffleModeUpdate(const char* server_name, mc_shuffle_mode_e mode,
                                                 void* user_data) {
   ScopeLogger();
@@ -490,6 +729,40 @@ void MediaControllerClient::OnShuffleModeUpdate(const char* server_name, mc_shuf
   client->playback_info_listener_(&data);
 }
 
+void MediaControllerClient::OnSimpleAbilityUpdate(const char* server_name, mc_ability_e type,
+                                                  mc_ability_support_e ability, void* user_data) {
+  ScopeLogger();
+  MediaControllerClient* client = static_cast<MediaControllerClient*>(user_data);
+
+  std::string type_str;
+  PlatformResult result = types::MediaControllerSimpleAbilityEnum.getName(type, &type_str);
+  if (!result) {
+    LoggerE("MediaControllerSimpleAbilityEnum.getName() failed, error: %s",
+            result.message().c_str());
+    return;
+  }
+
+  std::string ability_str;
+  result = types::MediaControllerAbilitySupportEnum.getName(ability, &ability_str);
+  if (!result) {
+    LoggerE("MediaControllerAbilitySupportEnum.getName() failed, error: %s",
+            result.message().c_str());
+    return;
+  }
+
+  picojson::value data = picojson::value(picojson::object());
+  picojson::object& data_o = data.get<picojson::object>();
+
+  data_o["action"] = picojson::value(std::string("onsimpleabilitychanged"));
+  data_o["name"] = picojson::value(std::string(server_name));
+  // only active servers can trigger listener, thus state is always "ACTIVE"
+  data_o["state"] = picojson::value(std::string("ACTIVE"));
+  data_o["type"] = picojson::value(type_str);
+  data_o["ability"] = picojson::value(ability_str);
+
+  client->ability_listener_(&data);
+}
+
 void MediaControllerClient::OnRepeatModeUpdate(const char* server_name, mc_repeat_mode_e mode,
                                                void* user_data) {
   ScopeLogger();
index e073a77..e9dd2b5 100644 (file)
@@ -18,6 +18,7 @@
 #define MEDIACONTROLLER_MEDIACONTROLLER_CLIENT_H_
 
 #include <media_controller_client.h>
+#include <list>
 #include <string>
 
 #include "common/optional.h"
@@ -39,25 +40,20 @@ class MediaControllerClient {
   common::PlatformResult GetPlaybackInfo(const std::string& server_name,
                                          picojson::object* playback_info);
   common::PlatformResult GetMetadata(const std::string& server_name, picojson::object* metadata);
-
   common::PlatformResult SendPlaybackState(const std::string& server_name,
                                            const std::string& state);
   common::PlatformResult SendPlaybackPosition(const std::string& server_name, double position);
   common::PlatformResult SendShuffleMode(const std::string& server_name, bool mode);
   common::PlatformResult SendRepeatMode(const std::string& server_name, bool mode);
   common::PlatformResult SendRepeatState(const std::string& server_name, const std::string& state);
-
   common::PlatformResult SendSearchRequest(const std::string& server_name,
                                            const picojson::value& request,
                                            const JsonCallback& callback, char** request_id);
-
   common::PlatformResult SendCommand(const std::string& server_name, const std::string& command,
                                      const picojson::value& data, const JsonCallback& reply_cb,
                                      char** request_id);
-
   common::PlatformResult SetServerStatusChangeListener(const JsonCallback& callback);
   common::PlatformResult UnsetServerStatusChangeListener();
-
   common::PlatformResult SetPlaybackInfoListener(const JsonCallback& callback);
   common::PlatformResult UnsetPlaybackInfoListener();
   common::PlatformResult SendPlaybackItem(const std::string& name, const std::string& playlist_name,
@@ -67,6 +63,16 @@ class MediaControllerClient {
   common::PlatformResult UnsetPlaylistUpdateListener();
   common::PlatformResult GetServerIconURI(const std::string& name,
                                           common::optional<std::string>* icon_uri);
+  common::PlatformResult GetPlaybackAbility(const std::string& server_name,
+                                            const std::string& action, picojson::value* abilities);
+  common::PlatformResult SetAbilityChangeListener(const JsonCallback& callback);
+  common::PlatformResult UnsetAbilityChangeListener();
+  common::PlatformResult GetSimpleAbility(const std::string& server_name,
+                                          const std::string& ability_type,
+                                          picojson::value* ability_val);
+  common::PlatformResult SubscribeServer(const std::string& server_name);
+  common::PlatformResult UnsubscribeServer(const std::string& server_name);
+  common::PlatformResult FindSubscribedServers(picojson::array* servers);
 
  private:
   mc_client_h handle_;
@@ -75,6 +81,9 @@ class MediaControllerClient {
   JsonCallback server_status_listener_;
   JsonCallback command_reply_callback_;
   JsonCallback playlist_update_listener_;
+  JsonCallback ability_listener_;
+
+  std::list<std::string> subscribed_servers;
 
   static bool FindServersCallback(const char* server_name, void* user_data);
 
@@ -88,6 +97,10 @@ class MediaControllerClient {
   static void OnMetadataUpdate(const char* server_name, mc_metadata_h metadata_h, void* user_data);
   static void OnPlaylistUpdate(const char* server_name, mc_playlist_update_mode_e mode,
                                const char* playlist_name, mc_playlist_h playlist, void* user_data);
+  static void OnPlaybackAbilityUpdate(const char* server_name, mc_playback_ability_h ability,
+                                      void* user_data);
+  static void OnSimpleAbilityUpdate(const char* server_name, mc_ability_e type,
+                                    mc_ability_support_e mode, void* user_data);
 };
 
 }  // namespace mediacontroller
index 41022de..6559f82 100644 (file)
@@ -36,6 +36,8 @@ const std::string kPrivilegeMediaControllerClient =
 const std::string kPrivilegeMediaControllerServer =
     "http://tizen.org/privilege/mediacontroller.server";
 
+const char* kAbilityType = "abilityType";
+const char* kServerName = "serverName";
 }  // namespace
 
 using common::ErrorCode;
@@ -85,6 +87,9 @@ MediaControllerInstance::MediaControllerInstance() {
                 MediaControllerServerUpdatePlaybackItem);
   REGISTER_ASYNC("MediaControllerServer_getAllPlaylists", MediaControllerServerGetAllPlaylists);
   REGISTER_SYNC("MediaControllerServer_updateIconURI", MediaControllerServerUpdateIconURI);
+  REGISTER_SYNC("MediaControllerServer_savePlaybackAbilities",
+                MediaControllerServerSavePlaybackAbilities);
+  REGISTER_SYNC("MediaControllerServer_setSimpleAbility", MediaControllerServerSetSimpleAbility);
 
   // client
   REGISTER_SYNC("MediaControllerManager_getClient", MediaControllerManagerGetClient);
@@ -92,6 +97,13 @@ MediaControllerInstance::MediaControllerInstance() {
   REGISTER_SYNC("MediaControllerClient_getLatestServerInfo",
                 MediaControllerClientGetLatestServerInfo);
   REGISTER_SYNC("MediaControllerClient_getPlaybackInfo", MediaControllerClientGetPlaybackInfo);
+  REGISTER_SYNC("MediaControllerClient_getPlaybackAbility",
+                MediaControllerClientGetPlaybackAbility);
+  REGISTER_SYNC("MediaControllerClient_getSimpleAbility", MediaControllerClientGetSimpleAbility);
+  REGISTER_ASYNC("MediaControllerClient_findSubscribedServers",
+                 MediaControllerClientFindSubscribedServers);
+
+  // server_info
   REGISTER_SYNC("MediaControllerServerInfo_sendPlaybackState",
                 MediaControllerServerInfoSendPlaybackState);
   REGISTER_ASYNC("MediaControllerServerInfo_sendPlaybackPosition",
@@ -122,6 +134,15 @@ MediaControllerInstance::MediaControllerInstance() {
   REGISTER_ASYNC("MediaControllerServerInfo_getAllPlaylists",
                  MediaControllerServerInfoGetAllPlaylists);
   REGISTER_SYNC("MediaControllerServerInfo_getIconURI", MediaControllerServerInfoGetIconURI);
+  REGISTER_SYNC("MediaControllerClient_addAbilityChangeListener",
+                MediaControllerClientAddAbilityChangeListener);
+  REGISTER_SYNC("MediaControllerClient_removeAbilityChangeListener",
+                MediaControllerClientRemoveAbilityChangeListener);
+
+  // abilities_info
+  REGISTER_SYNC("MediaControllerAbilitiesInfo_subscribe", MediaControllerAbilitiesInfoSubscribe);
+  REGISTER_SYNC("MediaControllerAbilitiesInfo_unsubscribe",
+                MediaControllerAbilitiesInfoUnsubscribe);
 
   // playlist
   REGISTER_SYNC("MediaControllerPlaylist_addItem", MediaControllerPlaylistAddItem);
@@ -135,18 +156,18 @@ MediaControllerInstance::~MediaControllerInstance() {
   ScopeLogger();
 }
 
-#define CHECK_EXIST(args, name, out)                                                              \
-  if (!args.contains(name)) {                                                                     \
-    LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, name " is required argument"), \
-                      &out);                                                                      \
-    return;                                                                                       \
+#define CHECK_EXIST(args, name, out)                                            \
+  if (!args.contains(name)) {                                                   \
+    std::string msg = std::string(name) + " is required argument";              \
+    LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg), &out); \
+    return;                                                                     \
   }
 
-#define CHECK_TYPE(args, name, type, out)                                                    \
-  if (!args.get(name).is<type>()) {                                                          \
-    LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, name " is not a " #type), \
-                      &out);                                                                 \
-    return;                                                                                  \
+#define CHECK_TYPE(args, name, type, out)                                       \
+  if (!args.get(name).is<type>()) {                                             \
+    std::string msg = std::string(name) + " is not a " + #type;                 \
+    LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg), &out); \
+    return;                                                                     \
   }
 
 #define CHECK_ARGS(args, name, type, out) \
@@ -682,6 +703,58 @@ void MediaControllerInstance::MediaControllerServerGetAllPlaylists(const picojso
   ReportSuccess(out);
 }
 
+void MediaControllerInstance::MediaControllerServerSavePlaybackAbilities(
+    const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  if (!server_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: server_"));
+    return;
+  }
+
+  CHECK_ARGS(args, "PLAY", std::string, out)
+  CHECK_ARGS(args, "PAUSE", std::string, out)
+  CHECK_ARGS(args, "STOP", std::string, out)
+  CHECK_ARGS(args, "NEXT", std::string, out)
+  CHECK_ARGS(args, "PREV", std::string, out)
+  CHECK_ARGS(args, "FORWARD", std::string, out)
+  CHECK_ARGS(args, "REWIND", std::string, out)
+  CHECK_ARGS(args, "TOGGLE_PLAY_PAUSE", std::string, out)
+
+  const PlatformResult& result = server_->SavePlaybackAbilities(args);
+  if (!result) {
+    LogAndReportError(result, &out, ("Failed server_->SavePlaybackAbilities()"));
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
+void MediaControllerInstance::MediaControllerServerSetSimpleAbility(const picojson::value& args,
+                                                                    picojson::object& out) {
+  ScopeLogger();
+
+  if (!server_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: server_"));
+    return;
+  }
+
+  CHECK_ARGS(args, kAbilityType, std::string, out)
+  CHECK_ARGS(args, "support", std::string, out)
+
+  std::string ability_type = args.get(kAbilityType).get<std::string>();
+  std::string support = args.get("support").get<std::string>();
+  const PlatformResult& result = server_->SetSimpleAbility(ability_type, support);
+  if (!result) {
+    LogAndReportError(result, &out, ("Failed server_->SetSimpleAbility()"));
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
 void MediaControllerInstance::MediaControllerManagerGetClient(const picojson::value& args,
                                                               picojson::object& out) {
   ScopeLogger();
@@ -778,6 +851,91 @@ void MediaControllerInstance::MediaControllerClientGetPlaybackInfo(const picojso
   ReportSuccess(playback_info, out);
 }
 
+void MediaControllerInstance::MediaControllerClientGetPlaybackAbility(const picojson::value& args,
+                                                                      picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+  CHECK_ARGS(args, kServerName, std::string, out)
+  CHECK_ARGS(args, "action", std::string, out)
+
+  std::string server_name = args.get(kServerName).get<std::string>();
+  std::string action = args.get("action").get<std::string>();
+  picojson::value ability = picojson::value(picojson::object());
+  PlatformResult result = client_->GetPlaybackAbility(server_name, action, &ability);
+  if (!result) {
+    LogAndReportError(result, &out, ("Failed: client_->GetPlaybackAbility"));
+    return;
+  }
+
+  ReportSuccess(ability, out);
+}
+
+void MediaControllerInstance::MediaControllerClientGetSimpleAbility(const picojson::value& args,
+                                                                    picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+
+  CHECK_ARGS(args, kAbilityType, std::string, out)
+  CHECK_ARGS(args, kServerName, std::string, out)
+
+  picojson::value ability_val = picojson::value();
+  std::string ability_type = args.get(kAbilityType).get<std::string>();
+  std::string server_name = args.get(kServerName).get<std::string>();
+  PlatformResult result = client_->GetSimpleAbility(server_name, ability_type, &ability_val);
+  if (!result) {
+    LogAndReportError(result, &out, ("Failed: client_->GetSimpleAbility"));
+    return;
+  }
+
+  ReportSuccess(ability_val, out);
+}
+
+void MediaControllerInstance::MediaControllerClientFindSubscribedServers(
+    const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+
+  CHECK_EXIST(args, "callbackId", out)
+
+  picojson::value callback_id = args.get("callbackId");
+
+  auto search = [this, callback_id]() -> void {
+    picojson::value response = picojson::value(picojson::object());
+    picojson::object& response_obj = response.get<picojson::object>();
+
+    picojson::value servers = picojson::value(picojson::array());
+    PlatformResult result = client_->FindSubscribedServers(&servers.get<picojson::array>());
+
+    response_obj["callbackId"] = callback_id;
+    if (result) {
+      ReportSuccess(servers, response_obj);
+    } else {
+      LogAndReportError(result, &response_obj, ("Failed to find subscribed servers"));
+    }
+
+    Instance::PostMessage(this, response.serialize().c_str());
+  };
+
+  TaskQueue::GetInstance().Async(search);
+
+  ReportSuccess(out);
+}
+
 void MediaControllerInstance::MediaControllerServerInfoSendPlaybackState(
     const picojson::value& args, picojson::object& out) {
   ScopeLogger();
@@ -1250,6 +1408,104 @@ void MediaControllerInstance::MediaControllerServerInfoGetIconURI(const picojson
   ReportSuccess(picojson::value(result), out);
 }
 
+void MediaControllerInstance::MediaControllerClientAddAbilityChangeListener(
+    const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+
+  CHECK_EXIST(args, "listenerId", out)
+
+  picojson::value listener_id = args.get("listenerId");
+
+  JsonCallback callback = [this, listener_id](picojson::value* data) -> void {
+    if (!data) {
+      LoggerE("No data passed to json callback");
+      return;
+    }
+
+    picojson::object& request_o = data->get<picojson::object>();
+    request_o["listenerId"] = listener_id;
+
+    Instance::PostMessage(this, data->serialize().c_str());
+  };
+
+  auto result = client_->SetAbilityChangeListener(callback);
+  if (!result) {
+    LogAndReportError(result, &out);
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
+void MediaControllerInstance::MediaControllerClientRemoveAbilityChangeListener(
+    const picojson::value& args, picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+
+  auto result = client_->UnsetAbilityChangeListener();
+  if (!result) {
+    LogAndReportError(result, &out);
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
+void MediaControllerInstance::MediaControllerAbilitiesInfoSubscribe(const picojson::value& args,
+                                                                    picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+
+  CHECK_ARGS(args, kServerName, std::string, out)
+
+  const std::string& server_name = args.get(kServerName).get<std::string>();
+  auto result = client_->SubscribeServer(server_name);
+  if (!result) {
+    LogAndReportError(result, &out);
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
+void MediaControllerInstance::MediaControllerAbilitiesInfoUnsubscribe(const picojson::value& args,
+                                                                      picojson::object& out) {
+  ScopeLogger();
+
+  if (!client_) {
+    LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out,
+                      ("Failed: client_"));
+    return;
+  }
+
+  CHECK_ARGS(args, kServerName, std::string, out)
+
+  const std::string& server_name = args.get(kServerName).get<std::string>();
+  auto result = client_->UnsubscribeServer(server_name);
+  if (!result) {
+    LogAndReportError(result, &out);
+    return;
+  }
+
+  ReportSuccess(out);
+}
+
 void MediaControllerInstance::MediaControllerPlaylistAddItem(const picojson::value& args,
                                                              picojson::object& out) {
   ScopeLogger();
index 5757c94..66b081f 100644 (file)
@@ -65,12 +65,25 @@ class MediaControllerInstance : public common::ParsedInstance {
   void MediaControllerServerUpdatePlaybackItem(const picojson::value& args, picojson::object& out);
   void MediaControllerServerGetAllPlaylists(const picojson::value& args, picojson::object& out);
   void MediaControllerServerUpdateIconURI(const picojson::value& args, picojson::object& out);
+  void MediaControllerServerSavePlaybackAbilities(const picojson::value& args,
+                                                  picojson::object& out);
+  void MediaControllerServerSetSimpleAbility(const picojson::value& args, picojson::object& out);
 
   // client
   void MediaControllerManagerGetClient(const picojson::value& args, picojson::object& out);
   void MediaControllerClientFindServers(const picojson::value& args, picojson::object& out);
   void MediaControllerClientGetLatestServerInfo(const picojson::value& args, picojson::object& out);
   void MediaControllerClientGetPlaybackInfo(const picojson::value& args, picojson::object& out);
+  void MediaControllerClientGetPlaybackAbility(const picojson::value& args, picojson::object& out);
+  void MediaControllerClientGetSimpleAbility(const picojson::value& args, picojson::object& out);
+  void MediaControllerClientAddAbilityChangeListener(const picojson::value& args,
+                                                     picojson::object& out);
+  void MediaControllerClientRemoveAbilityChangeListener(const picojson::value& args,
+                                                        picojson::object& out);
+  void MediaControllerClientFindSubscribedServers(const picojson::value& args,
+                                                  picojson::object& out);
+
+  // serverInfo
   void MediaControllerServerInfoSendPlaybackState(const picojson::value& args,
                                                   picojson::object& out);
   void MediaControllerServerInfoSendPlaybackPosition(const picojson::value& args,
@@ -98,6 +111,10 @@ class MediaControllerInstance : public common::ParsedInstance {
   void MediaControllerServerInfoGetAllPlaylists(const picojson::value& args, picojson::object& out);
   void MediaControllerServerInfoGetIconURI(const picojson::value& args, picojson::object& out);
 
+  // abilities_info
+  void MediaControllerAbilitiesInfoSubscribe(const picojson::value& args, picojson::object& out);
+  void MediaControllerAbilitiesInfoUnsubscribe(const picojson::value& args, picojson::object& out);
+
   // playlist
   void MediaControllerPlaylistAddItem(const picojson::value& args, picojson::object& out);
   void MediaControllerPlaylistGetItems(const picojson::value& args, picojson::object& out);
index 37e6212..fb913d5 100644 (file)
@@ -40,7 +40,14 @@ MediaControllerServer::MediaControllerServer()
       shuffle_mode_(MC_SHUFFLE_MODE_OFF),
       repeat_mode_(MC_REPEAT_MODE_OFF),
       is_shuffle_mode_set_(false),
-      is_repeat_mode_set_(false) {
+      is_repeat_mode_set_(false),
+      playback_abilities_{MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED,
+                          MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED,
+                          MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED,
+                          MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED},
+      simple_abilities_{MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED,
+                        MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED,
+                        MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED} {
   ScopeLogger();
 }
 
@@ -659,6 +666,117 @@ PlatformResult MediaControllerServer::MediaControllerPlaylistGetItems(const std:
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
+PlatformResult MediaControllerServer::SetPlaybackAbility(const std::string& action,
+                                                         const std::string& support_str) {
+  ScopeLogger();
+  PlatformResult result = PlatformResult(ErrorCode::NO_ERROR);
+
+  mc_playback_action_e action_e;
+  // Currently MediaControllerPlaybackActionEnum does not have member "TOGGLE_PLAY_PAUSE".
+  // It should be fixed in the future.
+  if ("TOGGLE_PLAY_PAUSE" == action) {
+    action_e = MC_PLAYBACK_ACTION_TOGGLE_PLAY_PAUSE;
+  } else {
+    result = types::MediaControllerPlaybackActionEnum.getValue(action, &action_e);
+    if (!result) {
+      return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of playback action",
+                                ("MediaControllerPlaybackActionEnum.getValue() failed, error: %s",
+                                 result.message().c_str()));
+    }
+  }
+
+  mc_ability_support_e support;
+  result = types::MediaControllerAbilitySupportEnum.getValue(support_str, &support);
+  if (!result) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability",
+                              ("MediaControllerAbilitySupportEnum.getValue() failed, error: %s",
+                               result.message().c_str()));
+  }
+
+  if (support == playback_abilities_.get(action)) {
+    LoggerD("No change in playback ability support, skipping");
+    return PlatformResult(ErrorCode::NO_ERROR);
+  }
+
+  int ret = mc_server_set_playback_ability(handle_, action_e, support);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(
+        ErrorCode::UNKNOWN_ERR, "Error setting playback ability",
+        ("mc_server_set_playback_ability() error: %d, message: %s", ret, get_error_message(ret)));
+  }
+
+  playback_abilities_.set(action, support);
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+PlatformResult MediaControllerServer::SavePlaybackAbilities(const picojson::value& abilities) {
+  ScopeLogger();
+
+  // Currently MediaControllerPlaybackActionEnum does not have member "TOGGLE_PLAY_PAUSE".
+  // It should be fixed in the future.
+  PlatformResult result = SetPlaybackAbility("TOGGLE_PLAY_PAUSE",
+                                             abilities.get("TOGGLE_PLAY_PAUSE").get<std::string>());
+  if (!result) {
+    return result;
+  }
+
+  for (auto action : types::MediaControllerPlaybackActionEnum) {
+    const std::string& action_str = action.first.c_str();
+    const std::string& support_str = abilities.get(action_str).get<std::string>();
+    result = SetPlaybackAbility(action_str, support_str);
+    if (!result) {
+      return result;
+    }
+  }
+
+  int ret = mc_server_update_playback_ability(handle_);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error updating playback ability",
+                              ("mc_server_update_playback_ability() error: %d, message: %s", ret,
+                               get_error_message(ret)));
+  }
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
+PlatformResult MediaControllerServer::SetSimpleAbility(const std::string& ability_type,
+                                                       const std::string& support_str) {
+  ScopeLogger();
+
+  mc_ability_support_e support_e;
+  PlatformResult result =
+      types::MediaControllerAbilitySupportEnum.getValue(support_str, &support_e);
+  if (!result) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability",
+                              ("MediaControllerAbilitySupportEnum.getValue() failed, error: %s",
+                               result.message().c_str()));
+  }
+
+  mc_ability_support_e current_ability = simple_abilities_.get(ability_type);
+
+  if (support_e == current_ability) {
+    LoggerD("No change in ability support, skipping");
+    return PlatformResult(ErrorCode::NO_ERROR);
+  }
+
+  mc_ability_e ability_e;
+  result = types::MediaControllerSimpleAbilityEnum.getValue(ability_type, &ability_e);
+  if (!result) {
+    return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability",
+                              ("MediaControllerSimpleAbilityEnum.getValue() failed, error: %s",
+                               result.message().c_str()));
+  }
+
+  int ret = mc_server_set_ability_support(handle_, ability_e, support_e);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(
+        ErrorCode::UNKNOWN_ERR, "Error setting ability",
+        ("mc_server_set_ability_support() error: %d, message: %s", ret, get_error_message(ret)));
+  }
+
+  simple_abilities_.set(ability_type, support_e);
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
 PlatformResult MediaControllerServer::SetChangeRequestPlaybackInfoListener(
     const JsonCallback& callback) {
   ScopeLogger();
index fc5a3bb..2de7696 100644 (file)
 namespace extension {
 namespace mediacontroller {
 
+namespace {
+const char* kPlay = "PLAY";
+const char* kPause = "PAUSE";
+const char* kStop = "STOP";
+const char* kNext = "NEXT";
+const char* kPrev = "PREV";
+const char* kForward = "FORWARD";
+const char* kRewind = "REWIND";
+const char* kTogglePlayPause = "TOGGLE_PLAY_PAUSE";
+
+const char* kPlaybackPosition = "PLAYBACK_POSITION";
+const char* kShuffle = "SHUFFLE";
+const char* kRepeat = "REPEAT";
+const char* kPlaylist = "PLAYLIST";
+const char* kClientCustom = "CLIENT_CUSTOM";
+const char* kSearch = "SEARCH";
+
+}  // namespace
+
+struct playback_abilities {
+  mc_ability_support_e play_;
+  mc_ability_support_e pause_;
+  mc_ability_support_e stop_;
+  mc_ability_support_e next_;
+  mc_ability_support_e prev_;
+  mc_ability_support_e forward_;
+  mc_ability_support_e rewind_;
+  mc_ability_support_e toggle_play_pause_;
+
+  mc_ability_support_e get(const std::string& ability) {
+    if (kPlay == ability) {
+      return play_;
+    }
+    if (kPause == ability) {
+      return pause_;
+    }
+    if (kStop == ability) {
+      return stop_;
+    }
+    if (kNext == ability) {
+      return next_;
+    }
+    if (kPrev == ability) {
+      return prev_;
+    }
+    if (kForward == ability) {
+      return forward_;
+    }
+    if (kRewind == ability) {
+      return rewind_;
+    }
+    if (kTogglePlayPause == ability) {
+      return toggle_play_pause_;
+    }
+    return MC_ABILITY_SUPPORTED_UNDECIDED;
+  }
+
+  void set(const std::string& action, mc_ability_support_e val) {
+    if (kPlay == action) {
+      play_ = val;
+    }
+    if (kPause == action) {
+      pause_ = val;
+    }
+    if (kStop == action) {
+      stop_ = val;
+    }
+    if (kNext == action) {
+      next_ = val;
+    }
+    if (kPrev == action) {
+      prev_ = val;
+    }
+    if (kForward == action) {
+      forward_ = val;
+    }
+    if (kRewind == action) {
+      rewind_ = val;
+    }
+    if (kTogglePlayPause == action) {
+      toggle_play_pause_ = val;
+    }
+  }
+};
+
+struct simple_abilities {
+  mc_ability_support_e playback_position_;
+  mc_ability_support_e shuffle_;
+  mc_ability_support_e repeat_;
+  mc_ability_support_e playlist_;
+  mc_ability_support_e custom_command_;
+  mc_ability_support_e search_;
+
+  mc_ability_support_e get(const std::string& ability) {
+    if (kPlaybackPosition == ability) {
+      return playback_position_;
+    }
+    if (kShuffle == ability) {
+      return shuffle_;
+    }
+    if (kRepeat == ability) {
+      return repeat_;
+    }
+    if (kPlaylist == ability) {
+      return playlist_;
+    }
+    if (kClientCustom == ability) {
+      return custom_command_;
+    }
+    if (kSearch == ability) {
+      return search_;
+    }
+    return MC_ABILITY_SUPPORTED_UNDECIDED;
+  }
+
+  void set(const std::string& ability, mc_ability_support_e val) {
+    if (kPlaybackPosition == ability) {
+      playback_position_ = val;
+    }
+    if (kShuffle == ability) {
+      shuffle_ = val;
+    }
+    if (kRepeat == ability) {
+      repeat_ = val;
+    }
+    if (kPlaylist == ability) {
+      playlist_ = val;
+    }
+    if (kClientCustom == ability) {
+      custom_command_ = val;
+    }
+    if (kSearch == ability) {
+      search_ = val;
+    }
+  }
+};
+
 class MediaControllerServer {
  public:
   MediaControllerServer();
@@ -64,6 +201,11 @@ class MediaControllerServer {
   common::PlatformResult MediaControllerPlaylistGetItems(const std::string& name,
                                                          picojson::array* items);
   common::PlatformResult UpdateIconURI(const char* icon_uri);
+  common::PlatformResult SetPlaybackAbility(const std::string& action,
+                                            const std::string& support_str);
+  common::PlatformResult SavePlaybackAbilities(const picojson::value& abilities);
+  common::PlatformResult SetSimpleAbility(const std::string& ability_type,
+                                          const std::string& support_str);
 
  private:
   mc_server_h handle_;
@@ -77,6 +219,8 @@ class MediaControllerServer {
   mc_repeat_mode_e repeat_mode_;
   bool is_shuffle_mode_set_;
   bool is_repeat_mode_set_;
+  playback_abilities playback_abilities_;
+  simple_abilities simple_abilities_;
 
   JsonCallback command_listener_;
   std::map<std::string, mc_playlist_h> playlist_handle_map_;
index 7df8487..8bf61a7 100644 (file)
@@ -95,6 +95,23 @@ const common::PlatformEnum<mc_search_category_e> MediaControllerSearchCategoryEn
     {"GENRE", MC_SEARCH_GENRE},
     {"TPO", MC_SEARCH_TPO}};
 
+const common::PlatformEnum<mc_ability_support_e> MediaControllerAbilitySupportEnum{
+    {"YES", MC_ABILITY_SUPPORTED_YES},
+    {"NO", MC_ABILITY_SUPPORTED_NO},
+    {"UNDECIDED", MC_ABILITY_SUPPORTED_UNDECIDED}};
+
+const common::PlatformEnum<mc_subscription_type_e> MediaControllerSubscriptionTypeEnum{
+    {"PLAYBACK_ABILITY", MC_SUBSCRIPTION_TYPE_PLAYBACK_ABILITY},
+    {"SIMPLE_ABILITIES", MC_SUBSCRIPTION_TYPE_ABILITY_SUPPORT}};
+
+const common::PlatformEnum<mc_ability_e> MediaControllerSimpleAbilityEnum{
+    {"PLAYBACK_POSITION", MC_ABILITY_PLAYBACK_POSITION},
+    {"SHUFFLE", MC_ABILITY_SHUFFLE},
+    {"REPEAT", MC_ABILITY_REPEAT},
+    {"PLAYLIST", MC_ABILITY_PLAYLIST},
+    {"CLIENT_CUSTOM", MC_ABILITY_CLIENT_CUSTOM},
+    {"SEARCH", MC_ABILITY_SEARCH}};
+
 PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state) {
   ScopeLogger();
 
@@ -200,6 +217,41 @@ PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metad
   return PlatformResult(ErrorCode::NO_ERROR);
 }
 
+PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h, const std::string& action,
+                                      std::string* ability_str) {
+  ScopeLogger();
+
+  mc_ability_support_e ability_e;
+  mc_playback_action_e action_e;
+  PlatformResult result = PlatformResult(ErrorCode::NO_ERROR);
+
+  if ("TOGGLE_PLAY_PAUSE" == action) {
+    action_e = MC_PLAYBACK_ACTION_TOGGLE_PLAY_PAUSE;
+  } else {
+    result = MediaControllerPlaybackActionEnum.getValue(action, &action_e);
+    if (!result) {
+      LoggerE("MediaControllerPlaybackActionEnum.getValue() failed, error: %s",
+              result.message().c_str());
+      return result;
+    }
+  }
+
+  int ret = mc_playback_action_is_supported(ability_h, action_e, &ability_e);
+  if (MEDIA_CONTROLLER_ERROR_NONE != ret) {
+    return LogAndCreateResult(
+        ErrorCode::UNKNOWN_ERR, "Error checking playback action ability support",
+        ("mc_playback_action_is_supported() error: %d, message: %s", ret, get_error_message(ret)));
+  }
+  result = MediaControllerAbilitySupportEnum.getName(ability_e, ability_str);
+  if (!result) {
+    LoggerE("MediaControllerAbilitySupportEnum.getName() failed, error: %s",
+            result.message().c_str());
+    return result;
+  }
+
+  return PlatformResult(ErrorCode::NO_ERROR);
+}
+
 }  // types
 
 PlatformResult utils::GetAllPlaylists(const std::string& app_id, picojson::array* playlists) {
index 18fe21b..6d98e88 100644 (file)
@@ -37,6 +37,8 @@ common::PlatformResult ConvertContentAgeRating(mc_playback_h playback_h, std::st
 common::PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* position);
 common::PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metadata);
 common::PlatformResult ConvertContentType(mc_playback_h playback_h, std::string* contentType);
+common::PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h,
+                                              const std::string& action, std::string* ability_str);
 
 extern const common::PlatformEnum<mc_server_state_e> MediaControllerServerStateEnum;
 extern const common::PlatformEnum<mc_playback_states_e> MediaControllerPlaybackStateEnum;
@@ -46,6 +48,9 @@ extern const common::PlatformEnum<mc_repeat_mode_e> MediaControllerRepeatModeEnu
 extern const common::PlatformEnum<mc_content_age_rating_e> MediaControllerContentAgeRatingEnum;
 extern const common::PlatformEnum<mc_content_type_e> MediaControllerContentTypeEnum;
 extern const common::PlatformEnum<mc_search_category_e> MediaControllerSearchCategoryEnum;
+extern const common::PlatformEnum<mc_ability_support_e> MediaControllerAbilitySupportEnum;
+extern const common::PlatformEnum<mc_subscription_type_e> MediaControllerSubscriptionTypeEnum;
+extern const common::PlatformEnum<mc_ability_e> MediaControllerSimpleAbilityEnum;
 
 }  // namespace types