Home | History | Annotate | Download | only in avrcp
      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 import android.media.session.MediaSession;
     22 
     23 import com.android.bluetooth.Utils;
     24 
     25 import java.util.List;
     26 import java.util.Arrays;
     27 import java.util.ArrayDeque;
     28 import java.util.Collection;
     29 
     30 /*************************************************************************************************
     31  * Helper classes used for callback/response of browsing commands:-
     32  *     1) To bundle parameters for  native callbacks/response.
     33  *     2) Stores information of Addressed and Browsed Media Players.
     34  ************************************************************************************************/
     35 
     36 class AvrcpCmd {
     37 
     38     public AvrcpCmd() {}
     39 
     40     /* Helper classes to pass parameters from callbacks to Avrcp handler */
     41     class FolderItemsCmd {
     42         byte mScope;
     43         long mStartItem;
     44         long mEndItem;
     45         byte mNumAttr;
     46         int[] mAttrIDs;
     47         public byte[] mAddress;
     48 
     49         public FolderItemsCmd(byte[] address, byte scope, long startItem, long endItem,
     50                 byte numAttr, int[] attrIds) {
     51             mAddress = address;
     52             this.mScope = scope;
     53             this.mStartItem = startItem;
     54             this.mEndItem = endItem;
     55             this.mNumAttr = numAttr;
     56             this.mAttrIDs = attrIds;
     57         }
     58 
     59         public String toString() {
     60             StringBuilder sb = new StringBuilder();
     61             sb.append("[FolderItemCmd: scope " + mScope);
     62             sb.append(" start " + mStartItem);
     63             sb.append(" end " + mEndItem);
     64             sb.append(" numAttr " + mNumAttr);
     65             sb.append(" attrs: ");
     66             for (int i = 0; i < mNumAttr; i++) {
     67                 sb.append(mAttrIDs[i] + " ");
     68             }
     69             return sb.toString();
     70         }
     71     }
     72 
     73     class ItemAttrCmd {
     74         byte mScope;
     75         byte[] mUid;
     76         int mUidCounter;
     77         byte mNumAttr;
     78         int[] mAttrIDs;
     79         public byte[] mAddress;
     80 
     81         public ItemAttrCmd(byte[] address, byte scope, byte[] uid, int uidCounter, byte numAttr,
     82                 int[] attrIDs) {
     83             mAddress = address;
     84             mScope = scope;
     85             mUid = uid;
     86             mUidCounter = uidCounter;
     87             mNumAttr = numAttr;
     88             mAttrIDs = attrIDs;
     89         }
     90 
     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         public 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 itemType;
    122     int[] mPlayerIds;
    123     byte[] mPlayerTypes;
    124     int[] mPlayerSubTypes;
    125     byte[] mPlayStatusValues;
    126     short[] mFeatureBitMaskValues;
    127     String[] mPlayerNameList;
    128     int mNumItems;
    129 
    130     public MediaPlayerListRsp(byte status, short UIDCounter, int numItems, byte itemType,
    131             int[] playerIds, 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.itemType = 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 
    166     public FolderItemsRsp(byte Status, short UIDCounter, byte scope, int numItems,
    167             byte[] folderTypes, byte[] playable, byte[] ItemTypes, byte[] ItemsUid,
    168             String[] displayNameArray, 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 
    190     public 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 majorType;
    202     private int subType;
    203     private byte playStatus;
    204     private short[] featureBitMask;
    205     private @NonNull String packageName;
    206     private @NonNull String displayableName;
    207     private @Nullable MediaController mediaController;
    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.playStatus = playStatus;
    215         // store a copy the FeatureBitMask array
    216         this.featureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
    217         Arrays.sort(this.featureBitMask);
    218         this.setPackageName(packageName);
    219         this.setDisplayableName(displayableName);
    220         this.setMediaController(controller);
    221     }
    222 
    223     /* getters and setters */
    224     byte getPlayStatus() {
    225         return playStatus;
    226     }
    227 
    228     void setPlayStatus(byte playStatus) {
    229         this.playStatus = playStatus;
    230     }
    231 
    232     MediaController getMediaController() {
    233         return mediaController;
    234     }
    235 
    236     void setMediaController(MediaController mediaController) {
    237         if (mediaController != null) {
    238             this.packageName = mediaController.getPackageName();
    239         }
    240         this.mediaController = mediaController;
    241     }
    242 
    243     void setPackageName(@NonNull String name) {
    244         // Controller determines package name when it is set.
    245         if (mediaController != null) return;
    246         this.packageName = name;
    247     }
    248 
    249     String getPackageName() {
    250         if (mediaController != null) {
    251             return mediaController.getPackageName();
    252         } else if (packageName != null) {
    253             return packageName;
    254         }
    255         return null;
    256     }
    257 
    258     byte getMajorType() {
    259         return majorType;
    260     }
    261 
    262     void setMajorType(byte majorType) {
    263         this.majorType = majorType;
    264     }
    265 
    266     int getSubType() {
    267         return subType;
    268     }
    269 
    270     void setSubType(int subType) {
    271         this.subType = subType;
    272     }
    273 
    274     String getDisplayableName() {
    275         return displayableName;
    276     }
    277 
    278     void setDisplayableName(@Nullable String displayableName) {
    279         if (displayableName == null) displayableName = "";
    280         this.displayableName = displayableName;
    281     }
    282 
    283     short[] getFeatureBitMask() {
    284         return featureBitMask;
    285     }
    286 
    287     void setFeatureBitMask(short[] featureBitMask) {
    288         synchronized (this) {
    289             this.featureBitMask = Arrays.copyOf(featureBitMask, featureBitMask.length);
    290             Arrays.sort(this.featureBitMask);
    291         }
    292     }
    293 
    294     boolean isBrowseSupported() {
    295         synchronized (this) {
    296             if (this.featureBitMask == null) return false;
    297             for (short bit : this.featureBitMask) {
    298                 if (bit == AvrcpConstants.AVRC_PF_BROWSE_BIT_NO) return true;
    299             }
    300         }
    301         return false;
    302     }
    303 
    304     /** Tests if the view of this player presented to the controller is different enough to
    305      *  justify sending an Available Players Changed update */
    306     public boolean equalView(MediaPlayerInfo other) {
    307         return (this.majorType == other.getMajorType()) && (this.subType == other.getSubType())
    308                 && Arrays.equals(this.featureBitMask, other.getFeatureBitMask())
    309                 && this.displayableName.equals(other.getDisplayableName());
    310     }
    311 
    312     @Override
    313     public String toString() {
    314         StringBuilder sb = new StringBuilder();
    315         sb.append("MediaPlayerInfo ");
    316         sb.append(getPackageName());
    317         sb.append(" (as '" + getDisplayableName() + "')");
    318         sb.append(" Type = " + getMajorType());
    319         sb.append(", SubType = " + getSubType());
    320         sb.append(", Status = " + playStatus);
    321         sb.append(" Feature Bits [");
    322         short[] bits = getFeatureBitMask();
    323         for (int i = 0; i < bits.length; i++) {
    324             if (i != 0) sb.append(" ");
    325             sb.append(bits[i]);
    326         }
    327         sb.append("] Controller: ");
    328         sb.append(getMediaController());
    329         return sb.toString();
    330     }
    331 }
    332 
    333 /* stores information for browsable Media Players available in the system */
    334 class BrowsePlayerInfo {
    335     String packageName;
    336     String displayableName;
    337     String serviceClass;
    338 
    339     public BrowsePlayerInfo(String packageName, String displayableName, String serviceClass) {
    340         this.packageName = packageName;
    341         this.displayableName = displayableName;
    342         this.serviceClass = serviceClass;
    343     }
    344 
    345     @Override
    346     public String toString() {
    347         StringBuilder sb = new StringBuilder();
    348         sb.append("BrowsePlayerInfo ");
    349         sb.append(packageName);
    350         sb.append(" ( as '" + displayableName + "')");
    351         sb.append(" service " + serviceClass);
    352         return sb.toString();
    353     }
    354 }
    355 
    356 class FolderItemsData {
    357     /* initialize sizes for rsp parameters */
    358     int mNumItems;
    359     int[] mAttributesNum;
    360     byte[] mFolderTypes;
    361     byte[] mItemTypes;
    362     byte[] mPlayable;
    363     byte[] mItemUid;
    364     String[] mDisplayNames;
    365     int[] mAttrIds;
    366     String[] mAttrValues;
    367     int attrCounter;
    368 
    369     public FolderItemsData(int size) {
    370         mNumItems = size;
    371         mAttributesNum = new int[size];
    372 
    373         mFolderTypes = new byte[size]; /* folderTypes */
    374         mItemTypes = new byte[size]; /* folder or media item */
    375         mPlayable = new byte[size];
    376         Arrays.fill(mFolderTypes, AvrcpConstants.FOLDER_TYPE_MIXED);
    377         Arrays.fill(mItemTypes, AvrcpConstants.BTRC_ITEM_MEDIA);
    378         Arrays.fill(mPlayable, AvrcpConstants.ITEM_PLAYABLE);
    379 
    380         mItemUid = new byte[size * AvrcpConstants.UID_SIZE];
    381         mDisplayNames = new String[size];
    382 
    383         mAttrIds = null; /* array of attr ids */
    384         mAttrValues = null; /* array of attr values */
    385     }
    386 }
    387 
    388 /** A queue that evicts the first element when you add an element to the end when it reaches a
    389  * maximum size.
    390  * This is useful for keeping a FIFO queue of items where the items drop off the front, i.e. a log
    391  * with a maximum size.
    392  */
    393 class EvictingQueue<E> extends ArrayDeque<E> {
    394     private int mMaxSize;
    395 
    396     public EvictingQueue(int maxSize) {
    397         super();
    398         mMaxSize = maxSize;
    399     }
    400 
    401     public EvictingQueue(int maxSize, int initialElements) {
    402         super(initialElements);
    403         mMaxSize = maxSize;
    404     }
    405 
    406     public EvictingQueue(int maxSize, Collection<? extends E> c) {
    407         super(c);
    408         mMaxSize = maxSize;
    409     }
    410 
    411     @Override
    412     public void addFirst(E e) {
    413         if (super.size() == mMaxSize) return;
    414         super.addFirst(e);
    415     }
    416 
    417     @Override
    418     public void addLast(E e) {
    419         if (super.size() == mMaxSize) {
    420             super.remove();
    421         }
    422         super.addLast(e);
    423     }
    424 
    425     @Override
    426     public boolean offerFirst(E e) {
    427         if (super.size() == mMaxSize) return false;
    428         return super.offerFirst(e);
    429     }
    430 
    431     @Override
    432     public boolean offerLast(E e) {
    433         if (super.size() == mMaxSize) {
    434             super.remove();
    435         }
    436         return super.offerLast(e);
    437     }
    438 }
    439