c51547c7ee40e67d0b044c3e262ac518aadca68f
[platform/core/api/webapi-plugins.git] / src / mediacontroller / mediacontroller_api.js
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16
17 var _global = window || global || {};
18
19 var utils_ = xwalk.utils;
20 var type_ = utils_.type;
21 var converter_ = utils_.converter;
22 var validator_ = utils_.validator;
23 var types_ = validator_.Types;
24 var native_ = new xwalk.utils.NativeManager(extension);
25
26 function ListenerManager(native, listenerName, handle) {
27   this.listeners = {};
28   this.listenerNameToIds = {};
29   this.listenerIdToName = {};
30   this.nextId = 1;
31   this.nativeSet = false;
32   this.native = native;
33   this.listenerName = listenerName;
34   this.handle = handle || function(msg, listener, watchId) {};
35 }
36
37 ListenerManager.prototype.addListener = function(callback) {
38   var id = this.nextId;
39   if (!this.nativeSet) {
40     this.native.addListener(this.listenerName, function(msg) {
41       for (var watchId in this.listeners) {
42         if (this.listeners.hasOwnProperty(watchId)) {
43           var stop = this.handle(msg, this.listeners[watchId], watchId);
44           if (stop) {
45             break;
46           }
47         }
48       }
49     }.bind(this));
50
51     this.nativeSet = true;
52   }
53
54   this.listeners[id] = callback;
55   ++this.nextId;
56   return id;
57 };
58
59 ListenerManager.prototype.addServerInfoListener = function(callback, name) {
60   var id = this.nextId;
61   if (!this.nativeSet) {
62     this.native.addListener(this.listenerName, function(msg) {
63       if (this.listenerNameToIds.hasOwnProperty(msg.name)) {
64         var cbArray = this.listenerNameToIds[msg.name];
65         for (var i = 0; i < cbArray.length; ++i) {
66           var watchId = cbArray[i];
67           this.handle(msg, this.listeners[watchId], watchId);
68         }
69       }
70     }.bind(this));
71
72     this.nativeSet = true;
73   }
74
75   this.listenerIdToName[id] = name;
76   if (this.listenerNameToIds[name]) {
77     this.listenerNameToIds[name].push(id);
78   } else {
79     this.listenerNameToIds[name] = [id];
80   }
81   this.listeners[id] = callback;
82   ++this.nextId;
83   return id;
84 };
85
86 ListenerManager.prototype.removeListener = function(watchId) {
87   if (this.listeners.hasOwnProperty(watchId)) {
88     delete this.listeners[watchId];
89   }
90 };
91
92 function removeArrayElement(arr, elem) {
93   var index = arr.indexOf(elem);
94   if (index !== -1) {
95     arr.splice(index, 1);
96   }
97 }
98
99 ListenerManager.prototype.removeServerInfoListener = function(watchId) {
100   this.removeListener(watchId);
101   if (this.listenerIdToName.hasOwnProperty(watchId)) {
102     var name = this.listenerIdToName[watchId];
103     removeArrayElement(this.listenerNameToIds[name], watchId);
104     delete this.listenerIdToName[watchId];
105   }
106 };
107
108 var ServerCommandListener = new ListenerManager(native_, '_ServerCommandListener', function(msg, listener) {
109   var data = undefined;
110   data = listener(msg.clientName, msg.command, msg.data);
111
112   if (type_.isUndefined(data)) {
113    data = null;
114   }
115
116   var nativeData = {
117     clientName: msg.clientName,
118     replyId: msg.replyId,
119     data: data
120   };
121
122   var result = native_.callSync('MediaControllerServer_replyCommand', nativeData);
123   if (native_.isFailure(result)) {
124     throw native_.getErrorObject(result);
125   }
126 });
127
128 var ReplyCommandListener = new ListenerManager(native_, '_ReplyCommandListener', function(msg, listener, watchId) {
129   if (msg.replyId === watchId) {
130     listener(msg.data);
131     this.removeListener(watchId);
132     return true;
133   }
134
135   return false;
136 });
137
138 var ServerPlaybackInfoListener = new ListenerManager(native_, '_ServerPlaybackInfoListener', function(msg, listener) {
139   if (msg.action === 'onplaybackstaterequest') {
140     listener[msg.action](msg.state);
141   }
142   if (msg.action === 'onplaybackpositionrequest') {
143     listener[msg.action](msg.position);
144   }
145   if (msg.action === 'onshufflemoderequest' || msg.action === 'onrepeatmoderequest') {
146     listener[msg.action](msg.mode);
147   }
148 });
149
150 var ServerInfoStatusListener = new ListenerManager(native_, '_ServerInfoStatusListener', function(msg, listener) {
151   listener(msg.state);
152 });
153
154 var ServerInfoPlaybackInfoListener = new ListenerManager(native_, '_ServerInfoPlaybackInfoListener', function(msg, listener) {
155   if (msg.action === 'onplaybackchanged') {
156     listener[msg.action](msg.state, msg.position);
157   }
158   if (msg.action === 'onshufflemodechanged' || msg.action === 'onrepeatmodechanged') {
159     listener[msg.action](msg.mode);
160   }
161   if (msg.action === 'onmetadatachanged') {
162     listener[msg.action](new MediaControllerMetadata(msg.metadata));
163   }
164 });
165
166 var EditManager = function() {
167   this.isAllowed = false;
168 };
169
170 EditManager.prototype.allow = function() {
171   this.isAllowed = true;
172 };
173
174 EditManager.prototype.disallow = function() {
175   this.isAllowed = false;
176 };
177
178 var edit_ = new EditManager();
179
180
181 var MediaControllerServerState = {
182   ACTIVE: 'ACTIVE',
183   INACTIVE: 'INACTIVE'
184 };
185
186 var MediaControllerPlaybackState = {
187   PLAY: 'PLAY',
188   PAUSE: 'PAUSE',
189   STOP: 'STOP',
190   NEXT: 'NEXT',
191   PREV: 'PREV',
192   FORWARD: 'FORWARD',
193   REWIND: 'REWIND'
194 };
195
196
197 function MediaControllerManager() {}
198
199 MediaControllerManager.prototype.getClient = function() {
200   var result = native_.callSync('MediaControllerManager_getClient', {});
201
202   if (native_.isFailure(result)) {
203     throw native_.getErrorObject(result);
204   }
205
206   return new MediaControllerClient(native_.getResultObject(result));
207 };
208
209 MediaControllerManager.prototype.createServer = function() {
210   var result = native_.callSync('MediaControllerManager_createServer', {});
211   if (native_.isFailure(result)) {
212     throw native_.getErrorObject(result);
213   }
214
215   return new MediaControllerServer(native_.getResultObject(result));
216 };
217
218
219 var MediaControllerMetadata = function(data) {
220   var _title = '';
221   var _artist = '';
222   var _album = '';
223   var _author = '';
224   var _genre = '';
225   var _duration = '';
226   var _date = '';
227   var _copyright = '';
228   var _description = '';
229   var _trackNum = '';
230   var _picture = '';
231   Object.defineProperties(this, {
232     title: {
233       get: function() {return _title;},
234       set: function(v) {_title = converter_.toString(v)},
235       enumerable: true
236     },
237     artist: {
238       get: function() {return _artist;},
239       set: function(v) {_artist = converter_.toString(v)},
240       enumerable: true
241     },
242     album: {
243       get: function() {return _album;},
244       set: function(v) {_album = converter_.toString(v)},
245       enumerable: true
246     },
247     author: {
248       get: function() {return _author;},
249       set: function(v) {_author = converter_.toString(v)},
250       enumerable: true
251     },
252     genre: {
253       get: function() {return _genre;},
254       set: function(v) {_genre = converter_.toString(v)},
255       enumerable: true
256     },
257     duration: {
258       get: function() {return _duration;},
259       set: function(v) {_duration = converter_.toString(v)},
260       enumerable: true
261     },
262     date: {
263       get: function() {return _date;},
264       set: function(v) {_date = converter_.toString(v)},
265       enumerable: true
266     },
267     copyright: {
268       get: function() {return _copyright;},
269       set: function(v) {_copyright = converter_.toString(v)},
270       enumerable: true
271     },
272     description: {
273       get: function() {return _description;},
274       set: function(v) {_description = converter_.toString(v)},
275       enumerable: true
276     },
277     trackNum: {
278       get: function() {return _trackNum;},
279       set: function(v) {_trackNum = converter_.toString(v)},
280       enumerable: true
281     },
282     picture: {
283       get: function() {return _picture;},
284       set: function(v) {_picture = converter_.toString(v)},
285       enumerable: true
286     }
287   });
288
289   if (data instanceof _global.Object) {
290     for (var prop in data) {
291       if (data.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
292         this[prop] = data[prop];
293       }
294     }
295   }
296 };
297
298 var MediaControllerPlaybackInfo = function(data) {
299   var _state = 'STOP';
300   var _position = 0;
301   var _shuffleMode = false;
302   var _repeatMode = false;
303   var _metadata = new MediaControllerMetadata();
304   Object.defineProperties(this, {
305     state: {
306       get: function() {
307         return _state;
308       },
309       set: function(v) {
310         _state = edit_.isAllowed && v ? v : _state;
311       },
312       enumerable: true
313     },
314     position: {
315       get: function() {
316         return _position;
317       },
318       set: function(v) {
319         _position = edit_.isAllowed ? converter_.toLong(v) : _position;
320       },
321       enumerable: true
322     },
323     shuffleMode: {
324       get: function() {
325         return _shuffleMode;
326       },
327       set: function(v) {
328         _shuffleMode = edit_.isAllowed ? converter_.toBoolean(v) : _shuffleMode;
329       },
330       enumerable: true
331     },
332     repeatMode: {
333       get: function() {
334         return _repeatMode;
335       },
336       set: function(v) {
337         _repeatMode = edit_.isAllowed ? converter_.toBoolean(v) : _repeatMode;
338       },
339       enumerable: true
340     },
341     metadata: {
342       get: function() {
343         return _metadata;
344       },
345       set: function(v) {
346         _metadata = edit_.isAllowed && v ? new MediaControllerMetadata(v) : _metadata;
347       },
348       enumerable: true
349     }
350   });
351
352   if (data instanceof _global.Object) {
353     for (var prop in data) {
354       if (data.hasOwnProperty(prop) && this.hasOwnProperty(prop)) {
355         this[prop] = data[prop];
356       }
357     }
358   }
359 };
360
361 function MediaControllerServer(data) {
362   Object.defineProperties(this, {
363     playbackInfo: {
364       value: new MediaControllerPlaybackInfo(data),
365       writable: false,
366       enumerable: true
367     }
368   });
369 }
370
371 MediaControllerServer.prototype.updatePlaybackState = function(state) {
372   var args = validator_.validateArgs(arguments, [
373     {name: 'state', type: types_.ENUM, values: Object.keys(MediaControllerPlaybackState)}
374   ]);
375
376   var data = {
377     state: args.state
378   };
379
380   var result = native_.callSync('MediaControllerServer_updatePlaybackState', data);
381
382   if (native_.isFailure(result)) {
383     throw native_.getErrorObject(result);
384   }
385
386   edit_.allow();
387   this.playbackInfo.state = args.state;
388   edit_.disallow();
389 };
390
391 MediaControllerServer.prototype.updatePlaybackPosition = function(position) {
392   var args = validator_.validateArgs(arguments, [
393     {name: 'position', type: types_.UNSIGNED_LONG_LONG}
394   ]);
395
396   var data = {
397     position: args.position
398   };
399
400   var result = native_.callSync('MediaControllerServer_updatePlaybackPosition', data);
401
402   if (native_.isFailure(result)) {
403     throw native_.getErrorObject(result);
404   }
405
406   edit_.allow();
407   this.playbackInfo.position = args.position;
408   edit_.disallow();
409 };
410
411 MediaControllerServer.prototype.updateShuffleMode = function(mode) {
412   var args = validator_.validateArgs(arguments, [
413     {name: 'mode', type: types_.BOOLEAN}
414   ]);
415
416   var data = {
417     mode: args.mode
418   };
419
420   var result = native_.callSync('MediaControllerServer_updateShuffleMode', data);
421
422   if (native_.isFailure(result)) {
423     throw native_.getErrorObject(result);
424   }
425
426   edit_.allow();
427   this.playbackInfo.shuffleMode = args.mode;
428   edit_.disallow();
429 };
430
431 MediaControllerServer.prototype.updateRepeatMode = function(mode) {
432   var args = validator_.validateArgs(arguments, [
433     {name: 'mode', type: types_.BOOLEAN}
434   ]);
435
436   var data = {
437     mode: args.mode
438   };
439
440   var result = native_.callSync('MediaControllerServer_updateRepeatMode', data);
441
442   if (native_.isFailure(result)) {
443     throw native_.getErrorObject(result);
444   }
445
446   edit_.allow();
447   this.playbackInfo.repeatMode = args.mode;
448   edit_.disallow();
449 };
450
451 MediaControllerServer.prototype.updateMetadata = function(metadata) {
452   var args = validator_.validateArgs(arguments, [
453     {name: 'metadata', type: types_.PLATFORM_OBJECT, values: MediaControllerMetadata}
454   ]);
455
456   var data = {
457     metadata: args.metadata
458   };
459
460   var result = native_.callSync('MediaControllerServer_updateMetadata', data);
461
462   if (native_.isFailure(result)) {
463     throw native_.getErrorObject(result);
464   }
465
466   edit_.allow();
467   this.playbackInfo.metadata = args.metadata;
468   edit_.disallow();
469 };
470
471 MediaControllerServer.prototype.addChangeRequestPlaybackInfoListener = function(listener) {
472   var args = validator_.validateArgs(arguments, [{
473     name: 'listener',
474     type: types_.LISTENER,
475     values: [
476       'onplaybackstaterequest',
477       'onplaybackpositionrequest',
478       'onshufflemoderequest',
479       'onrepeatmoderequest'
480     ]
481   }]);
482
483   if (type_.isEmptyObject(ServerPlaybackInfoListener.listeners)) {
484     var result = native_.callSync('MediaControllerServer_addChangeRequestPlaybackInfoListener', {
485       listenerId: ServerPlaybackInfoListener.listenerName
486     });
487     if (native_.isFailure(result)) {
488       throw native_.getErrorObject(result);
489     }
490   }
491
492   return ServerPlaybackInfoListener.addListener(args.listener);
493 };
494
495 MediaControllerServer.prototype.removeChangeRequestPlaybackInfoListener = function(watchId) {
496   var args = validator_.validateArgs(arguments, [
497     {name: 'watchId', type: types_.LONG}
498   ]);
499
500   ServerPlaybackInfoListener.removeListener(args.watchId);
501
502   if (type_.isEmptyObject(ServerPlaybackInfoListener.listeners)) {
503     native_.callSync('MediaControllerServer_removeCommandListener');
504   }
505 };
506
507 MediaControllerServer.prototype.addCommandListener = function(listener) {
508   var args = validator_.validateArgs(arguments, [
509     {name: 'listener', type: types_.FUNCTION}
510   ]);
511
512   if (type_.isEmptyObject(ServerCommandListener.listeners)) {
513     var result = native_.callSync('MediaControllerServer_addCommandListener', {
514       listenerId: ServerCommandListener.listenerName
515     });
516     if (native_.isFailure(result)) {
517       throw native_.getErrorObject(result);
518     }
519   }
520
521   return ServerCommandListener.addListener(args.listener);
522 };
523
524 MediaControllerServer.prototype.removeCommandListener = function(watchId) {
525   var args = validator_.validateArgs(arguments, [
526     {name: 'watchId', type: types_.LONG}
527   ]);
528
529   ServerCommandListener.removeListener(args.watchId);
530
531   if (type_.isEmptyObject(ServerCommandListener.listeners)) {
532     native_.callSync('MediaControllerServer_removeCommandListener');
533   }
534 };
535
536
537 function MediaControllerClient() {}
538
539 MediaControllerClient.prototype.findServers = function(successCallback, errorCallback) {
540   var args = validator_.validateArgs(arguments, [
541     {name: 'successCallback', type: types_.FUNCTION},
542     {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}
543   ]);
544
545   var callback = function(result) {
546     if (native_.isFailure(result)) {
547       native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
548       return;
549     }
550     var info = [];
551     var data = native_.getResultObject(result);
552     for (var i = 0; i < data.length; i++) {
553       info.push(new MediaControllerServerInfo(data[i]));
554     }
555     native_.callIfPossible(args.successCallback, info);
556   };
557
558   native_.call('MediaControllerClient_findServers', {}, callback);
559 };
560
561 MediaControllerClient.prototype.getLatestServerInfo = function() {
562
563   var result = native_.callSync('MediaControllerClient_getLatestServerInfo', {});
564
565   if (native_.isFailure(result)) {
566     throw native_.getErrorObject(result);
567   }
568
569   var serverInfo = native_.getResultObject(result);
570   if (serverInfo) {
571     if ('NONE' === serverInfo.state) {
572       serverInfo.state = 'INACTIVE';
573     }
574     serverInfo = new MediaControllerServerInfo(serverInfo);
575   }
576   return serverInfo;
577 };
578
579
580 function MediaControllerServerInfo(data) {
581   Object.defineProperties(this, {
582     name: {
583       value: data.name,
584       writable: false,
585       enumerable: true
586     },
587     state: {
588       value: data.state,
589       writable: false,
590       enumerable: true
591     },
592     playbackInfo: {
593       get: function () {
594         var result = native_.callSync('MediaControllerClient_getPlaybackInfo', {name: this.name});
595         if (native_.isFailure(result)) {
596           throw new native_.getErrorObject(result);
597         }
598         edit_.allow();
599         var data = native_.getResultObject(result);
600         var playbackInfo = new MediaControllerPlaybackInfo(data);
601         edit_.disallow();
602
603         return playbackInfo;
604       }.bind(this),
605       set: function() {},
606       enumerable: true
607     }
608   });
609 }
610
611
612 MediaControllerServerInfo.prototype.sendPlaybackState = function(state, successCallback, errorCallback) {
613   var args = validator_.validateArgs(arguments, [
614     {name: 'state', type: types_.ENUM, values: Object.keys(MediaControllerPlaybackState)},
615     {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true},
616     {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}
617   ]);
618
619   var data = {
620     name: this.name,
621     state: args.state
622   };
623
624   var callback = function(result) {
625     if (native_.isFailure(result)) {
626       native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
627       return;
628     }
629     native_.callIfPossible(args.successCallback);
630   };
631
632   native_.call('MediaControllerServerInfo_sendPlaybackState', data, callback);
633 };
634
635 MediaControllerServerInfo.prototype.sendPlaybackPosition = function(position, successCallback, errorCallback) {
636   var args = validator_.validateArgs(arguments, [
637     {name: 'position', type: types_.LONG_LONG},
638     {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true},
639     {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}
640   ]);
641
642   if (args.position < 0) {
643     throw new WebAPIException(WebAPIException.INVALID_VALUES_ERR);
644   }
645
646   var callback = function(result) {
647     if (native_.isFailure(result)) {
648       native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
649       return;
650     }
651     native_.callIfPossible(args.successCallback);
652   };
653
654   var data = {
655     position: args.position,
656     name: this.name
657   };
658
659   native_.call('MediaControllerServerInfo_sendPlaybackPosition', data, callback);
660 };
661
662 MediaControllerServerInfo.prototype.sendShuffleMode = function(mode, successCallback, errorCallback) {
663   var args = validator_.validateArgs(arguments, [
664     {name: 'mode', type: types_.BOOLEAN},
665     {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true},
666     {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}
667   ]);
668
669   var callback = function(result) {
670     if (native_.isFailure(result)) {
671       native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
672       return;
673     }
674     native_.callIfPossible(args.successCallback);
675   };
676
677   var data = {
678     mode: args.mode,
679     name: this.name
680   };
681   native_.call('MediaControllerServerInfo_sendShuffleMode', data, callback);
682 };
683
684 MediaControllerServerInfo.prototype.sendRepeatMode = function(mode, successCallback, errorCallback) {
685   var args = validator_.validateArgs(arguments, [
686     {name: 'mode', type: types_.BOOLEAN},
687     {name: 'successCallback', type: types_.FUNCTION, optional: true, nullable: true},
688     {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}
689   ]);
690
691   var callback = function(result) {
692     if (native_.isFailure(result)) {
693       native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
694       return;
695     }
696     native_.callIfPossible(args.successCallback);
697   };
698
699   var data = {
700     mode: args.mode,
701     name: this.name
702   };
703   native_.call('MediaControllerServerInfo_sendRepeatMode', data, callback);
704 };
705
706 MediaControllerServerInfo.prototype.sendCommand = function(command, data, successCallback, errorCallback) {
707   var args = validator_.validateArgs(arguments, [
708     {name: 'command', type: types_.STRING},
709     {name: 'data', type: types_.DICTIONARY},
710     {name: 'successCallback', type: types_.FUNCTION},
711     {name: 'errorCallback', type: types_.FUNCTION, optional: true, nullable: true}
712   ]);
713
714   var nativeData = {
715     command: args.command,
716     data: args.data,
717     name: this.name
718   };
719
720   var replyId = ReplyCommandListener.addListener(successCallback);
721
722   nativeData.replyId = replyId;
723   nativeData.listenerId = ReplyCommandListener.listenerName;
724   var callback = function(result) {
725     if (native_.isFailure(result)) {
726       native_.callIfPossible(args.errorCallback, native_.getErrorObject(result));
727       return;
728     }
729     args.successCallback(native_.getResultObject(result));
730   };
731
732   native_.call('MediaControllerServerInfo_sendCommand', nativeData, callback);
733 };
734
735 MediaControllerServerInfo.prototype.addServerStatusChangeListener = function(listener) {
736   var args = validator_.validateArgs(arguments, [
737     {name: 'listener', type: types_.FUNCTION}
738   ]);
739
740   if (type_.isEmptyObject(ServerInfoStatusListener.listeners)) {
741     var result = native_.callSync('MediaControllerServerInfo_addServerStatusChangeListener', {
742       listenerId: ServerInfoStatusListener.listenerName
743     });
744     if (native_.isFailure(result)) {
745       throw native_.getErrorObject(result);
746     }
747   }
748   return ServerInfoStatusListener.addServerInfoListener(args.listener, this.name);
749 };
750
751 MediaControllerServerInfo.prototype.removeServerStatusChangeListener = function(watchId) {
752   var args = validator_.validateArgs(arguments, [
753     {name: 'watchId', type: types_.LONG}
754   ]);
755
756   ServerInfoStatusListener.removeServerInfoListener(args.watchId);
757
758   if (type_.isEmptyObject(ServerInfoStatusListener.listeners)) {
759     native_.callSync('MediaControllerServerInfo_removeServerStatusChangeListener');
760   }
761 };
762
763 MediaControllerServerInfo.prototype.addPlaybackInfoChangeListener = function(listener) {
764   var args = validator_.validateArgs(arguments, [{
765     name: 'listener',
766     type: types_.LISTENER,
767     values: [
768       'onplaybackchanged',
769       'onshufflemodechanged',
770       'onrepeatmodechanged',
771       'onmetadatachanged'
772     ]
773   }]);
774
775   if (type_.isEmptyObject(ServerInfoPlaybackInfoListener.listeners)) {
776     var result = native_.callSync(
777         'MediaControllerServerInfo_addPlaybackInfoChangeListener', {
778           listenerId: ServerInfoPlaybackInfoListener.listenerName
779         });
780     if (native_.isFailure(result)) {
781       throw native_.getErrorObject(result);
782     }
783   }
784
785   return ServerInfoPlaybackInfoListener.addServerInfoListener(args.listener, this.name);
786 };
787
788 MediaControllerServerInfo.prototype.removePlaybackInfoChangeListener = function(watchId) {
789   var args = validator_.validateArgs(arguments, [
790     {name: 'watchId', type: types_.LONG}
791   ]);
792
793   ServerInfoPlaybackInfoListener.removeServerInfoListener(args.watchId);
794
795   if (type_.isEmptyObject(ServerInfoPlaybackInfoListener.listeners)) {
796     native_.callSync('MediaControllerServerInfo_removePlaybackInfoChangeListener');
797   }
798 };
799
800
801 exports = new MediaControllerManager();