1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.avrcp;
18 
19 import android.bluetooth.BluetoothDevice;
20 import android.bluetooth.BluetoothAvrcpPlayerSettings;
21 import com.android.bluetooth.Utils;
22 import java.util.ArrayList;
23 import java.util.Arrays;
24 import java.util.List;
25 import java.util.HashMap;
26 import android.util.Log;
27 import java.nio.charset.Charset;
28 import java.nio.ByteBuffer;
29 import android.media.session.PlaybackState;
30 import android.media.MediaMetadata;
31 /**
32  * Provides helper classes used by other AvrcpControllerClasses.
33  */
34 class AvrcpUtils {
35 
36     private static final String TAG = "AvrcpUtils";
37     /*
38      * First 2 apis are utility functions to converts values from AvrcpPlayerSettings defined
39      * in BluetoothAvrcpPlayerSettings to BT spec defined Id and Vals.
40      */
mapAttribIdValtoAvrcpPlayerSetting( byte attribId, byte attribVal)41     public static int mapAttribIdValtoAvrcpPlayerSetting( byte attribId, byte attribVal) {
42         if(AvrcpControllerConstants.VDBG) Log.d(TAG, "attribId: " + attribId + " attribVal: " + attribVal);
43         if (attribId == AvrcpControllerConstants.ATTRIB_EQUALIZER_STATUS) {
44             switch(attribVal) {
45             case AvrcpControllerConstants.EQUALIZER_STATUS_OFF:
46                 return BluetoothAvrcpPlayerSettings.STATE_OFF;
47             case AvrcpControllerConstants.EQUALIZER_STATUS_ON:
48                 return BluetoothAvrcpPlayerSettings.STATE_ON;
49             }
50         }
51         else if (attribId == AvrcpControllerConstants.ATTRIB_REPEAT_STATUS) {
52             switch(attribVal) {
53             case AvrcpControllerConstants.REPEAT_STATUS_ALL_TRACK_REPEAT:
54                 return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
55             case AvrcpControllerConstants.REPEAT_STATUS_GROUP_REPEAT:
56                 return BluetoothAvrcpPlayerSettings.STATE_GROUP;
57             case AvrcpControllerConstants.REPEAT_STATUS_OFF:
58                 return BluetoothAvrcpPlayerSettings.STATE_OFF;
59             case AvrcpControllerConstants.REPEAT_STATUS_SINGLE_TRACK_REPEAT:
60                 return BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK;
61             }
62         }
63         else if (attribId == AvrcpControllerConstants.ATTRIB_SCAN_STATUS) {
64             switch(attribVal) {
65             case AvrcpControllerConstants.SCAN_STATUS_ALL_TRACK_SCAN:
66                 return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
67             case AvrcpControllerConstants.SCAN_STATUS_GROUP_SCAN:
68                 return BluetoothAvrcpPlayerSettings.STATE_GROUP;
69             case AvrcpControllerConstants.SCAN_STATUS_OFF:
70                 return BluetoothAvrcpPlayerSettings.STATE_OFF;
71             }
72         }
73         else if (attribId == AvrcpControllerConstants.ATTRIB_SHUFFLE_STATUS) {
74             switch(attribVal) {
75             case AvrcpControllerConstants.SHUFFLE_STATUS_ALL_TRACK_SHUFFLE:
76                 return BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK;
77             case AvrcpControllerConstants.SHUFFLE_STATUS_GROUP_SHUFFLE:
78                 return BluetoothAvrcpPlayerSettings.STATE_GROUP;
79             case AvrcpControllerConstants.SHUFFLE_STATUS_OFF:
80                 return BluetoothAvrcpPlayerSettings.STATE_OFF;
81             }
82         }
83         return BluetoothAvrcpPlayerSettings.STATE_INVALID;
84     }
mapAvrcpPlayerSettingstoBTAttribVal(int mSetting, int mSettingVal)85     public static int mapAvrcpPlayerSettingstoBTAttribVal(int mSetting, int mSettingVal) {
86         if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) {
87             switch(mSettingVal) {
88             case BluetoothAvrcpPlayerSettings.STATE_OFF:
89                 return AvrcpControllerConstants.EQUALIZER_STATUS_OFF;
90             case BluetoothAvrcpPlayerSettings.STATE_ON:
91                 return AvrcpControllerConstants.EQUALIZER_STATUS_ON;
92             }
93         }
94         else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_REPEAT) {
95             switch(mSettingVal) {
96             case BluetoothAvrcpPlayerSettings.STATE_OFF:
97                 return AvrcpControllerConstants.REPEAT_STATUS_OFF;
98             case BluetoothAvrcpPlayerSettings.STATE_SINGLE_TRACK:
99                 return AvrcpControllerConstants.REPEAT_STATUS_SINGLE_TRACK_REPEAT;
100             case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
101                 return AvrcpControllerConstants.REPEAT_STATUS_ALL_TRACK_REPEAT;
102             case BluetoothAvrcpPlayerSettings.STATE_GROUP:
103                 return AvrcpControllerConstants.REPEAT_STATUS_GROUP_REPEAT;
104             }
105         }
106         else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) {
107             switch(mSettingVal) {
108             case BluetoothAvrcpPlayerSettings.STATE_OFF:
109                 return AvrcpControllerConstants.SHUFFLE_STATUS_OFF;
110             case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
111                 return AvrcpControllerConstants.SHUFFLE_STATUS_ALL_TRACK_SHUFFLE;
112             case BluetoothAvrcpPlayerSettings.STATE_GROUP:
113                 return AvrcpControllerConstants.SHUFFLE_STATUS_GROUP_SHUFFLE;
114             }
115         }
116         else if (mSetting == BluetoothAvrcpPlayerSettings.SETTING_SCAN) {
117             switch(mSettingVal) {
118             case BluetoothAvrcpPlayerSettings.STATE_OFF:
119                 return AvrcpControllerConstants.SCAN_STATUS_OFF;
120             case BluetoothAvrcpPlayerSettings.STATE_ALL_TRACK:
121                 return AvrcpControllerConstants.SCAN_STATUS_ALL_TRACK_SCAN;
122             case BluetoothAvrcpPlayerSettings.STATE_GROUP:
123                 return AvrcpControllerConstants.SCAN_STATUS_GROUP_SCAN;
124             }
125         }
126         return AvrcpControllerConstants.STATUS_INVALID;
127     }
128     /*
129      * This api converts btPlayStatus to PlaybackState
130      */
mapBtPlayStatustoPlayBackState(byte btPlayStatus, long btPlayPos)131     public static PlaybackState mapBtPlayStatustoPlayBackState(byte btPlayStatus, long btPlayPos) {
132         int mState = PlaybackState.STATE_NONE;
133         long position = btPlayPos;
134         float speed = 1;
135         switch(btPlayStatus) {
136             case AvrcpControllerConstants.PLAY_STATUS_STOPPED:
137                 mState = PlaybackState.STATE_STOPPED;
138                 position = 0;
139                 speed = 0;
140             break;
141             case AvrcpControllerConstants.PLAY_STATUS_PLAYING:
142                 mState = PlaybackState.STATE_PLAYING;
143             break;
144             case AvrcpControllerConstants.PLAY_STATUS_PAUSED:
145                 mState = PlaybackState.STATE_PAUSED;
146                 speed = 0;
147             break;
148             case AvrcpControllerConstants.PLAY_STATUS_FWD_SEEK:
149                 mState = PlaybackState.STATE_FAST_FORWARDING;
150                 speed = 3;
151             break;
152             case AvrcpControllerConstants.PLAY_STATUS_REV_SEEK:
153                 mState = PlaybackState.STATE_REWINDING;
154                 speed = -3;
155             break;
156         }
157         return new PlaybackState.Builder().setState(mState, position, speed).build();
158     }
159     /*
160      * This api converts meta info into MediaMetaData
161      */
getMediaMetaData(TrackInfo mTrackInfo)162     public static MediaMetadata getMediaMetaData(TrackInfo mTrackInfo) {
163         if(AvrcpControllerConstants.VDBG) Log.d(TAG, " TrackInfo " + mTrackInfo.toString());
164         MediaMetadata.Builder mMetaDataBuilder = new MediaMetadata.Builder();
165         mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_ARTIST,
166                 mTrackInfo.mArtistName);
167         mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_TITLE,
168                 mTrackInfo.mTrackTitle);
169         mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_ALBUM,
170                 mTrackInfo.mAlbumTitle);
171         mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_GENRE,
172                 mTrackInfo.mGenre);
173         mMetaDataBuilder.putLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER,
174                 mTrackInfo.mTrackNum);
175         mMetaDataBuilder.putLong(MediaMetadata.METADATA_KEY_NUM_TRACKS,
176                 mTrackInfo.mTotalTracks);
177         mMetaDataBuilder.putLong(MediaMetadata.METADATA_KEY_DURATION,
178                 mTrackInfo.mTrackLen);
179         mMetaDataBuilder.putString(MediaMetadata.METADATA_KEY_MEDIA_ID,
180                 String.valueOf(mTrackInfo.mItemUid));
181         return mMetaDataBuilder.build();
182     }
183     /*
184      * Display Apis
185      */
displayMetaData(MediaMetadata mMetaData)186     public static String displayMetaData(MediaMetadata mMetaData) {
187         StringBuffer sb = new StringBuffer();
188         /* this will only show artist, title and album */
189         sb.append(mMetaData.getDescription().toString() + " ");
190         if(mMetaData.containsKey(MediaMetadata.METADATA_KEY_GENRE))
191             sb.append(mMetaData.getString(MediaMetadata.METADATA_KEY_GENRE) + " ");
192         if(mMetaData.containsKey(MediaMetadata.METADATA_KEY_MEDIA_ID))
193             sb.append(mMetaData.getString(MediaMetadata.METADATA_KEY_MEDIA_ID) + " ");
194         if(mMetaData.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER))
195             sb.append(Long.toString(mMetaData.getLong(MediaMetadata.METADATA_KEY_TRACK_NUMBER)) + " ");
196         if(mMetaData.containsKey(MediaMetadata.METADATA_KEY_NUM_TRACKS))
197             sb.append(Long.toString(mMetaData.getLong(MediaMetadata.METADATA_KEY_NUM_TRACKS)) + " ");
198         if(mMetaData.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER))
199             sb.append(Long.toString(mMetaData.getLong(MediaMetadata.METADATA_KEY_DURATION)) + " ");
200         if(mMetaData.containsKey(MediaMetadata.METADATA_KEY_TRACK_NUMBER))
201             sb.append(Long.toString(mMetaData.getLong(MediaMetadata.METADATA_KEY_DURATION)) + " ");
202         return sb.toString();
203     }
displayBluetoothAvrcpSettings(BluetoothAvrcpPlayerSettings mSett)204     public static String displayBluetoothAvrcpSettings(BluetoothAvrcpPlayerSettings mSett) {
205         StringBuffer sb =  new StringBuffer();
206         int supportedSetting = mSett.getSettings();
207         if(AvrcpControllerConstants.VDBG) Log.d(TAG," setting: " + supportedSetting);
208         if((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER) != 0) {
209             sb.append(" EQ : ");
210             sb.append(Integer.toString(mSett.getSettingValue(BluetoothAvrcpPlayerSettings.
211                                                              SETTING_EQUALIZER)));
212         }
213         if((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_REPEAT) != 0) {
214             sb.append(" REPEAT : ");
215             sb.append(Integer.toString(mSett.getSettingValue(BluetoothAvrcpPlayerSettings.
216                                                              SETTING_REPEAT)));
217         }
218         if((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE) != 0) {
219             sb.append(" SHUFFLE : ");
220             sb.append(Integer.toString(mSett.getSettingValue(BluetoothAvrcpPlayerSettings.
221                                                              SETTING_SHUFFLE)));
222         }
223         if((supportedSetting & BluetoothAvrcpPlayerSettings.SETTING_SCAN) != 0) {
224             sb.append(" SCAN : ");
225             sb.append(Integer.toString(mSett.getSettingValue(BluetoothAvrcpPlayerSettings.
226                                                              SETTING_SCAN)));
227         }
228         return sb.toString();
229     }
230 }
231 /*
232  * Contains information about remote device
233  */
234 class RemoteDevice {
235     BluetoothDevice mBTDevice;
236     int mRemoteFeatures;
237     int mBatteryStatus;
238     int mSystemStatus;
239     int mAbsVolNotificationState;
240     int mNotificationLabel;
241     boolean mFirstAbsVolCmdRecvd;
242 
cleanup()243     public void cleanup() {
244         mBTDevice = null;
245         mRemoteFeatures = AvrcpControllerConstants.BTRC_FEAT_NONE;
246         mBatteryStatus = AvrcpControllerConstants.BATT_POWER_UNDEFINED;
247         mSystemStatus = AvrcpControllerConstants.SYSTEM_STATUS_UNDEFINED;
248         mAbsVolNotificationState = AvrcpControllerConstants.DEFER_VOLUME_CHANGE_RSP;
249         mNotificationLabel = AvrcpControllerConstants.VOLUME_LABEL_UNDEFINED;
250         mFirstAbsVolCmdRecvd = false;
251     }
252 
RemoteDevice(BluetoothDevice mDevice)253     public RemoteDevice(BluetoothDevice mDevice) {
254         mBTDevice = mDevice;
255         mRemoteFeatures = AvrcpControllerConstants.BTRC_FEAT_NONE;
256         mBatteryStatus = AvrcpControllerConstants.BATT_POWER_UNDEFINED;
257         mSystemStatus = AvrcpControllerConstants.SYSTEM_STATUS_UNDEFINED;
258         mAbsVolNotificationState = AvrcpControllerConstants.DEFER_VOLUME_CHANGE_RSP;
259         mNotificationLabel = AvrcpControllerConstants.VOLUME_LABEL_UNDEFINED;
260         mFirstAbsVolCmdRecvd = false;
261     }
262 
isBrowsingSupported()263     public boolean isBrowsingSupported() {
264         if((mRemoteFeatures & AvrcpControllerConstants.BTRC_FEAT_BROWSE) != 0)
265             return true;
266         else
267            return false;
268     }
isMetaDataSupported()269     public boolean isMetaDataSupported() {
270         if((mRemoteFeatures & AvrcpControllerConstants.BTRC_FEAT_METADATA) != 0)
271             return true;
272         else
273            return false;
274     }
275 }
276 
277 /*
278  * Base Class for Media Item
279  */
280 class MediaItem {
281     /*
282      * This is a combination of locaiton and item. Spec Snippet
283      * In VFS if same item is in different location it may have same uid.
284      * In Now Playing same item should have differnt UID
285      * Can never be 0, used only for GetElementAttributes
286      * TODO: UID counter, which is used for database aware player
287      */
288     double mItemUid;
289 }
290 
291 /*
292  * Contains information Player Application Setting
293  */
294 class PlayerApplicationSettings {
295     public byte attr_Id;
296     public byte attr_val;
297     public byte [] supported_values;
298     public String attr_text;
299     public String [] supported_values_text;// This is to keep displayable text in UTF-8
300 }
301 /*
302  * Contains information about remote player
303  */
304 class PlayerInfo {
305     private static final String TAG = "PlayerInfo";
306     byte mPlayStatus;
307     long mPlayTime;
308     /*
309      * 2 byte player id to identify player.
310      * In 1.3 this value will be set to zero
311      */
312     char mPlayerId;
313     ArrayList<PlayerApplicationSettings> mPlayerAppSetting;
resetPlayer()314     private void resetPlayer() {
315         mPlayStatus = AvrcpControllerConstants.PLAY_STATUS_STOPPED;
316         mPlayTime   = AvrcpControllerConstants.PLAYING_TIME_INVALID;
317         mPlayerId   = 0;
318         mPlayerAppSetting = new ArrayList<PlayerApplicationSettings>();
319     }
PlayerInfo()320     public PlayerInfo() {
321         resetPlayer();
322     }
setSupportedPlayerAppSetting(ByteBuffer bb)323     public void setSupportedPlayerAppSetting (ByteBuffer bb) {
324         /* ByteBuffer has to be of the following format
325          * id, num_values, values[]
326          */
327         while(bb.hasRemaining()) {
328             PlayerApplicationSettings plAppSetting = new PlayerApplicationSettings();
329             plAppSetting.attr_Id = bb.get();
330             byte numSupportedVals = bb.get();
331             plAppSetting.supported_values = new byte[numSupportedVals];
332             for (int i = 0; i<numSupportedVals; i++) {
333                 plAppSetting.supported_values[i] = bb.get();
334             }
335             mPlayerAppSetting.add(plAppSetting);
336         }
337     }
updatePlayerAppSetting(ByteBuffer bb)338     public void updatePlayerAppSetting(ByteBuffer bb) {
339         /* ByteBuffer has to be of the following format
340          * <id, value>
341          */
342         if(mPlayerAppSetting.isEmpty())
343             return;
344         while(bb.hasRemaining()) {
345             byte attribId = bb.get();
346             for(PlayerApplicationSettings plAppSetting: mPlayerAppSetting) {
347                 if(plAppSetting.attr_Id == attribId)
348                     plAppSetting.attr_val = bb.get();
349             }
350         }
351     }
352 
getSupportedPlayerAppSetting()353     public BluetoothAvrcpPlayerSettings getSupportedPlayerAppSetting() {
354         /*
355          * Here we create PlayerAppSetting
356          * based on BluetoothAvrcpPlayerSettings
357          */
358         int supportedSettings = 0; // Player App Setting used by BluetoothAvrcpPlayerSettings.
359         for(PlayerApplicationSettings plAppSetting: mPlayerAppSetting) {
360             switch(plAppSetting.attr_Id) {
361             case AvrcpControllerConstants.ATTRIB_EQUALIZER_STATUS:
362                 supportedSettings |= BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER;
363                 break;
364             case AvrcpControllerConstants.ATTRIB_REPEAT_STATUS:
365                 supportedSettings |= BluetoothAvrcpPlayerSettings.SETTING_REPEAT;
366                 break;
367             case AvrcpControllerConstants.ATTRIB_SCAN_STATUS:
368                 supportedSettings |= BluetoothAvrcpPlayerSettings.SETTING_SCAN;
369                 break;
370             case AvrcpControllerConstants.ATTRIB_SHUFFLE_STATUS:
371                 supportedSettings |= BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE;
372                 break;
373             }
374         }
375         BluetoothAvrcpPlayerSettings mAvrcpPlayerAppSetting = new
376                                   BluetoothAvrcpPlayerSettings(supportedSettings);
377         for(PlayerApplicationSettings plAppSetting: mPlayerAppSetting) {
378             switch(plAppSetting.attr_Id) {
379             case AvrcpControllerConstants.ATTRIB_EQUALIZER_STATUS:
380                 mAvrcpPlayerAppSetting.addSettingValue(BluetoothAvrcpPlayerSettings.SETTING_EQUALIZER,
381                            AvrcpUtils.mapAttribIdValtoAvrcpPlayerSetting(plAppSetting.attr_Id,
382                                 plAppSetting.attr_val));
383                 break;
384             case AvrcpControllerConstants.ATTRIB_REPEAT_STATUS:
385                 mAvrcpPlayerAppSetting.addSettingValue(BluetoothAvrcpPlayerSettings.SETTING_REPEAT,
386                         AvrcpUtils.mapAttribIdValtoAvrcpPlayerSetting(plAppSetting.attr_Id,
387                                 plAppSetting.attr_val));
388                 break;
389             case AvrcpControllerConstants.ATTRIB_SCAN_STATUS:
390                 mAvrcpPlayerAppSetting.addSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SCAN,
391                         AvrcpUtils.mapAttribIdValtoAvrcpPlayerSetting(plAppSetting.attr_Id,
392                                 plAppSetting.attr_val));
393                 break;
394             case AvrcpControllerConstants.ATTRIB_SHUFFLE_STATUS:
395                 mAvrcpPlayerAppSetting.addSettingValue(BluetoothAvrcpPlayerSettings.SETTING_SHUFFLE,
396                         AvrcpUtils.mapAttribIdValtoAvrcpPlayerSetting(plAppSetting.attr_Id,
397                                 plAppSetting.attr_val));
398                 break;
399             }
400         }
401         return mAvrcpPlayerAppSetting;
402     }
getCurrentPlayerAppSettingValue(byte mPlayerAppAttrId)403     public byte getCurrentPlayerAppSettingValue(byte mPlayerAppAttrId) {
404         for(PlayerApplicationSettings plAppSetting: mPlayerAppSetting) {
405             if(mPlayerAppAttrId == plAppSetting.attr_Id)
406                 return plAppSetting.attr_val;
407         }
408         return 0;
409     }
410     /*
411      * Checks if current setting is supported by remote.
412      * input would be in form of flattened strucuture <id,val>
413      */
isPlayerAppSettingSupported(byte numAttributes, byte[] playerAppSetting)414     public boolean isPlayerAppSettingSupported(byte numAttributes, byte[] playerAppSetting) {
415         for( int i = 0; (i < 2*numAttributes);) {
416             byte id = playerAppSetting[i++];
417             byte val = playerAppSetting[i++];
418             boolean found = false;
419             for(PlayerApplicationSettings plAppSetting: mPlayerAppSetting) {
420                 if(plAppSetting.attr_Id == id) {
421                     for(int j = 0; j < plAppSetting.supported_values.length; j++) {
422                         if(val == plAppSetting.supported_values[j]) {
423                             found = true;
424                             break;
425                         }
426                     }
427                 }
428             }
429             if(!found)
430                 return false;
431         }
432         return true;
433     }
434 }
435 
436 /*
437  * Contains information about track
438  */
439 class TrackInfo extends MediaItem {
440     String mArtistName;
441     String mTrackTitle;
442     String mAlbumTitle;
443     String mGenre;
444     long mTrackNum; // number of audio file on original recording.
445     long mTotalTracks;// total number of tracks on original recording
446     long mTrackLen;// full length of AudioFile.
447     /* In case of 1.3 we have to set itemUid explicitly to 0 */
448 
449     /* reset it to default values */
resetTrackInfo()450     private void resetTrackInfo() {
451         mArtistName = AvrcpControllerConstants.ARTIST_NAME_INVALID;;
452         mTrackTitle = AvrcpControllerConstants.TITLE_INVALID;;
453         mAlbumTitle = AvrcpControllerConstants.ALBUM_NAME_INVALID;
454         mGenre      = AvrcpControllerConstants.GENRE_INVALID;
455         mTrackNum   = AvrcpControllerConstants.TRACK_NUM_INVALID;
456         mTotalTracks = AvrcpControllerConstants.TOTAL_TRACK_TIME_INVALID;
457         mTrackLen = AvrcpControllerConstants.TOTAL_TRACK_TIME_INVALID;
458     }
TrackInfo()459     public TrackInfo() {
460         resetTrackInfo();
461     }
TrackInfo(int mTrackId, byte mNumAttributes, int[] mAttribIds, String[] mAttribs)462     public TrackInfo(int mTrackId, byte mNumAttributes, int[] mAttribIds, String[] mAttribs) {
463         mItemUid = mTrackId;
464         resetTrackInfo();
465         for (int i = 0; i < mNumAttributes; i++) {
466             switch(mAttribIds[i]) {
467             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_TITLE:
468                 mTrackTitle = mAttribs[i];
469                 break;
470             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_ARTIST_NAME:
471                 mArtistName = mAttribs[i];
472                 break;
473             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_ALBUM_NAME:
474                 mAlbumTitle = mAttribs[i];
475                 break;
476             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_TRACK_NUMBER:
477                 if(!mAttribs[i].isEmpty())
478                     mTrackNum = Long.valueOf(mAttribs[i]);
479                 break;
480             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_TOTAL_TRACK_NUMBER:
481                 if(!mAttribs[i].isEmpty())
482                     mTotalTracks = Long.valueOf(mAttribs[i]);
483                 break;
484             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_GENRE:
485                 mGenre = mAttribs[i];
486                 break;
487             case AvrcpControllerConstants.MEDIA_ATTRIBUTE_PLAYING_TIME:
488                 if(!mAttribs[i].isEmpty())
489                     mTrackLen = Long.valueOf(mAttribs[i]);
490                 break;
491             }
492         }
493     }
toString()494     public String toString() {
495         return "Metadata [artist=" + mArtistName + " trackTitle= " + mTrackTitle +
496                 " albumTitle= " + mAlbumTitle + " genre= " +mGenre+" trackNum= "+
497                 Long.toString(mTrackNum) + " track_len : "+ Long.toString(mTrackLen) +
498                 " TotalTracks " + Long.toString(mTotalTracks) + "]";
499     }
500 }
501