• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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.annotation.NonNull;
20 import android.annotation.Nullable;
21 
22 import com.android.bluetooth.Utils;
23 
24 import java.util.ArrayDeque;
25 import java.util.Arrays;
26 import java.util.Collection;
27 
28 /*************************************************************************************************
29  * Helper classes used for callback/response of browsing commands:-
30  *     1) To bundle parameters for  native callbacks/response.
31  *     2) Stores information of Addressed and Browsed Media Players.
32  ************************************************************************************************/
33 
34 class AvrcpCmd {
35 
AvrcpCmd()36     AvrcpCmd() {}
37 
38     /* Helper classes to pass parameters from callbacks to Avrcp handler */
39     class FolderItemsCmd {
40         byte mScope;
41         long mStartItem;
42         long mEndItem;
43         byte mNumAttr;
44         int[] mAttrIDs;
45         public byte[] mAddress;
46 
FolderItemsCmd(byte[] address, byte scope, long startItem, long endItem, byte numAttr, int[] attrIds)47         FolderItemsCmd(byte[] address, byte scope, long startItem, long endItem, byte numAttr,
48                 int[] attrIds) {
49             mAddress = address;
50             this.mScope = scope;
51             this.mStartItem = startItem;
52             this.mEndItem = endItem;
53             this.mNumAttr = numAttr;
54             this.mAttrIDs = attrIds;
55         }
56 
57         @Override
toString()58         public String toString() {
59             StringBuilder sb = new StringBuilder();
60             sb.append("[FolderItemCmd: scope " + mScope);
61             sb.append(" start " + mStartItem);
62             sb.append(" end " + mEndItem);
63             sb.append(" numAttr " + mNumAttr);
64             sb.append(" attrs: ");
65             for (int i = 0; i < mNumAttr; i++) {
66                 sb.append(mAttrIDs[i] + " ");
67             }
68             return sb.toString();
69         }
70     }
71 
72     class ItemAttrCmd {
73         byte mScope;
74         byte[] mUid;
75         int mUidCounter;
76         byte mNumAttr;
77         int[] mAttrIDs;
78         public byte[] mAddress;
79 
ItemAttrCmd(byte[] address, byte scope, byte[] uid, int uidCounter, byte numAttr, int[] attrIDs)80         ItemAttrCmd(byte[] address, byte scope, byte[] uid, int uidCounter, byte numAttr,
81                 int[] attrIDs) {
82             mAddress = address;
83             mScope = scope;
84             mUid = uid;
85             mUidCounter = uidCounter;
86             mNumAttr = numAttr;
87             mAttrIDs = attrIDs;
88         }
89 
90         @Override
toString()91         public String toString() {
92             StringBuilder sb = new StringBuilder();
93             sb.append("[ItemAttrCmd: scope " + mScope);
94             sb.append(" uid " + Utils.byteArrayToString(mUid));
95             sb.append(" numAttr " + mNumAttr);
96             sb.append(" attrs: ");
97             for (int i = 0; i < mNumAttr; i++) {
98                 sb.append(mAttrIDs[i] + " ");
99             }
100             return sb.toString();
101         }
102     }
103 
104     class ElementAttrCmd {
105         byte mNumAttr;
106         int[] mAttrIDs;
107         public byte[] mAddress;
108 
ElementAttrCmd(byte[] address, byte numAttr, int[] attrIDs)109         ElementAttrCmd(byte[] address, byte numAttr, int[] attrIDs) {
110             mAddress = address;
111             mNumAttr = numAttr;
112             mAttrIDs = attrIDs;
113         }
114     }
115 }
116 
117 /* Helper classes to pass parameters to native response */
118 class MediaPlayerListRsp {
119     byte mStatus;
120     short mUIDCounter;
121     byte mItemType;
122     int[] mPlayerIds;
123     byte[] mPlayerTypes;
124     int[] mPlayerSubTypes;
125     byte[] mPlayStatusValues;
126     short[] mFeatureBitMaskValues;
127     String[] mPlayerNameList;
128     int mNumItems;
129 
MediaPlayerListRsp(byte status, short uidCounter, int numItems, byte itemType, int[] playerIds, byte[] playerTypes, int[] playerSubTypes, byte[] playStatusValues, short[] featureBitMaskValues, String[] playerNameList)130     MediaPlayerListRsp(byte status, short uidCounter, int numItems, byte itemType, int[] playerIds,
131             byte[] playerTypes, int[] playerSubTypes, byte[] playStatusValues,
132             short[] featureBitMaskValues, String[] playerNameList) {
133         this.mStatus = status;
134         this.mUIDCounter = uidCounter;
135         this.mNumItems = numItems;
136         this.mItemType = itemType;
137         this.mPlayerIds = playerIds;
138         this.mPlayerTypes = playerTypes;
139         this.mPlayerSubTypes = new int[numItems];
140         this.mPlayerSubTypes = playerSubTypes;
141         this.mPlayStatusValues = new byte[numItems];
142         this.mPlayStatusValues = playStatusValues;
143         int bitMaskSize = AvrcpConstants.AVRC_FEATURE_MASK_SIZE;
144         this.mFeatureBitMaskValues = new short[numItems * bitMaskSize];
145         for (int bitMaskIndex = 0; bitMaskIndex < (numItems * bitMaskSize); bitMaskIndex++) {
146             this.mFeatureBitMaskValues[bitMaskIndex] = featureBitMaskValues[bitMaskIndex];
147         }
148         this.mPlayerNameList = playerNameList;
149     }
150 }
151 
152 class FolderItemsRsp {
153     byte mStatus;
154     short mUIDCounter;
155     byte mScope;
156     int mNumItems;
157     byte[] mFolderTypes;
158     byte[] mPlayable;
159     byte[] mItemTypes;
160     byte[] mItemUid;
161     String[] mDisplayNames; /* display name of the item. Eg: Folder name or song name */
162     int[] mAttributesNum;
163     int[] mAttrIds;
164     String[] mAttrValues;
165 
FolderItemsRsp(byte status, short uidCounter, byte scope, int numItems, byte[] folderTypes, byte[] playable, byte[] itemTypes, byte[] itemsUid, String[] displayNameArray, int[] attributesNum, int[] attrIds, String[] attrValues)166     FolderItemsRsp(byte status, short uidCounter, byte scope, int numItems, byte[] folderTypes,
167             byte[] playable, byte[] itemTypes, byte[] itemsUid, String[] displayNameArray,
168             int[] attributesNum, int[] attrIds, String[] attrValues) {
169         this.mStatus = status;
170         this.mUIDCounter = uidCounter;
171         this.mScope = scope;
172         this.mNumItems = numItems;
173         this.mFolderTypes = folderTypes;
174         this.mPlayable = playable;
175         this.mItemTypes = itemTypes;
176         this.mItemUid = itemsUid;
177         this.mDisplayNames = displayNameArray;
178         this.mAttributesNum = attributesNum;
179         this.mAttrIds = attrIds;
180         this.mAttrValues = attrValues;
181     }
182 }
183 
184 class ItemAttrRsp {
185     byte mStatus;
186     byte mNumAttr;
187     int[] mAttributesIds;
188     String[] mAttributesArray;
189 
ItemAttrRsp(byte status, int[] attributesIds, String[] attributesArray)190     ItemAttrRsp(byte status, int[] attributesIds, String[] attributesArray) {
191         mStatus = status;
192         mNumAttr = (byte) attributesIds.length;
193         mAttributesIds = attributesIds;
194         mAttributesArray = attributesArray;
195     }
196 }
197 
198 /* stores information of Media Players in the system */
199 class MediaPlayerInfo {
200 
201     private byte mMajorType;
202     private int mSubType;
203     private byte mPlayStatus;
204     private short[] mFeatureBitMask;
205     @NonNull private String mPackageName;
206     @NonNull private String mDisplayableName;
207     @Nullable private MediaController mMediaController;
208 
MediaPlayerInfo(@ullable MediaController controller, byte majorType, int subType, byte playStatus, short[] featureBitMask, @NonNull String packageName, @Nullable String displayableName)209     MediaPlayerInfo(@Nullable MediaController controller, byte majorType, int subType,
210             byte playStatus, short[] featureBitMask, @NonNull String packageName,
211             @Nullable String displayableName) {
212         this.setMajorType(majorType);
213         this.setSubType(subType);
214         this.mPlayStatus = playStatus;
215         // store a copy the FeatureBitMask array
216         this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
217         Arrays.sort(this.mFeatureBitMask);
218         this.setPackageName(packageName);
219         this.setDisplayableName(displayableName);
220         this.setMediaController(controller);
221     }
222 
223     /* getters and setters */
getPlayStatus()224     byte getPlayStatus() {
225         return mPlayStatus;
226     }
227 
setPlayStatus(byte playStatus)228     void setPlayStatus(byte playStatus) {
229         this.mPlayStatus = playStatus;
230     }
231 
getMediaController()232     MediaController getMediaController() {
233         return mMediaController;
234     }
235 
setMediaController(MediaController mediaController)236     void setMediaController(MediaController mediaController) {
237         if (mediaController != null) {
238             this.mPackageName = mediaController.getPackageName();
239         }
240         this.mMediaController = mediaController;
241     }
242 
setPackageName(@onNull String name)243     void setPackageName(@NonNull String name) {
244         // Controller determines package name when it is set.
245         if (mMediaController != null) {
246             return;
247         }
248         this.mPackageName = name;
249     }
250 
getPackageName()251     String getPackageName() {
252         if (mMediaController != null) {
253             return mMediaController.getPackageName();
254         } else if (mPackageName != null) {
255             return mPackageName;
256         }
257         return null;
258     }
259 
getMajorType()260     byte getMajorType() {
261         return mMajorType;
262     }
263 
setMajorType(byte majorType)264     void setMajorType(byte majorType) {
265         this.mMajorType = majorType;
266     }
267 
getSubType()268     int getSubType() {
269         return mSubType;
270     }
271 
setSubType(int subType)272     void setSubType(int subType) {
273         this.mSubType = subType;
274     }
275 
getDisplayableName()276     String getDisplayableName() {
277         return mDisplayableName;
278     }
279 
setDisplayableName(@ullable String displayableName)280     void setDisplayableName(@Nullable String displayableName) {
281         if (displayableName == null) {
282             displayableName = "";
283         }
284         this.mDisplayableName = displayableName;
285     }
286 
getFeatureBitMask()287     short[] getFeatureBitMask() {
288         return mFeatureBitMask;
289     }
290 
setFeatureBitMask(short[] featureBitMask)291     void setFeatureBitMask(short[] featureBitMask) {
292         synchronized (this) {
293             this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
294             Arrays.sort(this.mFeatureBitMask);
295         }
296     }
297 
isBrowseSupported()298     boolean isBrowseSupported() {
299         synchronized (this) {
300             if (this.mFeatureBitMask == null) {
301                 return false;
302             }
303             for (short bit : this.mFeatureBitMask) {
304                 if (bit == AvrcpConstants.AVRC_PF_BROWSE_BIT_NO) {
305                     return true;
306                 }
307             }
308         }
309         return false;
310     }
311 
312     /** Tests if the view of this player presented to the controller is different enough to
313      *  justify sending an Available Players Changed update */
equalView(MediaPlayerInfo other)314     public boolean equalView(MediaPlayerInfo other) {
315         return (this.mMajorType == other.getMajorType()) && (this.mSubType == other.getSubType())
316                 && Arrays.equals(this.mFeatureBitMask, other.getFeatureBitMask())
317                 && this.mDisplayableName.equals(other.getDisplayableName());
318     }
319 
320     @Override
toString()321     public String toString() {
322         StringBuilder sb = new StringBuilder();
323         sb.append("MediaPlayerInfo ");
324         sb.append(getPackageName());
325         sb.append(" (as '" + getDisplayableName() + "')");
326         sb.append(" Type = " + getMajorType());
327         sb.append(", SubType = " + getSubType());
328         sb.append(", Status = " + mPlayStatus);
329         sb.append(" Feature Bits [");
330         short[] bits = getFeatureBitMask();
331         for (int i = 0; i < bits.length; i++) {
332             if (i != 0) {
333                 sb.append(" ");
334             }
335             sb.append(bits[i]);
336         }
337         sb.append("] Controller: ");
338         sb.append(getMediaController());
339         return sb.toString();
340     }
341 }
342 
343 /* stores information for browsable Media Players available in the system */
344 class BrowsePlayerInfo {
345     public String packageName;
346     public String displayableName;
347     public String serviceClass;
348 
BrowsePlayerInfo(String packageName, String displayableName, String serviceClass)349     BrowsePlayerInfo(String packageName, String displayableName, String serviceClass) {
350         this.packageName = packageName;
351         this.displayableName = displayableName;
352         this.serviceClass = serviceClass;
353     }
354 
355     @Override
toString()356     public String toString() {
357         StringBuilder sb = new StringBuilder();
358         sb.append("BrowsePlayerInfo ");
359         sb.append(packageName);
360         sb.append(" ( as '" + displayableName + "')");
361         sb.append(" service " + serviceClass);
362         return sb.toString();
363     }
364 }
365 
366 class FolderItemsData {
367     /* initialize sizes for rsp parameters */ int mNumItems;
368     int[] mAttributesNum;
369     byte[] mFolderTypes;
370     byte[] mItemTypes;
371     byte[] mPlayable;
372     byte[] mItemUid;
373     String[] mDisplayNames;
374     int[] mAttrIds;
375     String[] mAttrValues;
376     int mAttrCounter;
377 
FolderItemsData(int size)378     FolderItemsData(int size) {
379         mNumItems = size;
380         mAttributesNum = new int[size];
381 
382         mFolderTypes = new byte[size]; /* folderTypes */
383         mItemTypes = new byte[size]; /* folder or media item */
384         mPlayable = new byte[size];
385         Arrays.fill(mFolderTypes, AvrcpConstants.FOLDER_TYPE_MIXED);
386         Arrays.fill(mItemTypes, AvrcpConstants.BTRC_ITEM_MEDIA);
387         Arrays.fill(mPlayable, AvrcpConstants.ITEM_PLAYABLE);
388 
389         mItemUid = new byte[size * AvrcpConstants.UID_SIZE];
390         mDisplayNames = new String[size];
391 
392         mAttrIds = null; /* array of attr ids */
393         mAttrValues = null; /* array of attr values */
394     }
395 }
396 
397 /** A queue that evicts the first element when you add an element to the end when it reaches a
398  * maximum size.
399  * This is useful for keeping a FIFO queue of items where the items drop off the front, i.e. a log
400  * with a maximum size.
401  */
402 class EvictingQueue<E> extends ArrayDeque<E> {
403     private int mMaxSize;
404 
EvictingQueue(int maxSize)405     EvictingQueue(int maxSize) {
406         super();
407         mMaxSize = maxSize;
408     }
409 
EvictingQueue(int maxSize, int initialElements)410     EvictingQueue(int maxSize, int initialElements) {
411         super(initialElements);
412         mMaxSize = maxSize;
413     }
414 
EvictingQueue(int maxSize, Collection<? extends E> c)415     EvictingQueue(int maxSize, Collection<? extends E> c) {
416         super(c);
417         mMaxSize = maxSize;
418     }
419 
420     @Override
addFirst(E e)421     public void addFirst(E e) {
422         if (super.size() == mMaxSize) {
423             return;
424         }
425         super.addFirst(e);
426     }
427 
428     @Override
addLast(E e)429     public void addLast(E e) {
430         if (super.size() == mMaxSize) {
431             super.remove();
432         }
433         super.addLast(e);
434     }
435 
436     @Override
offerFirst(E e)437     public boolean offerFirst(E e) {
438         if (super.size() == mMaxSize) {
439             return false;
440         }
441         return super.offerFirst(e);
442     }
443 
444     @Override
offerLast(E e)445     public boolean offerLast(E e) {
446         if (super.size() == mMaxSize) {
447             super.remove();
448         }
449         return super.offerLast(e);
450     }
451 }
452