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 
     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 
     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 */
    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 
    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 
    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 
    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 
    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 */
    344 class 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 
    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 
    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 
    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