AvrcpHelperClasses.java revision c4fbd756e2645147470c486ae96f2253f5e13a52
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
17package com.android.bluetooth.avrcp;
18
19import android.annotation.NonNull;
20import android.annotation.Nullable;
21
22import com.android.bluetooth.Utils;
23
24import java.util.ArrayDeque;
25import java.util.Arrays;
26import 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
34class AvrcpCmd {
35
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
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
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
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
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
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 */
118class 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
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
152class 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
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
184class ItemAttrRsp {
185    byte mStatus;
186    byte mNumAttr;
187    int[] mAttributesIds;
188    String[] mAttributesArray;
189
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 */
199class 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
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 */
224    byte getPlayStatus() {
225        return mPlayStatus;
226    }
227
228    void setPlayStatus(byte playStatus) {
229        this.mPlayStatus = playStatus;
230    }
231
232    MediaController getMediaController() {
233        return mMediaController;
234    }
235
236    void setMediaController(MediaController mediaController) {
237        if (mediaController != null) {
238            this.mPackageName = mediaController.getPackageName();
239        }
240        this.mMediaController = mediaController;
241    }
242
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
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
260    byte getMajorType() {
261        return mMajorType;
262    }
263
264    void setMajorType(byte majorType) {
265        this.mMajorType = majorType;
266    }
267
268    int getSubType() {
269        return mSubType;
270    }
271
272    void setSubType(int subType) {
273        this.mSubType = subType;
274    }
275
276    String getDisplayableName() {
277        return mDisplayableName;
278    }
279
280    void setDisplayableName(@Nullable String displayableName) {
281        if (displayableName == null) {
282            displayableName = "";
283        }
284        this.mDisplayableName = displayableName;
285    }
286
287    short[] getFeatureBitMask() {
288        return mFeatureBitMask;
289    }
290
291    void setFeatureBitMask(short[] featureBitMask) {
292        synchronized (this) {
293            this.mFeatureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
294            Arrays.sort(this.mFeatureBitMask);
295        }
296    }
297
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 */
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
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 */
344class BrowsePlayerInfo {
345    public String packageName;
346    public String displayableName;
347    public String serviceClass;
348
349    BrowsePlayerInfo(String packageName, String displayableName, String serviceClass) {
350        this.packageName = packageName;
351        this.displayableName = displayableName;
352        this.serviceClass = serviceClass;
353    }
354
355    @Override
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
366class 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
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 */
402class EvictingQueue<E> extends ArrayDeque<E> {
403    private int mMaxSize;
404
405    EvictingQueue(int maxSize) {
406        super();
407        mMaxSize = maxSize;
408    }
409
410    EvictingQueue(int maxSize, int initialElements) {
411        super(initialElements);
412        mMaxSize = maxSize;
413    }
414
415    EvictingQueue(int maxSize, Collection<? extends E> c) {
416        super(c);
417        mMaxSize = maxSize;
418    }
419
420    @Override
421    public void addFirst(E e) {
422        if (super.size() == mMaxSize) {
423            return;
424        }
425        super.addFirst(e);
426    }
427
428    @Override
429    public void addLast(E e) {
430        if (super.size() == mMaxSize) {
431            super.remove();
432        }
433        super.addLast(e);
434    }
435
436    @Override
437    public boolean offerFirst(E e) {
438        if (super.size() == mMaxSize) {
439            return false;
440        }
441        return super.offerFirst(e);
442    }
443
444    @Override
445    public boolean offerLast(E e) {
446        if (super.size() == mMaxSize) {
447            super.remove();
448        }
449        return super.offerLast(e);
450    }
451}
452