/src/com/android/bluetooth/avrcp/Avrcp.java
Java | 1089 lines | 960 code | 89 blank | 40 comment | 107 complexity | 47ae9468a0645137fce30d2872ea6349 MD5 | raw file
- /*
- * Copyright (C) 2013-2014, The Linux Foundation. All rights reserved.
- * Not a Contribution.
- *
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- package com.android.bluetooth.avrcp;
- import java.util.Timer;
- import java.util.TimerTask;
- import android.app.PendingIntent;
- import android.bluetooth.BluetoothAdapter;
- import android.bluetooth.BluetoothA2dp;
- import android.bluetooth.BluetoothAvrcp;
- import android.bluetooth.BluetoothDevice;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.graphics.Bitmap;
- import android.media.AudioManager;
- import android.media.IRemoteControlDisplay;
- import android.media.MediaMetadataRetriever;
- import android.media.RemoteControlClient;
- import android.media.RemoteController;
- import android.media.RemoteController.MetadataEditor;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.HandlerThread;
- import android.os.Looper;
- import android.os.Message;
- import android.os.ParcelUuid;
- import android.os.PowerManager;
- import android.os.PowerManager.WakeLock;
- import android.os.RemoteException;
- import android.os.ServiceManager;
- import android.os.SystemClock;
- import android.util.Log;
- import android.view.KeyEvent;
- import android.content.BroadcastReceiver;
- import com.android.bluetooth.a2dp.A2dpService;
- import com.android.bluetooth.btservice.AdapterService;
- import com.android.bluetooth.btservice.ProfileService;
- import com.android.bluetooth.Utils;
- import com.android.internal.util.IState;
- import com.android.internal.util.State;
- import com.android.internal.util.StateMachine;
- import java.lang.ref.WeakReference;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.Set;
- import java.util.Iterator;
- import android.provider.MediaStore;
- import android.content.ContentResolver;
- import android.database.Cursor;
- import android.database.sqlite.SQLiteException;
- import android.net.Uri;
- import android.app.Notification;
- import android.app.NotificationManager;
- /**
- * support Bluetooth AVRCP profile.
- * support metadata, play status and event notification
- */
- public final class Avrcp {
- private static final boolean DEBUG = false;
- private static final String TAG = "Avrcp";
- private Context mContext;
- private final AudioManager mAudioManager;
- private A2dpService mA2dpService;
- private AvrcpMessageHandler mHandler;
- private RemoteController mRemoteController;
- private RemoteControllerWeak mRemoteControllerCb;
- private AvrcpRemoteControllerWeak mAvrcpRemoteControllerCb;
- private Metadata mMetadata;
- private int mTransportControlFlags;
- private int mCurrentPlayerState;
- private int mPlayStatusChangedNT;
- private int mTrackChangedNT;
- private long mCurrentPosMs;
- private long mPlayStartTimeMs;
- private long mTrackNumber;
- private long mSongLengthMs;
- private long mPlaybackIntervalMs;
- private int mPlayPosChangedNT;
- private long mSkipStartTime;
- private final int mVolumeStep;
- private final int mAudioStreamMax;
- private static final String BLUETOOTH_ADMIN_PERM = android.Manifest.permission.BLUETOOTH_ADMIN;
- private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH;
- private int mSkipAmount;
- private final BluetoothAdapter mAdapter;
- private static Uri mMediaUriStatic;
- private static long currentTrackPos;
- private static boolean updatePlayTime;
- private static boolean updateValues;
- private int mAddressedPlayerId;
- /* BTRC features */
- public static final int BTRC_FEAT_METADATA = 0x01;
- public static final int BTRC_FEAT_ABSOLUTE_VOLUME = 0x02;
- public static final int BTRC_FEAT_BROWSE = 0x04;
- public static final int BTRC_FEAT_AVRC_UI_UPDATE = 0x08;
- /* AVRC response codes, from avrc_defs */
- private static final int AVRC_RSP_NOT_IMPL = 8;
- private static final int AVRC_RSP_ACCEPT = 9;
- private static final int AVRC_RSP_REJ = 10;
- private static final int AVRC_RSP_IN_TRANS = 11;
- private static final int AVRC_RSP_IMPL_STBL = 12;
- private static final int AVRC_RSP_CHANGED = 13;
- private static final int AVRC_RSP_INTERIM = 15;
- private static final int MESSAGE_GET_RC_FEATURES = 1;
- private static final int MESSAGE_GET_PLAY_STATUS = 2;
- private static final int MESSAGE_GET_ELEM_ATTRS = 3;
- private static final int MESSAGE_REGISTER_NOTIFICATION = 4;
- private static final int MESSAGE_PLAY_INTERVAL_TIMEOUT = 5;
- private static final int MESSAGE_VOLUME_CHANGED = 6;
- private static final int MESSAGE_ADJUST_VOLUME = 7;
- private static final int MESSAGE_SET_ABSOLUTE_VOLUME = 8;
- private static final int MESSAGE_ABS_VOL_TIMEOUT = 9;
- private static final int MESSAGE_FAST_FORWARD = 10;
- private static final int MESSAGE_REWIND = 11;
- private static final int MESSAGE_CHANGE_PLAY_POS = 12;
- private static final int MESSAGE_SET_A2DP_AUDIO_STATE = 13;
- private static final int MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT = 14;
- private static final int AVRCP_BR_RSP_TIMEOUT = 2000;
- private static final int MESSAGE_SEND_PASS_THROUGH_CMD = 2001;
- private static final int MESSAGE_SET_ADDR_PLAYER = 2002;
- private static final int MESSAGE_GET_FOLDER_ITEMS = 2003;
- private static final int MESSAGE_SET_BROWSED_PLAYER = 2004;
- private static final int MESSAGE_CHANGE_PATH = 2005;
- private static final int MESSAGE_PLAY_ITEM = 2006;
- private static final int MESSAGE_GET_ITEM_ATTRS = 2007;
- private CachedRequest mCachedRequest = null;
- private static final int MSG_UPDATE_STATE = 100;
- private static final int MSG_SET_METADATA = 101;
- private static final int MSG_SET_TRANSPORT_CONTROLS = 102;
- private static final int MSG_SET_GENERATION_ID = 104;
- private static final int MSG_UPDATE_AVAILABLE_PLAYERS = 201;
- private static final int MSG_UPDATE_ADDRESSED_PLAYER = 202;
- private static final int MSG_UPDATE_RCC_CHANGE = 203;
- private static final int MSG_UPDATE_BROWSED_PLAYER_FOLDER = 204;
- private static final int MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED = 205;
- private static final int MSG_PLAY_ITEM_RESPONSE = 206;
- private static final int MSG_NOW_PLAYING_ENTRIES_RECEIVED = 207;
- private MediaPlayerInfo mediaPlayerInfo1;
- private static final int BUTTON_TIMEOUT_TIME = 2000;
- private static final int BASE_SKIP_AMOUNT = 2000;
- private static final int KEY_STATE_PRESS = 1;
- private static final int KEY_STATE_RELEASE = 0;
- private static final int SKIP_PERIOD = 400;
- private static final int SKIP_DOUBLE_INTERVAL = 3000;
- private static final long MAX_MULTIPLIER_VALUE = 128L;
- private static final int CMD_TIMEOUT_DELAY = 2000;
- private static final int MAX_ERROR_RETRY_TIMES = 3;
- private static final int AVRCP_MAX_VOL = 127;
- private static final int AVRCP_BASE_VOLUME_STEP = 1;
- private final static int MESSAGE_PLAYERSETTINGS_TIMEOUT = 602;
- private static final int AVRCP_CONNECTED = 1;
- public static final int KEY_STATE_PRESSED = 0;
- public static final int KEY_STATE_RELEASED = 1;
- private final static int TYPE_MEDIA_PLAYER_ITEM = 0x01;
- private final static int TYPE_FOLDER_ITEM = 0x02;
- private final static int TYPE_MEDIA_ELEMENT_ITEM = 0x03;
- private final static int FOLDER_UP = 0x00;
- private final static int FOLDER_DOWN = 0x01;
- private static final String PATH_INVALID = "invalid";
- private static final String PATH_ROOT = "root";
- private static final String PATH_TITLES = "titles";
- private static final String PATH_ALBUMS = "albums";
- private static final String PATH_ARTISTS = "artists";
- private static final String PATH_PLAYLISTS = "playlists";
- private final static long UID_TITLES = 0x01;
- private final static long UID_ALBUM = 0x02;
- private final static long UID_ARTIST = 0x03;
- private final static long UID_PLAYLIST = 0x04;
- private final static int NUM_ROOT_ELEMENTS = 0x04;
- private static final int INTERNAL_ERROR = 0x03;
- private static final int OPERATION_SUCCESSFUL = 0x04;
- private static final int INVALID_DIRECTION = 0x07;
- private static final int NOT_A_DIRECTORY = 0x08;
- private static final int DOES_NOT_EXIST = 0x09;
- private static final int INVALID_SCOPE = 0x0a;
- private static final int RANGE_OUT_OF_BOUNDS = 0x0b;
- private static final int UID_A_DIRECTORY = 0x0c;
- private static final int MEDIA_IN_USE = 0x0d;
- private static final int INVALID_PLAYER_ID = 0x11;
- private static final int PLAYER_NOT_BROWSABLE = 0x12;
- private static final int PLAYER_NOT_ADDRESSED = 0x13;
- private static final int FOLDER_TYPE_MIXED = 0x00;
- private static final int FOLDER_TYPE_TITLES = 0x01;
- private static final int FOLDER_TYPE_ALBUMS = 0x02;
- private static final int FOLDER_TYPE_ARTISTS = 0x03;
- private static final int FOLDER_TYPE_GENRES = 0x04;
- private static final int FOLDER_TYPE_PLAYLISTS = 0x05;
- private static final int MEDIA_TYPE_AUDIO = 0X00;
- private static final int MEDIA_TYPE_VIDEO = 0X01;
- private static final int MAX_BROWSE_ITEM_TO_SEND = 0x03;
- private static final int MAX_ATTRIB_COUNT = 0x07;
- private final static int ALBUMS_ITEM_INDEX = 0;
- private final static int ARTISTS_ITEM_INDEX = 1;
- private final static int PLAYLISTS_ITEM_INDEX = 2;
- private final static int TITLES_ITEM_INDEX = 3;
- //Intents for PlayerApplication Settings
- private static final String PLAYERSETTINGS_REQUEST =
- "org.codeaurora.music.playersettingsrequest";
- private static final String PLAYERSETTINGS_RESPONSE =
- "org.codeaurora.music.playersettingsresponse";
- // Max number of Avrcp connections at any time
- private int maxAvrcpConnections = 1;
- BluetoothDevice mBrowserDevice = null;
- private static final int INVALID_DEVICE_INDEX = 0xFF;
- // codes for reset of of notifications
- private static final int PLAY_POSITION_CHANGE_NOTIFICATION = 101;
- private static final int PLAY_STATUS_CHANGE_NOTIFICATION = 102;
- private static final int TRACK_CHANGE_NOTIFICATION = 103;
- private static final int NOW_PALYING_CONTENT_CHANGED_NOTIFICATION = 104;
- private static final int INVALID_ADDRESSED_PLAYER_ID = -1;
- // Device dependent registered Notification & Variables
- private class DeviceDependentFeature {
- private BluetoothDevice mCurrentDevice;
- private int mCurrentPlayState;
- private int mPlayStatusChangedNT;
- private int mPlayerStatusChangeNT;
- private int mTrackChangedNT;
- private long mNextPosMs;
- private long mPrevPosMs;
- private long mPlaybackIntervalMs;
- private int mPlayPosChangedNT;
- private int mFeatures;
- private int mAbsoluteVolume;
- private int mLastSetVolume;
- private int mLastDirection;
- private boolean mVolCmdInProgress;
- private int mAbsVolRetryTimes;
- private int keyPressState;
- private int mAddressedPlayerChangedNT;
- private int mAvailablePlayersChangedNT;
- private int mNowPlayingContentChangedNT;
- private String mRequestedAddressedPlayerPackageName;
- private String mCurrentPath;
- private String mCurrentPathUid;
- private Uri mMediaUri;
- private boolean isMusicAppResponsePending;
- private boolean isBrowsingSupported;
- private boolean isAbsoluteVolumeSupportingDevice;
- public DeviceDependentFeature() {
- mCurrentDevice = null;
- mCurrentPlayState = RemoteControlClient.PLAYSTATE_NONE;
- mPlayStatusChangedNT = NOTIFICATION_TYPE_CHANGED;
- mPlayerStatusChangeNT = NOTIFICATION_TYPE_CHANGED;
- mTrackChangedNT = NOTIFICATION_TYPE_CHANGED;
- mPlaybackIntervalMs = 0L;
- mPlayPosChangedNT = NOTIFICATION_TYPE_CHANGED;
- mFeatures = 0;
- mAbsoluteVolume = -1;
- mLastSetVolume = -1;
- mLastDirection = 0;
- mVolCmdInProgress = false;
- mAbsVolRetryTimes = 0;
- mSkipAmount = 0;
- keyPressState = KEY_STATE_RELEASE; //Key release state
- mAddressedPlayerChangedNT = NOTIFICATION_TYPE_CHANGED;
- mAvailablePlayersChangedNT = NOTIFICATION_TYPE_CHANGED;
- mNowPlayingContentChangedNT = NOTIFICATION_TYPE_CHANGED;
- mRequestedAddressedPlayerPackageName = null;
- mCurrentPath = PATH_INVALID;
- mCurrentPathUid = null;
- mMediaUri = Uri.EMPTY;
- isMusicAppResponsePending = false;
- isBrowsingSupported = false;
- isAbsoluteVolumeSupportingDevice = false;
- }
- };
- private class PlayerSettings {
- public byte attr;
- public byte [] attrIds;
- public String path;
- };
- private PlayerSettings mPlayerSettings = new PlayerSettings();
- private class localPlayerSettings {
- public byte eq_value = 0x01;
- public byte repeat_value = 0x01;
- public byte shuffle_value = 0x01;
- public byte scan_value = 0x01;
- };
- private localPlayerSettings settingValues = new localPlayerSettings();
- private static final String COMMAND = "command";
- private static final String CMDGET = "get";
- private static final String CMDSET = "set";
- private static final String EXTRA_GET_COMMAND = "commandExtra";
- private static final String EXTRA_GET_RESPONSE = "Response";
- private static final int GET_ATTRIBUTE_IDS = 0;
- private static final int GET_VALUE_IDS = 1;
- private static final int GET_ATTRIBUTE_TEXT = 2;
- private static final int GET_VALUE_TEXT = 3;
- private static final int GET_ATTRIBUTE_VALUES = 4;
- private static final int NOTIFY_ATTRIBUTE_VALUES = 5;
- private static final int SET_ATTRIBUTE_VALUES = 6;
- private static final int GET_INVALID = 0xff;
- private static final String EXTRA_ATTRIBUTE_ID = "Attribute";
- private static final String EXTRA_VALUE_STRING_ARRAY = "ValueStrings";
- private static final String EXTRA_ATTRIB_VALUE_PAIRS = "AttribValuePairs";
- private static final String EXTRA_ATTRIBUTE_STRING_ARRAY = "AttributeStrings";
- private static final String EXTRA_VALUE_ID_ARRAY = "Values";
- private static final String EXTRA_ATTIBUTE_ID_ARRAY = "Attributes";
- public static final int VALUE_SHUFFLEMODE_OFF = 1;
- public static final int VALUE_SHUFFLEMODE_ALL = 2;
- public static final int VALUE_REPEATMODE_OFF = 1;
- public static final int VALUE_REPEATMODE_SINGLE = 2;
- public static final int VALUE_REPEATMODE_ALL = 3;
- public static final int VALUE_INVALID = 0;
- public static final int ATTRIBUTE_NOTSUPPORTED = -1;
- public static final int ATTRIBUTE_EQUALIZER = 1;
- public static final int ATTRIBUTE_REPEATMODE = 2;
- public static final int ATTRIBUTE_SHUFFLEMODE = 3;
- public static final int ATTRIBUTE_SCANMODE = 4;
- public static final int NUMPLAYER_ATTRIBUTE = 2;
- private byte [] def_attrib = new byte [] {ATTRIBUTE_REPEATMODE, ATTRIBUTE_SHUFFLEMODE};
- private byte [] value_repmode = new byte [] { VALUE_REPEATMODE_OFF,
- VALUE_REPEATMODE_SINGLE,
- VALUE_REPEATMODE_ALL };
- private byte [] value_shufmode = new byte [] { VALUE_SHUFFLEMODE_OFF,
- VALUE_SHUFFLEMODE_ALL };
- private byte [] value_default = new byte [] {0};
- private final String UPDATE_ATTRIBUTES = "UpdateSupportedAttributes";
- private final String UPDATE_VALUES = "UpdateSupportedValues";
- private final String UPDATE_ATTRIB_VALUE = "UpdateCurrentValues";
- private final String UPDATE_ATTRIB_TEXT = "UpdateAttributesText";
- private final String UPDATE_VALUE_TEXT = "UpdateValuesText";
- private ArrayList <Integer> mPendingCmds;
- private ArrayList <Integer> mPendingSetAttributes;
- DeviceDependentFeature[] deviceFeatures;
- static {
- classInitNative();
- }
- private Avrcp(Context context, A2dpService svc, int maxConnections ) {
- if (DEBUG)
- Log.v(TAG, "Avrcp");
- mAdapter = BluetoothAdapter.getDefaultAdapter();
- mMetadata = new Metadata();
- mTrackNumber = -1L;
- mCurrentPosMs = -1L;
- mPlayStartTimeMs = -1L;
- mSongLengthMs = 0L;
- mA2dpService = svc;
- maxAvrcpConnections = maxConnections;
- deviceFeatures = new DeviceDependentFeature[maxAvrcpConnections];
- mAddressedPlayerId = INVALID_ADDRESSED_PLAYER_ID;
- mCurrentPlayerState = RemoteControlClient.PLAYSTATE_NONE;
- for(int i = 0; i < maxAvrcpConnections; i++) {
- deviceFeatures[i] = new DeviceDependentFeature();
- }
- mContext = context;
- initNative(maxConnections);
- mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
- mAudioStreamMax = mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
- mVolumeStep = Math.max(AVRCP_BASE_VOLUME_STEP, AVRCP_MAX_VOL/mAudioStreamMax);
- }
- private void start() {
- if (DEBUG)
- Log.v(TAG, "start");
- HandlerThread thread = new HandlerThread("BluetoothAvrcpHandler");
- thread.start();
- Looper looper = thread.getLooper();
- mHandler = new AvrcpMessageHandler(looper);
- mPendingCmds = new ArrayList<Integer>();
- mPendingSetAttributes = new ArrayList<Integer>();
- // clear path for all devices
- for (int i = 0; i < maxAvrcpConnections; i++) {
- deviceFeatures[i].mCurrentPath = PATH_INVALID;
- deviceFeatures[i].mCurrentPathUid = null;
- deviceFeatures[i].mMediaUri = Uri.EMPTY;
- }
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(AudioManager.RCC_CHANGED_ACTION);
- intentFilter.addAction(PLAYERSETTINGS_RESPONSE);
- try {
- mContext.registerReceiver(mIntentReceiver, intentFilter);
- }catch (Exception e) {
- Log.e(TAG,"Unable to register Avrcp receiver", e);
- }
- registerMediaPlayers();
- mRemoteControllerCb = new RemoteControllerWeak(mHandler);
- mAvrcpRemoteControllerCb = new AvrcpRemoteControllerWeak(mHandler);
- mRemoteController = new RemoteController(mContext, mRemoteControllerCb,
- null, mAvrcpRemoteControllerCb);
- mAudioManager.registerRemoteController(mRemoteController);
- mRemoteController.setSynchronizationMode(RemoteController.POSITION_SYNCHRONIZATION_CHECK);
- }
- //Listen to intents from MediaPlayer and Audio Manager and update data structures
- private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(AudioManager.RCC_CHANGED_ACTION)) {
- Log.v(TAG, "received RCC_CHANGED_ACTION");
- int isRCCFocussed = 0;
- int isRCCAvailable = 0;
- String callingPackageName =
- intent.getStringExtra(AudioManager.EXTRA_CALLING_PACKAGE_NAME);
- boolean isFocussed =
- intent.getBooleanExtra(AudioManager.EXTRA_FOCUS_CHANGED_VALUE,
- false);
- boolean isAvailable =
- intent.getBooleanExtra(AudioManager.EXTRA_AVAILABLITY_CHANGED_VALUE,
- false);
- if (isFocussed)
- isRCCFocussed = 1;
- if (isAvailable)
- isRCCAvailable = 1;
- Log.v(TAG, "focus: " + isFocussed + " , availability: " + isAvailable);
- if (mHandler != null) {
- mHandler.obtainMessage(MSG_UPDATE_RCC_CHANGE, isRCCFocussed,
- isRCCAvailable, callingPackageName).sendToTarget();
- }
- } else if (action.equals(PLAYERSETTINGS_RESPONSE)) {
- int getResponse = intent.getIntExtra(EXTRA_GET_RESPONSE,
- GET_INVALID);
- byte [] data;
- String [] text;
- boolean isSetAttrValRsp = false;
- BluetoothDevice device = null;
- synchronized (mPendingCmds) {
- Integer val = new Integer(getResponse);
- if (mPendingCmds.contains(val)) {
- if (getResponse == SET_ATTRIBUTE_VALUES) {
- isSetAttrValRsp = true;
- if (DEBUG) Log.v(TAG,"Response received for SET_ATTRIBUTE_VALUES");
- }
- mHandler.removeMessages(MESSAGE_PLAYERSETTINGS_TIMEOUT);
- mPendingCmds.remove(val);
- }
- }
- for (int i = 0; i < maxAvrcpConnections; i++) {
- if (deviceFeatures[i].isMusicAppResponsePending ==
- true) {
- device = deviceFeatures[i].mCurrentDevice;
- deviceFeatures[i].isMusicAppResponsePending = false;
- break;
- }
- }
- if (DEBUG)
- Log.v(TAG,"getResponse" + getResponse);
- switch (getResponse) {
- case GET_ATTRIBUTE_IDS:
- if (device == null) {
- Log.e(TAG,"ERROR!!! device is null");
- return;
- }
- data = intent.getByteArrayExtra(EXTRA_ATTIBUTE_ID_ARRAY);
- byte numAttr = (byte) data.length;
- if (DEBUG)
- Log.v(TAG,"GET_ATTRIBUTE_IDS");
- getListPlayerappAttrRspNative(numAttr ,
- data ,getByteAddress(device));
- break;
- case GET_VALUE_IDS:
- if (device == null) {
- Log.e(TAG,"ERROR!!! device is null");
- return;
- }
- data = intent.getByteArrayExtra(EXTRA_VALUE_ID_ARRAY);
- numAttr = (byte) data.length;
- if (DEBUG)
- Log.v(TAG,"GET_VALUE_IDS" + numAttr);
- getPlayerAppValueRspNative(numAttr, data,
- getByteAddress(device));
- break;
- case GET_ATTRIBUTE_VALUES:
- if (device == null) {
- Log.e(TAG,"ERROR!!! device is null");
- return;
- }
- data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS);
- updateLocalPlayerSettings(data);
- numAttr = (byte) data.length;
- if (DEBUG)
- Log.v(TAG,"GET_ATTRIBUTE_VALUES" + numAttr);
- SendCurrentPlayerValueRspNative(numAttr ,
- data, getByteAddress(device));
- break;
- case SET_ATTRIBUTE_VALUES:
- data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS);
- updateLocalPlayerSettings(data);
- if (isSetAttrValRsp) {
- isSetAttrValRsp = false;
- for (int i = 0; i < maxAvrcpConnections; i++) {
- if (deviceFeatures[i].mCurrentDevice != null) {
- Log.v(TAG,"Respond to SET_ATTRIBUTE_VALUES request");
- if (checkPlayerAttributeResponse(data)) {
- SendSetPlayerAppRspNative(OPERATION_SUCCESSFUL,
- getByteAddress(deviceFeatures[i].mCurrentDevice));
- } else {
- SendSetPlayerAppRspNative(INTERNAL_ERROR,
- getByteAddress(deviceFeatures[i].mCurrentDevice));
- }
- }
- }
- mPendingSetAttributes.clear();
- }
- for (int i = 0; i < maxAvrcpConnections; i++) {
- if (deviceFeatures[i].mPlayerStatusChangeNT ==
- NOTIFICATION_TYPE_INTERIM) {
- Log.v(TAG,"device has registered for"+
- "mPlayerStatusChangeNT");
- deviceFeatures[i].mPlayerStatusChangeNT =
- NOTIFICATION_TYPE_CHANGED;
- sendPlayerAppChangedRsp(deviceFeatures[i].mPlayerStatusChangeNT,
- deviceFeatures[i].mCurrentDevice);
- } else {
- Log.v(TAG,"Drop Set Attr Val update from media player");
- }
- }
- break;
- case GET_ATTRIBUTE_TEXT:
- text = intent.getStringArrayExtra(EXTRA_ATTRIBUTE_STRING_ARRAY);
- if (device == null) {
- Log.e(TAG,"ERROR!!! device is null");
- return;
- }
- sendSettingsTextRspNative(mPlayerSettings.attrIds.length ,
- mPlayerSettings.attrIds ,text.length,
- text, getByteAddress(device));
- if (DEBUG)
- Log.v(TAG,"mPlayerSettings.attrIds"
- + mPlayerSettings.attrIds.length);
- break;
- case GET_VALUE_TEXT:
- text = intent.getStringArrayExtra(EXTRA_VALUE_STRING_ARRAY);
- if (device == null) {
- Log.e(TAG,"ERROR!!! device is null");
- return;
- }
- sendValueTextRspNative(mPlayerSettings.attrIds.length ,
- mPlayerSettings.attrIds,
- text.length, text,
- getByteAddress(device));
- break;
- }
- }
- }
- };
- /* This method is used for create entries of existing media players on RCD start
- * Later when media players become avaialable corresponding entries
- * are marked accordingly and similarly when media players changes focus
- * the corresponding fields are modified */
- private void registerMediaPlayers () {
- if (DEBUG)
- Log.v(TAG, "registerMediaPlayers");
- int[] featureMasks = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
- byte[] playerName1 = {0x4d, 0x75, 0x73, 0x69, 0x63}/*Music*/;
- featureMasks[FEATURE_MASK_PLAY_OFFSET] =
- featureMasks[FEATURE_MASK_PLAY_OFFSET] | FEATURE_MASK_PLAY_MASK;
- featureMasks[FEATURE_MASK_PAUSE_OFFSET] =
- featureMasks[FEATURE_MASK_PAUSE_OFFSET] | FEATURE_MASK_PAUSE_MASK;
- featureMasks[FEATURE_MASK_STOP_OFFSET] =
- featureMasks[FEATURE_MASK_STOP_OFFSET] | FEATURE_MASK_STOP_MASK;
- featureMasks[FEATURE_MASK_PAGE_UP_OFFSET] =
- featureMasks[FEATURE_MASK_PAGE_UP_OFFSET] | FEATURE_MASK_PAGE_UP_MASK;
- featureMasks[FEATURE_MASK_PAGE_DOWN_OFFSET] =
- featureMasks[FEATURE_MASK_PAGE_DOWN_OFFSET] | FEATURE_MASK_PAGE_DOWN_MASK;
- featureMasks[FEATURE_MASK_REWIND_OFFSET] =
- featureMasks[FEATURE_MASK_REWIND_OFFSET] | FEATURE_MASK_REWIND_MASK;
- featureMasks[FEATURE_MASK_FAST_FWD_OFFSET] =
- featureMasks[FEATURE_MASK_FAST_FWD_OFFSET] | FEATURE_MASK_FAST_FWD_MASK;
- featureMasks[FEATURE_MASK_VENDOR_OFFSET] =
- featureMasks[FEATURE_MASK_VENDOR_OFFSET] | FEATURE_MASK_VENDOR_MASK;
- featureMasks[FEATURE_MASK_ADV_CTRL_OFFSET] =
- featureMasks[FEATURE_MASK_ADV_CTRL_OFFSET] | FEATURE_MASK_ADV_CTRL_MASK;
- featureMasks[FEATURE_MASK_BROWSE_OFFSET] =
- featureMasks[FEATURE_MASK_BROWSE_OFFSET] | FEATURE_MASK_BROWSE_MASK;
- featureMasks[FEATURE_MASK_NOW_PLAY_OFFSET] =
- featureMasks[FEATURE_MASK_NOW_PLAY_OFFSET] | FEATURE_MASK_NOW_PLAY_MASK;
- featureMasks[FEATURE_MASK_BR_WH_ADDR_OFFSET] =
- featureMasks[FEATURE_MASK_BR_WH_ADDR_OFFSET] | FEATURE_MASK_BR_WH_ADDR_MASK;
- mediaPlayerInfo1 = new MediaPlayerInfo ((short)0x0001,
- MAJOR_TYPE_AUDIO,
- SUB_TYPE_NONE,
- (byte)RemoteControlClient.PLAYSTATE_PAUSED,
- CHAR_SET_UTF8,
- (short)0x05,
- playerName1,
- "com.android.music",
- true,
- featureMasks);
- mMediaPlayers.add(mediaPlayerInfo1);
- }
- public static Avrcp make(Context context, A2dpService svc,
- int maxConnections) {
- if (DEBUG)
- Log.v(TAG, "make");
- Avrcp ar = new Avrcp(context, svc, maxConnections);
- ar.start();
- return ar;
- }
- public void doQuit() {
- if (DEBUG)
- Log.v(TAG, "doQuit");
- mHandler.removeCallbacksAndMessages(null);
- Looper looper = mHandler.getLooper();
- if (looper != null) {
- looper.quit();
- }
- mAudioManager.unregisterRemoteController(mRemoteController);
- clearDeviceDependentFeature();
- for (int i = 0; i < maxAvrcpConnections; i++) {
- cleanupDeviceFeaturesIndex(i);
- }
- try {
- mContext.unregisterReceiver(mIntentReceiver);
- }catch (Exception e) {
- Log.e(TAG,"Unable to unregister Avrcp receiver", e);
- }
- mMediaPlayers.clear();
- if (mHandler.hasMessages(MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT)) {
- mHandler.removeMessages(MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOUT);
- if (DEBUG)
- Log.v(TAG, "Addressed player message cleanup as part of doQuit");
- }
- }
- public void clearDeviceDependentFeature() {
- for (int i = 0; i < maxAvrcpConnections; i++) {
- deviceFeatures[i].keyPressState = KEY_STATE_RELEASE; //Key release state
- deviceFeatures[i].mCurrentPath = PATH_INVALID;
- deviceFeatures[i].mMediaUri = Uri.EMPTY;
- deviceFeatures[i].mCurrentPathUid = null;
- deviceFeatures[i].mRequestedAddressedPlayerPackageName = null;
- }
- }
- public void cleanup() {
- if (DEBUG)
- Log.v(TAG, "cleanup");
- cleanupNative();
- }
- private static class RemoteControllerWeak implements RemoteController.OnClientUpdateListener {
- private final WeakReference<Handler> mLocalHandler;
- public RemoteControllerWeak(Handler handler) {
- mLocalHandler = new WeakReference<Handler>(handler);
- }
- @Override
- public void onClientChange(boolean clearing) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_GENERATION_ID,
- 0, (clearing ? 1 : 0), null).sendToTarget();
- }
- }
- @Override
- public void onClientPlaybackStateUpdate(int state) {
- // Should never be called with the existing code, but just in case
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_UPDATE_STATE, 0, state,
- new Long(RemoteControlClient.PLAYBACK_POSITION_INVALID)).sendToTarget();
- }
- }
- @Override
- public void onClientPlaybackStateUpdate(int state, long stateChangeTimeMs,
- long currentPosMs, float speed) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_UPDATE_STATE, 0, state,
- new Long(currentPosMs)).sendToTarget();
- }
- }
- @Override
- public void onClientTransportControlUpdate(int transportControlFlags) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_TRANSPORT_CONTROLS, 0, transportControlFlags)
- .sendToTarget();
- }
- }
- @Override
- public void onClientMetadataUpdate(MetadataEditor metadataEditor) {
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_SET_METADATA, 0, 0, metadataEditor).sendToTarget();
- }
- }
- }
- private static class AvrcpRemoteControllerWeak implements
- RemoteController.OnClientAvrcpUpdateListener {
- private final WeakReference<Handler> mLocalHandler;
- public AvrcpRemoteControllerWeak(Handler handler) {
- mLocalHandler = new WeakReference<Handler>(handler);
- }
- @Override
- public void onClientFolderInfoBrowsedPlayer(String stringUri) {
- Log.v(TAG, "onClientFolderInfoBrowsedPlayer: stringUri: " + stringUri);
- Handler handler = mLocalHandler.get();
- if (stringUri != null) {
- String[] ExternalPath = stringUri.split("/");
- if (ExternalPath.length < 4) {
- Log.d(TAG, "Wrong entries.");
- handler.obtainMessage(MSG_UPDATE_BROWSED_PLAYER_FOLDER, 0, INTERNAL_ERROR,
- null).sendToTarget();
- return;
- }
- Uri uri = Uri.parse(stringUri);
- Log.v(TAG, "URI received: " + uri);
- String[] SplitPath = new String[ExternalPath.length - 3];
- for (int count = 2; count < (ExternalPath.length - 1); count++) {
- SplitPath[count - 2] = ExternalPath[count];
- Log.d(TAG, "SplitPath[" + (count - 2) + "] = " + SplitPath[count - 2]);
- }
- Log.v(TAG, "folderDepth: " + SplitPath.length);
- for (int count = 0; count < SplitPath.length; count++) {
- Log.v(TAG, "folderName: " + SplitPath[count]);
- }
- mMediaUriStatic = uri;
- if (handler != null) {
- // Don't send the complete path to CK as few gets confused by that
- // Send only the name of the root folder
- handler.obtainMessage(MSG_UPDATE_BROWSED_PLAYER_FOLDER, NUM_ROOT_ELEMENTS,
- OPERATION_SUCCESSFUL, SplitPath).sendToTarget();
- }
- } else {
- handler.obtainMessage(MSG_UPDATE_BROWSED_PLAYER_FOLDER, 0, INTERNAL_ERROR,
- null).sendToTarget();
- }
- }
- @Override
- public void onClientUpdateNowPlayingEntries(long[] playList) {
- Log.v(TAG, "onClientUpdateNowPlayingEntries");
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_NOW_PLAYING_ENTRIES_RECEIVED, 0, 0,
- playList).sendToTarget();
- }
- }
- @Override
- public void onClientNowPlayingContentChange() {
- Log.v(TAG, "onClientNowPlayingContentChange");
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED).sendToTarget();
- }
- }
- @Override
- public void onClientPlayItemResponse(boolean success) {
- Log.v(TAG, "onClientPlayItemResponse");
- Handler handler = mLocalHandler.get();
- if (handler != null) {
- handler.obtainMessage(MSG_PLAY_ITEM_RESPONSE, 0, 0, new Boolean(success))
- .sendToTarget();
- }
- }
- }
- /* Handles Avrcp messages. */
- private final class AvrcpMessageHandler extends Handler {
- private AvrcpMessageHandler(Looper looper) {
- super(looper);
- }
- @Override
- public void handleMessage(Message msg) {
- int deviceIndex = INVALID_DEVICE_INDEX;
- switch (msg.what) {
- case MESSAGE_PLAYERSETTINGS_TIMEOUT:
- Log.e(TAG, "**MESSAGE_PLAYSTATUS_TIMEOUT: Addr: " +
- (String)msg.obj + " Msg: " + msg.arg1);
- synchronized (mPendingCmds) {
- Integer val = new Integer(msg.arg1);
- if (!mPendingCmds.contains(val)) {
- break;
- }
- mPendingCmds.remove(val);
- }
- switch (msg.arg1) {
- case GET_ATTRIBUTE_IDS:
- getListPlayerappAttrRspNative((byte)def_attrib.length ,
- def_attrib, getByteAddress(
- mAdapter.getRemoteDevice((String) msg.obj)));
- break;
- case GET_VALUE_IDS:
- switch (mPlayerSettings.attr) {
- case ATTRIBUTE_REPEATMODE:
- getPlayerAppValueRspNative((byte)value_repmode.length,
- value_repmode,
- getByteAddress(mAdapter.getRemoteDevice(
- (String) msg.obj)));
- break;
- case ATTRIBUTE_SHUFFLEMODE:
- getPlayerAppValueRspNative((byte)value_shufmode.length,
- value_shufmode,
- getByteAddress(mAdapter.getRemoteDevice(
- (String) msg.obj)));
- break;
- default:
- getPlayerAppValueRspNative((byte)value_default.length,
- value_default,
- getByteAddress(mAdapter.getRemoteDevice(
- (String) msg.obj)));
- break;
- }
- break;
- case GET_ATTRIBUTE_VALUES:
- int j = 0;
- byte [] retVal = new byte [mPlayerSettings.attrIds.length*2];
- for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
- retVal[j++] = mPlayerSettings.attrIds[i];
- if (mPlayerSettings.attrIds[i] == ATTRIBUTE_REPEATMODE) {
- retVal[j++] = settingValues.repeat_value;
- } else if (mPlayerSettings.attrIds[i] == ATTRIBUTE_SHUFFLEMODE) {
- retVal[j++] = settingValues.shuffle_value;
- } else {
- retVal[j++] = 0x0;
- }
- }
- SendCurrentPlayerValueRspNative((byte)retVal.length ,
- retVal, getByteAddress(mAdapter.getRemoteDevice(
- (String) msg.obj)));
- break;
- case SET_ATTRIBUTE_VALUES :
- SendSetPlayerAppRspNative(INTERNAL_ERROR, getByteAddress(
- mAdapter.getRemoteDevice((String) msg.obj)));
- break;
- case GET_ATTRIBUTE_TEXT:
- String [] attribText = new String [mPlayerSettings.attrIds.length];
- for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
- attribText[i] = "";
- }
- sendSettingsTextRspNative(mPlayerSettings.attrIds.length ,
- mPlayerSettings.attrIds, attribText.length,
- attribText, getByteAddress(mAdapter.getRemoteDevice(
- (String) msg.obj)));
- break;
- case GET_VALUE_TEXT:
- String [] valueText = new String [mPlayerSettings.attrIds.length];
- for (int i = 0; i < mPlayerSettings.attrIds.length; i++) {
- valueText[i] = "";
- }
- sendValueTextRspNative(mPlayerSettings.attrIds.length ,
- mPlayerSettings.attrIds, valueText.length,
- valueText,getByteAddress(mAdapter.getRemoteDevice(
- (String) msg.obj)));
- break;
- default :
- Log.e(TAG,"in default case");
- break;
- }
- break;
- case MSG_UPDATE_STATE:
- /* since we get this from music app we need to update
- * current playing start time */
- Log.i(TAG,"State change for music app");
- updatePlayPauseState(msg.arg2, ((Long) msg.obj).longValue(),
- null);
- break;
- case MSG_SET_METADATA:
- updateMetadata((MetadataEditor) msg.obj);
- break;
- case MSG_UPDATE_AVAILABLE_PLAYERS:
- updateAvailableMediaPlayers();
- break;
- case MSG_UPDATE_ADDRESSED_PLAYER:
- updateAddressedMediaPlayer(msg.arg1);
- break;
- case MSG_UPDATE_BROWSED_PLAYER_FOLDER:
- Log.v(TAG, "MSG_UPDATE_BROWSED_PLAYER_FOLDER");
- updateBrowsedPlayerFolder(msg.arg1, msg.arg2, (String [])msg.obj);
- break;
- case MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED:
- Log.v(TAG, "MSG_UPDATE_NOW_PLAYING_CONTENT_CHANGED");
- updateNowPlayingContentChanged();
- break;
- case MSG_PLAY_ITEM_RESPONSE:
- Log.v(TAG, "MSG_PLAY_ITEM_RESPONSE");
- boolean success = ((Boolean)msg.obj).booleanValue();
- Log.v(TAG, "success: " + success);
- updatePlayItemResponse(success);
- break;
- case MSG_NOW_PLAYING_ENTRIES_RECEIVED:
- Log.v(TAG, "MSG_NOW_PLAYING_ENTRIES_RECEIVED");
- updateNowPlayingEntriesReceived((long [])msg.obj);
- break;
- case MSG_SET_TRANSPORT_CONTROLS:
- updateTransportControls(msg.arg2);
- break;
- case MSG_SET_GENERATION_ID:
- if (DEBUG)
- Log.v(TAG, "New genId = " + msg.arg1 +
- ", clearing = " + msg.arg2);
- break;
- case MESSAGE_GET_RC_FEATURES:
- {
- String address = (String) msg.obj;
- if (DEBUG)
- Log.v(TAG, "MESSAGE_GET_RC_FEATURES: address="+address+
- ", features="+msg.arg1);
- BluetoothDevice device = mAdapter.getRemoteDevice(address);
- deviceIndex = getIndexForDevice(device);
- if (deviceIndex == INVALID_DEVICE_INDEX) {
- Log.v(TAG,"device entry not present, bailing out");
- return;
- }
- deviceFeatures[deviceIndex].mFeatures = msg.arg1;
- deviceFeatures[deviceIndex].isAbsoluteVolumeSupportingDevice =
- ((deviceFeatures[deviceIndex].mFeatures &
- BTRC_FEAT_ABSOLUTE_VOLUME) != 0);
- mAudioManager.avrcpSupportsAbsoluteVolume(device.getAddress(),
- isAbsoluteVolumeSupported());
- Log.v(TAG," update audio manager for abs vol state = "
- + isAbsoluteVolumeSupported());
- if ((deviceFeatures[deviceIndex].mFeatures &
- BTRC_FEAT_AVRC_UI_UPDATE) != 0)
- {
- int NOTIFICATION_ID = android.R.drawable.stat_sys_data_bluetooth;
- Notification notification = new Notification.Builder(mContext)
- .setContentTitle("Bluetooth Media Browsing")
- .setContentText("Peer supports advanced feature")
- .setSubText("Re-pair from peer to enable it")
- .setSmallIcon(android.R.drawable.stat_sys_data_bluetooth)
- .setDefaults(Notification.DEFAULT_ALL)
- .build();
- NotificationManager manager = (NotificationManager)
- mContext.getSystemService(Context.NOTIFICATION_SERVICE);
- manager.notify(NOTIFICATION_ID, notification);
- Log.v(TAG," update notification manager on remote repair request");
- }
- break;
- }
- case MESSAGE_GET_PLAY_STATUS:
- {
- BluetoothDevice device;
- int playState, position;
- if (DEBUG)
- Log.v(TAG, "MESSAGE_GET_PLAY_STATUS");
- Log.v(TAG, "Event for device address " + (String)msg.obj);
- device = mAdapter.getRemoteDevice((String) msg.obj);
- deviceIndex = getIndexForDevice(device);
- if (deviceIndex == INVALID_DEVICE_INDEX) {
- Log.e(TAG,"Invalid device index for play status");
- break;
- }
- playState = convertPlayStateToPlayStatus(deviceFeatures[deviceIndex].mCurrentPlayState);
- position = (int)getPlayPosition(device);
- Log.v(TAG, "Play Status for : " + device.getName() +
- " state: " + playState + " position: " + position);
- if (position == -1) {
- Log.v(TAG, "Force play postion to 0, for getPlayStatus Rsp");
- position = 0;
- }
- getPlayStatusRspNative(playState, (int)mSongLengthMs, position,
- getByteAddress(device));
- break;
- }
- case MESSAGE_GET_ELEM_ATTRS:
- {
- String[] textArray;
- int[] attrIds;
- byte numAttr = (byte) msg.arg1;
- ItemAttr itemAttr = (ItemAttr)msg.obj;
- Log.v(TAG, "event for device address " + itemAttr.mAddress);
- ArrayList<Integer> attrList = itemAttr.mAttrList;
- if (DEBUG)
- Log.v(TAG, "MESSAGE_GET_ELEM_ATTRS:numAttr=" + numAttr);
- attrIds = new int[numAttr];
- textArray = new String[numAttr];
- for (int i = 0; i < numAttr; ++i) {
- attrIds[i] = attrList.get(i).intValue();
- textArray[i] = getAttributeString(attrIds[i]);
- }
- getElementAttrRspNative(numAttr ,attrIds ,textArray ,
- getByteAddress(mAdapter.getRemoteDevice(itemAttr.mAddress)));
- break;
- }
- case MESSAGE_REGISTER_NOTIFICATION:
- if (DEBUG)
- Log.v(TAG, "MESSAGE_REGISTER_NOTIFICATION:event=" + msg.arg1 +
- " param=" + msg.arg2);
- processRegisterNotification(msg.arg1, msg.arg2, (String) msg.obj);
- break;
- case MESSAGE_PLAY_INTERVAL_TIMEOUT:
- if (DEBUG)
- Log.v(TAG, "MESSAGE_PLAY_INTERVAL_TIMEOUT");
- Log.v(TAG, "event for device address " + (BluetoothDevice)msg.obj);
- deviceIndex = getIndexForDevice((BluetoothDevice) msg.obj);
- if (deviceIndex == INVALID_DEVICE_INDEX) {
- Log.e(TAG,"invalid index for device");
- break;
- }
- deviceFeatures[deviceIndex].mPlayPosChangedNT =
- NOTIFICATION_TYPE_CHANGED;
- Log.v(TAG, "event for device address " + (BluetoothDevice) msg.obj);
- registerNotificationRspPlayPosNative(deviceFeatures[deviceIndex].mPlayPosChangedNT,
- (int)getPlayPosition((BluetoothDevice) msg.obj) ,
- getByteAddress((BluetoothDevice) msg.obj));
- break;
- case MESSAGE_SET_ADDR_PLAYER_REQ_TIMEOU