diff options
author | Steve Kondik <steve@cyngn.com> | 2014-12-18 16:40:16 -0800 |
---|---|---|
committer | Steve Kondik <steve@cyngn.com> | 2014-12-18 16:40:16 -0800 |
commit | 73b826f84b9a6337b3d36b2b1aed0ebff997421c (patch) | |
tree | 651cc6ebd91cc4e6f084a67cb9b12638a561e5a4 | |
parent | 7897776ccedf883cd137ca8ccab6d180ba243e0f (diff) | |
parent | 2b582a6914f8e13a261b5a66567517ac62074f74 (diff) | |
download | android_packages_apps_Bluetooth-73b826f84b9a6337b3d36b2b1aed0ebff997421c.tar.gz android_packages_apps_Bluetooth-73b826f84b9a6337b3d36b2b1aed0ebff997421c.tar.bz2 android_packages_apps_Bluetooth-73b826f84b9a6337b3d36b2b1aed0ebff997421c.zip |
Merge branch 'LA.BF.1.1_rb1.16' of git://codeaurora.org/platform/packages/apps/Bluetooth into cm-12.0
24 files changed, 689 insertions, 339 deletions
diff --git a/jni/com_android_bluetooth_avrcp.cpp b/jni/com_android_bluetooth_avrcp.cpp index eaed3bd5d..a8ff2c986 100644 --- a/jni/com_android_bluetooth_avrcp.cpp +++ b/jni/com_android_bluetooth_avrcp.cpp @@ -304,8 +304,8 @@ static void btavrcp_volume_change_callback(uint8_t volume, uint8_t ctype) { static void btavrcp_get_folder_items_callback(btrc_browse_folderitem_t scope , btrc_getfolderitem_t *param) { - jint start = param->start_item; - jint end = param->end_item; + jlong start = param->start_item; + jlong end = param->end_item; jint size = param->size; jint num_attr = param->attr_count; jintArray attrs; @@ -516,7 +516,7 @@ static void classInitNative(JNIEnv* env, jclass clazz) { env->GetMethodID(clazz, "setAddressedPlayer", "(I)V"); //getFolderItems: attributes to pass: Scope, Start, End, Attr Cnt method_getFolderItems = - env->GetMethodID(clazz, "getFolderItems", "(BIIII[I)V"); + env->GetMethodID(clazz, "getFolderItems", "(BJJII[I)V"); method_setBrowsedPlayer = env->GetMethodID(clazz, "setBrowsedPlayer", "(I)V"); method_changePath = @@ -1081,7 +1081,7 @@ static jboolean registerNotificationRspNowPlayingContentChangedNative(JNIEnv *en } static jboolean getFolderItemsRspNative(JNIEnv *env, jobject object, jbyte statusCode, - jint numItems, jintArray itemType, jlongArray uid, jintArray type, + jlong numItems, jintArray itemType, jlongArray uid, jintArray type, jbyteArray playable, jobjectArray displayName, jbyteArray numAtt, jobjectArray attValues, jintArray attIds) { bt_status_t status = BT_STATUS_SUCCESS; @@ -1158,6 +1158,10 @@ static jboolean getFolderItemsRspNative(JNIEnv *env, jobject object, jbyte statu param.p_item_list[count].u.folder.playable = playableElements[count]; text = (jstring) env->GetObjectArrayElement(displayName, count); + if (text == NULL) { + ALOGE("getFolderItemsRspNative: App string is NULL, bail out"); + break; + } utfStringLength = env->GetStringUTFLength(text); if (!utfStringLength) { ALOGE("getFolderItemsRspNative: GetStringUTFLength return NULL"); @@ -1186,6 +1190,10 @@ static jboolean getFolderItemsRspNative(JNIEnv *env, jobject object, jbyte statu param.p_item_list[count].u.media.type = (uint8_t)typeElements[count]; ALOGI("getFolderItemsRspNative: type: %d", param.p_item_list[count].u.folder.type); text = (jstring) env->GetObjectArrayElement(displayName, count); + if (text == NULL) { + ALOGE("getFolderItemsRspNative: App string is NULL, bail out"); + break; + } utfStringLength = env->GetStringUTFLength(text); if (!utfStringLength) { ALOGE("getFolderItemsRspNative: GetStringUTFLength return NULL"); @@ -1213,6 +1221,10 @@ static jboolean getFolderItemsRspNative(JNIEnv *env, jobject object, jbyte statu for (int i = 0; i < numAttElements[count]; i++) { text = (jstring) env->GetObjectArrayElement(attValues, (7 * count) + i); + if (text == NULL) { + ALOGE("getFolderItemsRspNative: Attribute string is NULL, continue to next"); + continue; + } utfStringLength = env->GetStringUTFLength(text); if (!utfStringLength) { ALOGE("getFolderItemsRspNative: GetStringUTFLength return NULL"); @@ -1594,7 +1606,7 @@ static JNINativeMethod sMethods[] = { {"changePathRspNative", "(IJ)Z", (void *) changePathRspNative}, {"playItemRspNative", "(I)Z", (void *) playItemRspNative}, {"getItemAttrRspNative", "(B[I[Ljava/lang/String;)Z", (void *) getItemAttrRspNative}, - {"getFolderItemsRspNative", "(BI[I[J[I[B[Ljava/lang/String;[B[Ljava/lang/String;[I)Z", + {"getFolderItemsRspNative", "(BJ[I[J[I[B[Ljava/lang/String;[B[Ljava/lang/String;[I)Z", (void *) getFolderItemsRspNative}, }; diff --git a/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java b/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java index 2f1f3d9cc..13c4ade46 100644 --- a/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java +++ b/src/com/android/bluetooth/a2dp/A2dpSinkStateMachine.java @@ -619,7 +619,7 @@ final class A2dpSinkStateMachine extends StateMachine { mPlayingDevice = null; broadcastAudioState(device, BluetoothA2dpSink.STATE_NOT_PLAYING, BluetoothA2dpSink.STATE_PLAYING); - if ((mAudioFocusAcquired == AUDIO_FOCUS_LOSS_TRANSIENT) || + if ((mAudioFocusAcquired == AUDIO_FOCUS_LOSS_TRANSIENT) && (state == AUDIO_STATE_REMOTE_SUSPEND)) { log(" Dont't Loose audiofocus in case of suspend "); break; diff --git a/src/com/android/bluetooth/avrcp/Avrcp.java b/src/com/android/bluetooth/avrcp/Avrcp.java index 095f0ed6c..3b7a4bfc6 100644 --- a/src/com/android/bluetooth/avrcp/Avrcp.java +++ b/src/com/android/bluetooth/avrcp/Avrcp.java @@ -310,6 +310,7 @@ public final class Avrcp { private final String UPDATE_ATTRIB_TEXT = "UpdateAttributesText"; private final String UPDATE_VALUE_TEXT = "UpdateValuesText"; private ArrayList <Integer> mPendingCmds; + private ArrayList <Integer> mPendingSetAttributes; static { classInitNative(); @@ -359,6 +360,7 @@ public final class Avrcp { Looper looper = thread.getLooper(); mHandler = new AvrcpMessageHandler(looper); mPendingCmds = new ArrayList<Integer>(); + mPendingSetAttributes = new ArrayList<Integer>(); mCurrentPath = PATH_INVALID; mCurrentPathUid = null; mMediaUri = Uri.EMPTY; @@ -402,14 +404,19 @@ public final class Avrcp { GET_INVALID); byte [] data; String [] text; + boolean isSetAttrValRsp = false; 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); } } - if (DEBUG) Log.v(TAG,"getResponse" + getResponse); + if (DEBUG) Log.v(TAG,"getResponse " + getResponse); switch (getResponse) { case GET_ATTRIBUTE_IDS: data = intent.getByteArrayExtra(EXTRA_ATTIBUTE_ID_ARRAY); @@ -420,35 +427,34 @@ public final class Avrcp { case GET_VALUE_IDS: data = intent.getByteArrayExtra(EXTRA_VALUE_ID_ARRAY); numAttr = (byte) data.length; - if (DEBUG) Log.v(TAG,"GET_VALUE_IDS" + numAttr); + if (DEBUG) Log.v(TAG,"GET_VALUE_IDS " + numAttr); getPlayerAppValueRspNative(numAttr, data); break; case GET_ATTRIBUTE_VALUES: data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS); updateLocalPlayerSettings(data); numAttr = (byte) data.length; - if (DEBUG) Log.v(TAG,"GET_ATTRIBUTE_VALUES" + numAttr); + if (DEBUG) Log.v(TAG,"GET_ATTRIBUTE_VALUES " + numAttr); SendCurrentPlayerValueRspNative(numAttr, data); break; case SET_ATTRIBUTE_VALUES: data = intent.getByteArrayExtra(EXTRA_ATTRIB_VALUE_PAIRS); updateLocalPlayerSettings(data); - Log.v(TAG,"SET_ATTRIBUTE_VALUES: " + data[0] + ", " + data[1]); - if (data[0] == ATTRIBUTE_EQUALIZER || - data[0] == ATTRIBUTE_REPEATMODE || - data[0] == ATTRIBUTE_SHUFFLEMODE) { - if (mPlayerStatusChangeNT == NOTIFICATION_TYPE_INTERIM) { - Log.v(TAG,"Send Player appl attribute changed response"); - mPlayerStatusChangeNT = NOTIFICATION_TYPE_CHANGED; - sendPlayerAppChangedRsp(mPlayerStatusChangeNT); + Log.v(TAG,"SET_ATTRIBUTE_VALUES: "); + if (isSetAttrValRsp){ + isSetAttrValRsp = false; + Log.v(TAG,"Respond to SET_ATTRIBUTE_VALUES request"); + if (checkPlayerAttributeResponse(data)) { + SendSetPlayerAppRspNative(OPERATION_SUCCESSFUL); } else { - Log.v(TAG,"Respond to SET_ATTRIBUTE_VALUES request"); - if (data[1] == ATTRIBUTE_NOTSUPPORTED) { - SendSetPlayerAppRspNative(INTERNAL_ERROR); - } else { - SendSetPlayerAppRspNative(OPERATION_SUCCESSFUL); - } + SendSetPlayerAppRspNative(INTERNAL_ERROR); } + } else if (mPlayerStatusChangeNT == NOTIFICATION_TYPE_INTERIM) { + Log.v(TAG,"Send Player appl attribute changed response"); + mPlayerStatusChangeNT = NOTIFICATION_TYPE_CHANGED; + sendPlayerAppChangedRsp(mPlayerStatusChangeNT); + } else { + Log.v(TAG,"Drop Set Attr Val update from media player"); } break; case GET_ATTRIBUTE_TEXT: @@ -1259,8 +1265,8 @@ public final class Avrcp { void updateNowPlayingEntriesReceived(long[] playList) { int status = OPERATION_SUCCESSFUL; int numItems = 0; - int reqItems = (mCachedRequest.mEnd - mCachedRequest.mStart) + 1; - int availableItems = 0; + long reqItems = (mCachedRequest.mEnd - mCachedRequest.mStart) + 1; + long availableItems = 0; Cursor cursor = null; int[] itemType = new int[MAX_BROWSE_ITEM_TO_SEND]; long[] uid = new long[MAX_BROWSE_ITEM_TO_SEND]; @@ -1288,7 +1294,7 @@ public final class Avrcp { return; } - if ((mCachedRequest.mStart < 0) || (mCachedRequest.mEnd< 0) || + if ((mCachedRequest.mStart < 0) || (mCachedRequest.mEnd < 0) || (mCachedRequest.mStart > mCachedRequest.mEnd)) { Log.i(TAG, "wrong start / end index"); getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, type, @@ -1297,18 +1303,23 @@ public final class Avrcp { } availableItems = availableItems - mCachedRequest.mStart; + Log.i(TAG, "start Index: " + mCachedRequest.mStart); + Log.i(TAG, "end Index: " + mCachedRequest.mEnd); + Log.i(TAG, "availableItems: " + availableItems); if (availableItems > MAX_BROWSE_ITEM_TO_SEND) availableItems = MAX_BROWSE_ITEM_TO_SEND; if (reqItems > availableItems) reqItems = availableItems; + Log.i(TAG, "reqItems: " + reqItems); for (index = 0; index < reqItems; index++) { try { cursor = mContext.getContentResolver().query( mMediaUri, mCursorCols, MediaStore.Audio.Media.IS_MUSIC + "=1 AND _id=" + - playList[index], null, null); + playList[index + (int)mCachedRequest.mStart], null, null); if (cursor != null) { + int validAttrib = 0; cursor.moveToFirst(); itemType[index] = TYPE_MEDIA_ELEMENT_ITEM; uid[index] = cursor.getLong(cursor.getColumnIndexOrThrow("_id")); @@ -1316,20 +1327,25 @@ public final class Avrcp { playable[index] = 0; displayName[index] = cursor.getString(cursor.getColumnIndexOrThrow( MediaStore.Audio.Media.TITLE)); - numAtt[index] = mCachedRequest.mAttrCnt; for (int attIndex = 0; attIndex < mCachedRequest.mAttrCnt; attIndex++) { int attr = mCachedRequest.mAttrList.get(attIndex).intValue(); - attValues[(7 * index) + attIndex] = getAttributeStringFromCursor( - cursor, attr); - attIds[(7 * index) + attIndex] = attr; + if ((attr <= MEDIA_ATTR_MAX) && (attr >= MEDIA_ATTR_MIN)) { + attValues[(7 * index) + attIndex] = + getAttributeStringFromCursor(cursor, attr); + attIds[(7 * index) + attIndex] = attr; + validAttrib ++; + } } - cursor.close(); + numAtt[index] = (byte)validAttrib; } } catch(Exception e) { - Log.i(TAG, "Exception e"+ e); - cursor.close(); + Log.i(TAG, "Exception "+ e); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } numItems = index; @@ -1338,11 +1354,11 @@ public final class Avrcp { } class CachedRequest { - int mStart; - int mEnd; + long mStart; + long mEnd; byte mAttrCnt; ArrayList<Integer> mAttrList; - public CachedRequest(int start, int end, byte attrCnt, int[] attrs) { + public CachedRequest(long start, long end, byte attrCnt, int[] attrs) { mStart = start; mEnd = end; mAttrCnt = attrCnt; @@ -1355,12 +1371,12 @@ public final class Avrcp { class FolderListEntries { byte mScope; - int mStart; - int mEnd; + long mStart; + long mEnd; int mAttrCnt; int mNumAttr; ArrayList<Integer> mAttrList; - public FolderListEntries(byte scope, int start, int end, int attrCnt, int numAttr, + public FolderListEntries(byte scope, long start, long end, int attrCnt, int numAttr, int[] attrs) { mScope = scope; mStart = start; @@ -1418,11 +1434,16 @@ public final class Avrcp { final MediaPlayerInfo di = rccIterator.next(); if (di.GetPlayerFocus()) { if (DEBUG) Log.v(TAG, "resetting current MetaData"); - mMetadata = di.GetMetadata(); + mMetadata.artist = di.GetMetadata().artist; + mMetadata.trackTitle = di.GetMetadata().trackTitle; + mMetadata.albumTitle = di.GetMetadata().albumTitle; + mMetadata.genre = di.GetMetadata().genre; + mMetadata.tracknum = di.GetMetadata().tracknum; break; } } } + String oldMetadata = mMetadata.toString(); mMetadata.artist = data.getString(MediaMetadataRetriever.METADATA_KEY_ARTIST, null); mMetadata.trackTitle = data.getString(MediaMetadataRetriever.METADATA_KEY_TITLE, null); @@ -1431,7 +1452,8 @@ public final class Avrcp { mTrackNumber = data.getLong(MediaMetadataRetriever.METADATA_KEY_NUM_TRACKS, -1L); mMetadata.tracknum = data.getLong(MediaMetadataRetriever.METADATA_KEY_CD_TRACK_NUMBER, -1L); - Log.v(TAG,"mMetadata.toString() = " + mMetadata.toString()); + Log.v(TAG,"old Metadata = " + oldMetadata); + Log.v(TAG,"new MetaData " + mMetadata.toString()); if (mMediaPlayers.size() > 0) { final Iterator<MediaPlayerInfo> rccIterator = mMediaPlayers.iterator(); @@ -1444,6 +1466,7 @@ public final class Avrcp { } } } + if (!oldMetadata.equals(mMetadata.toString())) { updateTrackNumber(); Log.v(TAG,"new mMetadata, mTrackNumber update to " + mTrackNumber); @@ -1622,11 +1645,13 @@ public final class Avrcp { status = NOT_A_DIRECTORY; else status = DOES_NOT_EXIST; - cursor.close(); } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); changePathRspNative(INTERNAL_ERROR, numberOfItems); + } finally { + if (cursor != null) { + cursor.close(); + } } break; default: @@ -1661,12 +1686,14 @@ public final class Avrcp { } else{ numberOfItems = cursor.getCount(); mCurrentPathUid = String.valueOf(folderUid); - cursor.close(); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); changePathRspNative(INTERNAL_ERROR, numberOfItems); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { // Path @ Individual Album id Cursor cursor = null; @@ -1682,11 +1709,13 @@ public final class Avrcp { status = NOT_A_DIRECTORY; else status = DOES_NOT_EXIST; - cursor.close(); } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); changePathRspNative(INTERNAL_ERROR, numberOfItems); + } finally { + if (cursor != null) { + cursor.close(); + } } } break; @@ -1723,12 +1752,14 @@ public final class Avrcp { numberOfItems = cursor.getCount(); mCurrentPathUid = String.valueOf(folderUid); mCurrentPath = PATH_ARTISTS; - cursor.close(); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); changePathRspNative(INTERNAL_ERROR, numberOfItems); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { Cursor cursor = null; @@ -1742,11 +1773,13 @@ public final class Avrcp { status = NOT_A_DIRECTORY; else status = DOES_NOT_EXIST; - cursor.close(); } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); changePathRspNative(INTERNAL_ERROR, numberOfItems); + } finally { + if (cursor != null) { + cursor.close(); + } } } break; @@ -1789,12 +1822,14 @@ public final class Avrcp { numberOfItems = cursor.getCount(); mCurrentPathUid = String.valueOf(folderUid); mCurrentPath = PATH_PLAYLISTS; - cursor.close(); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); changePathRspNative(INTERNAL_ERROR, numberOfItems); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { numberOfItems = 0; @@ -1829,13 +1864,15 @@ public final class Avrcp { return 0; } else { long count = cursor.getCount(); - cursor.close(); return count; } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); return 0; + } finally { + if (cursor != null) { + cursor.close(); + } } } @@ -1853,7 +1890,6 @@ public final class Avrcp { return 0; } else if (path.equals(PATH_TITLES)) { long count = cursor.getCount(); - cursor.close(); return count; } else if (path.equals(PATH_ALBUMS) || path.equals(PATH_ARTISTS)){ long elemCount = 0; @@ -1872,12 +1908,14 @@ public final class Avrcp { count--; } Log.i(TAG, "element Count is "+ elemCount); - cursor.close(); return elemCount; } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); + } finally { + if (cursor != null) { + cursor.close(); + } } return 0; } @@ -1924,13 +1962,15 @@ public final class Avrcp { playItemRspNative(DOES_NOT_EXIST); } else { Log.i(TAG, "Play uid:" + uid); - cursor.close(); mRemoteController.setRemoteControlClientPlayItem(uid, scope); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); playItemRspNative(INTERNAL_ERROR); + } finally { + if (cursor != null) { + cursor.close(); + } } } else if (mCurrentPath.equals(PATH_ALBUMS)) { if (mCurrentPathUid == null) { @@ -1949,13 +1989,15 @@ public final class Avrcp { playItemRspNative(DOES_NOT_EXIST); } else { Log.i(TAG, "Play uid:" + uid); - cursor.close(); mRemoteController.setRemoteControlClientPlayItem(uid, scope); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); playItemRspNative(INTERNAL_ERROR); + } finally { + if (cursor != null) { + cursor.close(); + } } } } else if (mCurrentPath.equals(PATH_ARTISTS)) { @@ -1975,13 +2017,15 @@ public final class Avrcp { playItemRspNative(DOES_NOT_EXIST); } else { Log.i(TAG, "Play uid:" + uid); - cursor.close(); mRemoteController.setRemoteControlClientPlayItem(uid, scope); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); playItemRspNative(INTERNAL_ERROR); + } finally { + if (cursor != null) { + cursor.close(); + } } } } else if (mCurrentPath.equals(PATH_PLAYLISTS)) { @@ -2014,13 +2058,15 @@ public final class Avrcp { playItemRspNative(DOES_NOT_EXIST); } else { Log.i(TAG, "Play uid:" + uid); - cursor.close(); mRemoteController.setRemoteControlClientPlayItem(uid, scope); } } catch (Exception e) { Log.e(TAG, "Exception " + e); - cursor.close(); playItemRspNative(INTERNAL_ERROR); + } finally { + if (cursor != null) { + cursor.close(); + } } } } else { @@ -2083,16 +2129,23 @@ public final class Avrcp { Log.i(TAG, "Invalid track UID"); getItemAttrRspNative((byte)0, attrs, textArray); } else { + int validAttrib = 0; cursor.moveToFirst(); for (int i = 0; i < numAttr; ++i) { - textArray[i] = getAttributeStringFromCursor(cursor, attrs[i]); + if ((attrs[i] <= MEDIA_ATTR_MAX) && (attrs[i] >= MEDIA_ATTR_MIN)) { + textArray[i] = getAttributeStringFromCursor(cursor, attrs[i]); + validAttrib ++; + } } - getItemAttrRspNative(numAttr, attrs, textArray); - cursor.close(); + getItemAttrRspNative((byte)validAttrib, attrs, textArray); } } catch (Exception e) { - Log.e(TAG, "Exception " + e); cursor.close(); + Log.e(TAG, "Exception " + e); getItemAttrRspNative((byte)0, attrs, textArray); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { Log.i(TAG, "Invalid scope"); @@ -2153,7 +2206,7 @@ public final class Avrcp { } } - private void getFolderItems(byte scope, int start, int end, int attrCnt, + private void getFolderItems(byte scope, long start, long end, int attrCnt, int numAttr, int[] attrs) { if (DEBUG) Log.v(TAG, "getFolderItems"); if (DEBUG) Log.v(TAG, "scope: " + scope + " attrCnt: " + attrCnt); @@ -2168,7 +2221,7 @@ public final class Avrcp { mHandler.sendMessage(msg); } - private void processGetFolderItems(byte scope, int start, int end, int size, + private void processGetFolderItems(byte scope, long start, long end, int size, int numAttr, int[] attrs) { if (DEBUG) Log.v(TAG, "processGetFolderItems"); if (DEBUG) Log.v(TAG, "scope: " + scope + " size: " + size); @@ -2183,7 +2236,7 @@ public final class Avrcp { } } - private void processGetMediaPlayerItems(byte scope, int start, int end, int size, + private void processGetMediaPlayerItems(byte scope, long start, long end, int size, int numAttr, int[] attrs) { byte[] folderItems = new byte[size]; int[] folderItemLengths = new int[32]; @@ -2223,12 +2276,12 @@ public final class Avrcp { return false; } - private void processGetFolderItemsInternal(byte scope, int start, int end, int size, + private void processGetFolderItemsInternal(byte scope, long start, long end, long size, byte numAttr, int[] attrs) { int status = OPERATION_SUCCESSFUL; - int numItems = 0; - int reqItems = (end - start) + 1; + long numItems = 0; + long reqItems = (end - start) + 1; int[] itemType = new int[MAX_BROWSE_ITEM_TO_SEND]; long[] uid = new long[MAX_BROWSE_ITEM_TO_SEND]; int[] type = new int[MAX_BROWSE_ITEM_TO_SEND]; @@ -2269,7 +2322,7 @@ public final class Avrcp { } if (mCurrentPath.equals(PATH_ROOT)) { - int availableItems = NUM_ROOT_ELEMENTS; + long availableItems = NUM_ROOT_ELEMENTS; if (start >= availableItems) { Log.i(TAG, "startIteam exceeds the available item index"); getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, @@ -2288,8 +2341,8 @@ public final class Avrcp { numItems = reqItems; for (int count = 0; count < reqItems; count ++) { - int index = start + count; - switch (index) { + long index = start + count; + switch ((int)index) { case ALBUMS_ITEM_INDEX: itemType[count] = TYPE_FOLDER_ITEM; uid[count] = UID_ALBUM; @@ -2336,7 +2389,7 @@ public final class Avrcp { getFolderItemsRspNative((byte)status, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); } else if (mCurrentPath.equals(PATH_TITLES)) { - int availableItems = 0; + long availableItems = 0; Cursor cursor = null; try { cursor = mContext.getContentResolver().query( @@ -2350,7 +2403,6 @@ public final class Avrcp { getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); return; } cursor.moveToFirst(); @@ -2382,23 +2434,30 @@ public final class Avrcp { displayName[index] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore. Audio.Media.TITLE)); - numAtt[index] = numAttr; + int validAttrib = 0; for (attIndex = 0; attIndex < numAttr; attIndex++) { - attValues[(7 * index) + attIndex] = getAttributeStringFromCursor( - cursor, attrs[attIndex]); - attIds[(7 * index) + attIndex] = attrs[attIndex]; + if ((attrs[attIndex] <= MEDIA_ATTR_MAX) && + (attrs[attIndex] >= MEDIA_ATTR_MIN)) { + attValues[(7 * index) + attIndex] = + getAttributeStringFromCursor(cursor, attrs[attIndex]); + attIds[(7 * index) + attIndex] = attrs[attIndex]; + validAttrib ++; + } } + numAtt[index] = (byte)validAttrib; cursor.moveToNext(); } numItems = index; getFolderItemsRspNative((byte)OPERATION_SUCCESSFUL, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); } catch(Exception e) { Log.i(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } else if (mCurrentPath.equals(PATH_ALBUMS)) { if (mCurrentPathUid == null) { @@ -2477,16 +2536,18 @@ public final class Avrcp { itemType, uid, type, playable, displayName, numAtt, attValues, attIds); } - cursor.close(); } catch(Exception e) { Log.i(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { long folderUid = Long.valueOf(mCurrentPathUid); - int availableItems = 0; + long availableItems = 0; Cursor cursor = null; try { cursor = mContext.getContentResolver().query( @@ -2502,7 +2563,6 @@ public final class Avrcp { getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); return; } cursor.moveToFirst(); @@ -2535,24 +2595,31 @@ public final class Avrcp { playable[index] = 0; displayName[index] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore. - Audio.Media.TITLE)); - numAtt[index] = numAttr; + Audio.Media.TITLE)); + int validAttrib = 0; for (attIndex = 0; attIndex < numAttr; attIndex++) { - attValues[(7 * index) + attIndex] = getAttributeStringFromCursor( - cursor, attrs[attIndex]); - attIds[(7 * index) + attIndex] = attrs[attIndex]; + if ((attrs[attIndex] <= MEDIA_ATTR_MAX) && + (attrs[attIndex] >= MEDIA_ATTR_MIN)) { + attValues[(7 * index) + attIndex] = + getAttributeStringFromCursor(cursor, attrs[attIndex]); + attIds[(7 * index) + attIndex] = attrs[attIndex]; + validAttrib ++; + } } + numAtt[index] = (byte)validAttrib; cursor.moveToNext(); } numItems = index; getFolderItemsRspNative((byte)OPERATION_SUCCESSFUL, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); } catch(Exception e) { Log.i(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } } else if (mCurrentPath.equals(PATH_ARTISTS)) { @@ -2630,16 +2697,18 @@ public final class Avrcp { getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); } - cursor.close(); } catch(Exception e) { Log.i(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { long folderUid = Long.valueOf(mCurrentPathUid); - int availableItems = 0; + long availableItems = 0; Cursor cursor = null; try { cursor = mContext.getContentResolver().query( @@ -2655,7 +2724,6 @@ public final class Avrcp { getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); return; } cursor.moveToFirst(); @@ -2688,23 +2756,30 @@ public final class Avrcp { displayName[index] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore. Audio.Media.TITLE)); - numAtt[index] = numAttr; + int validAttrib = 0; for (attIndex = 0; attIndex < numAttr; attIndex++) { - attValues[(7 * index) + attIndex] = getAttributeStringFromCursor( - cursor, attrs[attIndex]); - attIds[(7 * index) + attIndex] = attrs[attIndex]; + if ((attrs[attIndex] <= MEDIA_ATTR_MAX) && + (attrs[attIndex] >= MEDIA_ATTR_MIN)) { + attValues[(7 * index) + attIndex] = + getAttributeStringFromCursor(cursor, attrs[attIndex]); + attIds[(7 * index) + attIndex] = attrs[attIndex]; + validAttrib ++; + } } + numAtt[index] = (byte)validAttrib; cursor.moveToNext(); } numItems = index; getFolderItemsRspNative((byte)OPERATION_SUCCESSFUL, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); } catch(Exception e) { Log.i(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } } else if (mCurrentPath.equals(PATH_PLAYLISTS)) { @@ -2776,16 +2851,18 @@ public final class Avrcp { itemType, uid, type, playable, displayName, numAtt, attValues, attIds); } - cursor.close(); } catch(Exception e) { Log.i(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } else { long folderUid = Long.valueOf(mCurrentPathUid); - int availableItems = 0; + long availableItems = 0; Cursor cursor = null; String[] playlistMemberCols = new String[] { @@ -2816,7 +2893,6 @@ public final class Avrcp { getFolderItemsRspNative((byte)RANGE_OUT_OF_BOUNDS, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); return; } cursor.moveToFirst(); @@ -2850,23 +2926,30 @@ public final class Avrcp { displayName[index] = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore. Audio.Media.TITLE)); - numAtt[index] = numAttr; + int validAttrib = 0; for (attIndex = 0; attIndex < numAttr; attIndex++) { - attValues[(7 * index) + attIndex] = getAttributeStringFromCursor( - cursor, attrs[attIndex]); - attIds[(7 * index) + attIndex] = attrs[attIndex]; + if ((attrs[attIndex] <= MEDIA_ATTR_MAX) && + (attrs[attIndex] >= MEDIA_ATTR_MIN)) { + attValues[(7 * index) + attIndex] = + getAttributeStringFromCursor(cursor, attrs[attIndex]); + attIds[(7 * index) + attIndex] = attrs[attIndex]; + validAttrib ++; + } } + numAtt[index] = (byte)validAttrib; cursor.moveToNext(); } numItems = index; getFolderItemsRspNative((byte)OPERATION_SUCCESSFUL, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); - cursor.close(); } catch(Exception e) { Log.e(TAG, "Exception e" + e); - cursor.close(); getFolderItemsRspNative((byte)INTERNAL_ERROR, numItems, itemType, uid, type, playable, displayName, numAtt, attValues, attIds); + } finally { + if (cursor != null) { + cursor.close(); + } } } } else { @@ -3097,7 +3180,7 @@ public final class Avrcp { } private String getAttributeStringFromCursor(Cursor cursor, int attrId) { - String attrStr = null; + String attrStr = "<unknown>"; switch (attrId) { case MEDIA_ATTR_TITLE: attrStr = cursor.getString(cursor.getColumnIndexOrThrow( @@ -3285,8 +3368,10 @@ public final class Avrcp { return (int) Math.ceil((double) volume*AVRCP_MAX_VOL/mAudioStreamMax); } -private void updateLocalPlayerSettings( byte[] data) { + private void updateLocalPlayerSettings( byte[] data) { + if (DEBUG) Log.v(TAG, "updateLocalPlayerSettings"); for (int i = 0; i < data.length; i += 2) { + if (DEBUG) Log.v(TAG, "ID: " + data[i] + " Value: " + data[i+1]); switch (data[i]) { case ATTRIBUTE_EQUALIZER: settingValues.eq_value = data[i+1]; @@ -3304,6 +3389,45 @@ private void updateLocalPlayerSettings( byte[] data) { } } + private boolean checkPlayerAttributeResponse( byte[] data) { + boolean ret = false; + if (DEBUG) Log.v(TAG, "checkPlayerAttributeResponse"); + for (int i = 0; i < data.length; i += 2) { + if (DEBUG) Log.v(TAG, "ID: " + data[i] + " Value: " + data[i+1]); + switch (data[i]) { + case ATTRIBUTE_EQUALIZER: + if (mPendingSetAttributes.contains(new Integer(ATTRIBUTE_EQUALIZER))) { + if(data[i+1] == ATTRIBUTE_NOTSUPPORTED) { + ret = false; + } else { + ret = true; + } + } + break; + case ATTRIBUTE_REPEATMODE: + if (mPendingSetAttributes.contains(new Integer(ATTRIBUTE_REPEATMODE))) { + if(data[i+1] == ATTRIBUTE_NOTSUPPORTED) { + ret = false; + } else { + ret = true; + } + } + break; + case ATTRIBUTE_SHUFFLEMODE: + if (mPendingSetAttributes.contains(new Integer(ATTRIBUTE_SHUFFLEMODE))) { + if(data[i+1] == ATTRIBUTE_NOTSUPPORTED) { + ret = false; + } else { + ret = true; + } + } + break; + } + } + mPendingSetAttributes.clear(); + return ret; + } + //PDU ID 0x11 private void onListPlayerAttributeRequest() { if (DEBUG) Log.v(TAG, "onListPlayerAttributeRequest"); @@ -3360,12 +3484,13 @@ private void updateLocalPlayerSettings( byte[] data) { //PDU 0x14 private void setPlayerAppSetting( byte num , byte [] attr_id , byte [] attr_val ) { - if (DEBUG) Log.v(TAG, "setPlayerAppSetting" + num ); + if (DEBUG) Log.v(TAG, "setPlayerAppSetting " + num ); byte[] array = new byte[num*2]; for ( int i = 0; i < num; i++) { array[i] = attr_id[i] ; array[i+1] = attr_val[i]; + mPendingSetAttributes.add(new Integer(attr_id[i])); } Intent intent = new Intent(PLAYERSETTINGS_REQUEST); intent.putExtra(COMMAND, CMDSET); @@ -3375,7 +3500,7 @@ private void updateLocalPlayerSettings( byte[] data) { msg.what = MESSAGE_PLAYERSETTINGS_TIMEOUT; msg.arg1 = SET_ATTRIBUTE_VALUES; mPendingCmds.add(new Integer(msg.arg1)); - mHandler.sendMessageDelayed(msg, 130); + mHandler.sendMessageDelayed(msg, 500); } //PDU 0x15 @@ -3438,6 +3563,7 @@ private void updateLocalPlayerSettings( byte[] data) { final static short PLAYSTATUS_ERROR = 255; // match up with btrc_media_attr_t enum of bt_rc.h + final static int MEDIA_ATTR_MIN = 1; final static int MEDIA_ATTR_TITLE = 1; final static int MEDIA_ATTR_ARTIST = 2; final static int MEDIA_ATTR_ALBUM = 3; @@ -3445,6 +3571,7 @@ private void updateLocalPlayerSettings( byte[] data) { final static int MEDIA_ATTR_NUM_TRACKS = 5; final static int MEDIA_ATTR_GENRE = 6; final static int MEDIA_ATTR_PLAYING_TIME = 7; + final static int MEDIA_ATTR_MAX = 7; // match up with btrc_event_id_t enum of bt_rc.h final static int EVT_PLAY_STATUS_CHANGED = 1; @@ -3784,7 +3911,7 @@ private void updateLocalPlayerSettings( byte[] data) { private native boolean setAdressedPlayerRspNative(byte statusCode); private native boolean getMediaPlayerListRspNative(byte statusCode, int uidCounter, int itemCount, byte[] folderItems, int[] folderItemLengths); - private native boolean getFolderItemsRspNative(byte statusCode, int numItems, + private native boolean getFolderItemsRspNative(byte statusCode, long numItems, int[] itemType, long[] uid, int[] type, byte[] playable, String[] displayName, byte[] numAtt, String[] attValues, int[] attIds); private native boolean getListPlayerappAttrRspNative(byte attr, byte[] attrIds); @@ -3894,6 +4021,8 @@ private void updateLocalPlayerSettings( byte[] data) { mMetadata.albumTitle = metaData.albumTitle; mMetadata.artist = metaData.artist; mMetadata.trackTitle = metaData.trackTitle; + mMetadata.genre = metaData.genre; + mMetadata.tracknum = metaData.tracknum; } public byte GetPlayState() { return mPlayState; diff --git a/src/com/android/bluetooth/btservice/AdapterService.java b/src/com/android/bluetooth/btservice/AdapterService.java index 8a303fc0e..524d16a74 100755 --- a/src/com/android/bluetooth/btservice/AdapterService.java +++ b/src/com/android/bluetooth/btservice/AdapterService.java @@ -1595,9 +1595,9 @@ public class AdapterService extends Service { void setProfileAutoConnectionPriority (BluetoothDevice device, int profileId){ if (profileId == BluetoothProfile.HEADSET) { HeadsetService hsService = HeadsetService.getHeadsetService(); - List<BluetoothDevice> deviceList = hsService.getConnectedDevices(); if ((hsService != null) && (BluetoothProfile.PRIORITY_AUTO_CONNECT != hsService.getPriority(device))){ + List<BluetoothDevice> deviceList = hsService.getConnectedDevices(); adjustOtherHeadsetPriorities(hsService, deviceList); hsService.setPriority(device,BluetoothProfile.PRIORITY_AUTO_CONNECT); } diff --git a/src/com/android/bluetooth/btservice/AdapterState.java b/src/com/android/bluetooth/btservice/AdapterState.java index c0d3f3999..419300ac3 100644 --- a/src/com/android/bluetooth/btservice/AdapterState.java +++ b/src/com/android/bluetooth/btservice/AdapterState.java @@ -341,6 +341,8 @@ final class AdapterState extends StateMachine { mPendingCommandState.setTurningOff(false); transitionTo(mOffState); notifyAdapterStateChange(BluetoothAdapter.STATE_OFF); + errorLog("STOP_TIMEOUT:Killing the process to force a restart as part cleanup"); + android.os.Process.killProcess(android.os.Process.myPid()); break; case DISABLE_TIMEOUT: if (DBG) Log.d(TAG,"CURRENT_STATE=PENDING, MESSAGE = DISABLE_TIMEOUT, isTurningOn=" + isTurningOn + ", isTurningOff=" + isTurningOff); diff --git a/src/com/android/bluetooth/btservice/BondStateMachine.java b/src/com/android/bluetooth/btservice/BondStateMachine.java index 3b82b18b2..68590e7b6 100644 --- a/src/com/android/bluetooth/btservice/BondStateMachine.java +++ b/src/com/android/bluetooth/btservice/BondStateMachine.java @@ -229,11 +229,21 @@ final class BondStateMachine extends StateMachine { case SSP_REQUEST: int passkey = msg.arg1; int variant = msg.arg2; + if(devProp == null) + { + Log.e(TAG,"Received msg from an unknown device"); + return false; + } sendDisplayPinIntent(devProp.getAddress(), passkey, variant); break; case PIN_REQUEST: BluetoothClass btClass = dev.getBluetoothClass(); int btDeviceClass = btClass.getDeviceClass(); + if(devProp == null) + { + Log.e(TAG,"Received msg from an unknown device"); + return false; + } if (btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD || btDeviceClass == BluetoothClass.Device.PERIPHERAL_KEYBOARD_POINTING) { // Its a keyboard. Follow the HID spec recommendation of creating the diff --git a/src/com/android/bluetooth/btservice/RemoteDevices.java b/src/com/android/bluetooth/btservice/RemoteDevices.java index b6c94890e..7ec817054 100644 --- a/src/com/android/bluetooth/btservice/RemoteDevices.java +++ b/src/com/android/bluetooth/btservice/RemoteDevices.java @@ -351,7 +351,7 @@ final class RemoteDevices { device = getDeviceProperties(bdDevice); } - for (int j = 0; j < types.length; j++) { + for (int j = 0; j < types.length && device != null; j++) { type = types[j]; val = values[j]; if(val.length <= 0) diff --git a/src/com/android/bluetooth/gatt/AdvertiseManager.java b/src/com/android/bluetooth/gatt/AdvertiseManager.java index a2c859316..22b20dd6b 100644 --- a/src/com/android/bluetooth/gatt/AdvertiseManager.java +++ b/src/com/android/bluetooth/gatt/AdvertiseManager.java @@ -87,6 +87,16 @@ class AdvertiseManager { void cleanup() { logd("advertise clients cleared"); mAdvertiseClients.clear(); + + if (mHandler != null) { + // Shut down the thread + mHandler.removeCallbacksAndMessages(null); + Looper looper = mHandler.getLooper(); + if (looper != null) { + looper.quit(); + } + mHandler = null; + } } /** @@ -219,12 +229,16 @@ class AdvertiseManager { // Returns maximum advertise instances supported by controller. private int maxAdvertiseInstances() { - AdapterService adapter = AdapterService.getAdapterService(); - int numOfAdvtInstances = adapter.getNumOfAdvertisementInstancesSupported(); - // Note numOfAdvtInstances includes the standard advertising instance. - // TODO: remove - 1 once the stack is able to include standard instance for multiple - // advertising. - return numOfAdvtInstances - 1; + AdapterService adapter; + int numOfAdvtInstances = 0; + if (null != (adapter = AdapterService.getAdapterService())){ + numOfAdvtInstances = adapter.getNumOfAdvertisementInstancesSupported(); + // Note numOfAdvtInstances includes the standard advertising instance. + // TODO: remove - 1 once the stack is able to include standard instance for multiple + // advertising. + return numOfAdvtInstances - 1; + } + return numOfAdvtInstances; } } diff --git a/src/com/android/bluetooth/gatt/GattService.java b/src/com/android/bluetooth/gatt/GattService.java index 4dbf0fed8..654df69b9 100644 --- a/src/com/android/bluetooth/gatt/GattService.java +++ b/src/com/android/bluetooth/gatt/GattService.java @@ -1721,10 +1721,14 @@ public class GattService extends ProfileService { case HandleMap.TYPE_CHARACTERISTIC: { HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle); + if (null != serviceEntry) { app.callback.onCharacteristicReadRequest(address, transId, offset, isLong, serviceEntry.serviceType, serviceEntry.instance, new ParcelUuid(serviceEntry.uuid), entry.instance, new ParcelUuid(entry.uuid)); + }else { + Log.d(TAG, "null == serviceEntry"); + } break; } @@ -1732,11 +1736,15 @@ public class GattService extends ProfileService { { HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle); HandleMap.Entry charEntry = mHandleMap.getByHandle(entry.charHandle); - app.callback.onDescriptorReadRequest(address, transId, offset, isLong, + if (null != serviceEntry && null != charEntry) { + app.callback.onDescriptorReadRequest(address, transId, offset, isLong, serviceEntry.serviceType, serviceEntry.instance, new ParcelUuid(serviceEntry.uuid), charEntry.instance, new ParcelUuid(charEntry.uuid), new ParcelUuid(entry.uuid)); + } else { + Log.d(TAG, "null == serviceEntry || null == charEntry"); + } break; } @@ -1771,11 +1779,15 @@ public class GattService extends ProfileService { case HandleMap.TYPE_CHARACTERISTIC: { HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle); + if (null != serviceEntry) { app.callback.onCharacteristicWriteRequest(address, transId, offset, length, isPrep, needRsp, serviceEntry.serviceType, serviceEntry.instance, new ParcelUuid(serviceEntry.uuid), entry.instance, new ParcelUuid(entry.uuid), data); + }else { + Log.d(TAG, "null == serviceEntry"); + } break; } @@ -1783,12 +1795,16 @@ public class GattService extends ProfileService { { HandleMap.Entry serviceEntry = mHandleMap.getByHandle(entry.serviceHandle); HandleMap.Entry charEntry = mHandleMap.getByHandle(entry.charHandle); + if (null != serviceEntry && null != charEntry) { app.callback.onDescriptorWriteRequest(address, transId, offset, length, isPrep, needRsp, serviceEntry.serviceType, serviceEntry.instance, new ParcelUuid(serviceEntry.uuid), charEntry.instance, new ParcelUuid(charEntry.uuid), new ParcelUuid(entry.uuid), data); + } else { + Log.d(TAG, "null == serviceEntry || null == charEntry"); + } break; } @@ -1893,8 +1909,12 @@ public class GattService extends ProfileService { if (DBG) Log.d(TAG, "beginServiceDeclaration() - uuid=" + srvcUuid + " serverIf=" + serverIf); ServiceDeclaration serviceDeclaration = addToActiveDeclaration(serverIf); - serviceDeclaration.addService(srvcUuid, srvcType, srvcInstanceId, minHandles, + if (null != serviceDeclaration ) { + serviceDeclaration.addService(srvcUuid, srvcType, srvcInstanceId, minHandles, advertisePreferred); + } else { + if (DBG) Log.d(TAG, "beginServiceDeclaration: Got null from addToActiveDeclaration()"); + } } void addIncludedService(int serverIf, int srvcType, int srvcInstanceId, @@ -1902,7 +1922,12 @@ public class GattService extends ProfileService { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "addIncludedService() - uuid=" + srvcUuid); - getActiveDeclaration(serverIf).addIncludedService(srvcUuid, srvcType, srvcInstanceId); + ServiceDeclaration serviceDeclaration = getActiveDeclaration(serverIf); + if (null != serviceDeclaration) { + serviceDeclaration.addIncludedService(srvcUuid, srvcType, srvcInstanceId); + } else { + Log.d(TAG,"getActiveDeclaration(serverIf) is null"); + } } void addCharacteristic(int serverIf, UUID charUuid, int properties, @@ -1910,14 +1935,24 @@ public class GattService extends ProfileService { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "addCharacteristic() - uuid=" + charUuid); - getActiveDeclaration(serverIf).addCharacteristic(charUuid, properties, permissions); + ServiceDeclaration serviceDeclaration = getActiveDeclaration(serverIf); + if (null != serviceDeclaration) { + serviceDeclaration.addCharacteristic(charUuid, properties, permissions); + } else { + Log.d(TAG,"getActiveDeclaration(serverIf) is null"); + } } void addDescriptor(int serverIf, UUID descUuid, int permissions) { enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (DBG) Log.d(TAG, "addDescriptor() - uuid=" + descUuid); - getActiveDeclaration(serverIf).addDescriptor(descUuid, permissions); + ServiceDeclaration serviceDeclaration = getActiveDeclaration(serverIf); + if (null != serviceDeclaration) { + serviceDeclaration.addDescriptor(descUuid, permissions); + } else { + Log.d(TAG,"getActiveDeclaration(serverIf) is null"); + } } void endServiceDeclaration(int serverIf) { @@ -1965,9 +2000,11 @@ public class GattService extends ProfileService { HandleMap.Entry entry = mHandleMap.getByRequestId(requestId); if (entry != null) handle = entry.handle; - int connId = mServerMap.connIdByAddress(serverIf, address); - gattServerSendResponseNative(serverIf, connId, requestId, (byte)status, + Integer connId; + if(null != (connId = mServerMap.connIdByAddress(serverIf, address))) { + gattServerSendResponseNative(serverIf, connId, requestId, (byte)status, handle, offset, value, (byte)0); + } mHandleMap.deleteRequest(requestId); } @@ -1985,9 +2022,8 @@ public class GattService extends ProfileService { int charHandle = mHandleMap.getCharacteristicHandle(srvcHandle, charUuid, charInstanceId); if (charHandle == 0) return; - int connId = mServerMap.connIdByAddress(serverIf, address); - if (connId == 0) return; - + Integer connId = mServerMap.connIdByAddress(serverIf, address); + if (connId == null) return; if (confirm) { gattServerSendIndicationNative(serverIf, charHandle, connId, value); } else { @@ -2060,12 +2096,14 @@ public class GattService extends ProfileService { private void continueServiceDeclaration(int serverIf, int status, int srvcHandle) throws RemoteException { if (mServiceDeclarations.size() == 0) return; if (DBG) Log.d(TAG, "continueServiceDeclaration() - srvcHandle=" + srvcHandle); - + ServiceDeclaration serviceDeclaration; boolean finished = false; ServiceDeclaration.Entry entry = null; - if (status == 0) - entry = getPendingDeclaration().getNext(); + if (status == 0) { + if (null != (serviceDeclaration = getPendingDeclaration())) + entry = serviceDeclaration.getNext(); + } if (entry != null) { if (DBG) Log.d(TAG, "continueServiceDeclaration() - next entry type=" @@ -2075,11 +2113,13 @@ public class GattService extends ProfileService { if (entry.advertisePreferred) { mAdvertisingServiceUuids.add(entry.uuid); } - gattServerAddServiceNative(serverIf, entry.serviceType, + if (null != (serviceDeclaration = getPendingDeclaration())) { + gattServerAddServiceNative(serverIf, entry.serviceType, entry.instance, entry.uuid.getLeastSignificantBits(), entry.uuid.getMostSignificantBits(), - getPendingDeclaration().getNumHandles()); + serviceDeclaration.getNumHandles()); + } break; case ServiceDeclaration.TYPE_CHARACTERISTIC: diff --git a/src/com/android/bluetooth/gatt/ScanManager.java b/src/com/android/bluetooth/gatt/ScanManager.java index 27e492f30..705d49e72 100644 --- a/src/com/android/bluetooth/gatt/ScanManager.java +++ b/src/com/android/bluetooth/gatt/ScanManager.java @@ -101,6 +101,16 @@ public class ScanManager { mRegularScanClients.clear(); mBatchClients.clear(); mScanNative.cleanup(); + + if (mHandler != null) { + // Shut down the thread + mHandler.removeCallbacksAndMessages(null); + Looper looper = mHandler.getLooper(); + if (looper != null) { + looper.quit(); + } + mHandler = null; + } } /** @@ -351,14 +361,15 @@ public class ScanManager { @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "awakened up at time " + SystemClock.elapsedRealtime()); - String action = intent.getAction(); - - if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) { - if (mBatchClients.isEmpty()) { - return; + String action; + if (null != (action = intent.getAction())) { + if (action.equals(ACTION_REFRESH_BATCHED_SCAN)) { + if (mBatchClients.isEmpty()) { + return; + } + // Note this actually flushes all pending batch data. + flushBatchScanResults(mBatchClients.iterator().next()); } - // Note this actually flushes all pending batch data. - flushBatchScanResults(mBatchClients.iterator().next()); } } }; @@ -649,20 +660,24 @@ public class ScanManager { waitForCallback(); } else { Deque<Integer> clientFilterIndices = new ArrayDeque<Integer>(); + int featureSelection; + int filterIndex; for (ScanFilter filter : client.filters) { ScanFilterQueue queue = new ScanFilterQueue(); - queue.addScanFilter(filter); - int featureSelection = queue.getFeatureSelection(); - int filterIndex = mFilterIndexStack.pop(); - while (!queue.isEmpty()) { + if (null != queue) { + queue.addScanFilter(filter); + featureSelection = queue.getFeatureSelection(); + filterIndex = mFilterIndexStack.pop(); + while (!queue.isEmpty()) { + resetCountDownLatch(); + addFilterToController(clientIf, queue.pop(), filterIndex); + waitForCallback(); + } resetCountDownLatch(); - addFilterToController(clientIf, queue.pop(), filterIndex); + configureFilterParamter(clientIf, client, featureSelection, filterIndex); waitForCallback(); + clientFilterIndices.add(filterIndex); } - resetCountDownLatch(); - configureFilterParamter(clientIf, client, featureSelection, filterIndex); - waitForCallback(); - clientFilterIndices.add(filterIndex); } mClientFilterIndexMap.put(clientIf, clientFilterIndices); } @@ -748,7 +763,7 @@ public class ScanManager { if (client.filters == null || client.filters.isEmpty()) { return true; } - return client.filters.size() < mClientFilterIndexMap.size(); + return false; } private void addFilterToController(int clientIf, ScanFilterQueue.Entry entry, @@ -797,14 +812,16 @@ public class ScanManager { } private void initFilterIndexStack() { - int maxFiltersSupported = - AdapterService.getAdapterService().getNumOfOffloadedScanFilterSupported(); - // Start from index 3 as: - // index 0 is reserved for ALL_PASS filter in Settings app. - // index 1 is reserved for ALL_PASS filter for regular scan apps. - // index 2 is reserved for ALL_PASS filter for batch scan apps. - for (int i = 3; i < maxFiltersSupported; ++i) { - mFilterIndexStack.add(i); + AdapterService adapterService; + if (null != (adapterService = AdapterService.getAdapterService())) { + int maxFiltersSupported = adapterService.getNumOfOffloadedScanFilterSupported(); + // Start from index 3 as: + // index 0 is reserved for ALL_PASS filter in Settings app. + // index 1 is reserved for ALL_PASS filter for regular scan apps. + // index 2 is reserved for ALL_PASS filter for batch scan apps. + for (int i = 3; i < maxFiltersSupported; ++i) { + mFilterIndexStack.add(i); + } } } diff --git a/src/com/android/bluetooth/hdp/HealthService.java b/src/com/android/bluetooth/hdp/HealthService.java index 21846c677..01e3f4121 100644 --- a/src/com/android/bluetooth/hdp/HealthService.java +++ b/src/com/android/bluetooth/hdp/HealthService.java @@ -189,7 +189,12 @@ public class HealthService extends ProfileService { { BluetoothHealthAppConfiguration appConfig = (BluetoothHealthAppConfiguration) msg.obj; - int appId = (mApps.get(appConfig)).mAppId; + AppInfo appInfo = mApps.get(appConfig); + if (appInfo == null) { + Log.e(TAG, "No AppInfo found for AppConfig: " + appConfig); + break; + } + int appId = appInfo.mAppId; if (!unregisterHealthAppNative(appId)) { Log.e(TAG, "Failed to unregister application: id: " + appId); callStatusCallback(appConfig, @@ -201,7 +206,12 @@ public class HealthService extends ProfileService { { HealthChannel chan = (HealthChannel) msg.obj; byte[] devAddr = Utils.getByteAddress(chan.mDevice); - int appId = (mApps.get(chan.mConfig)).mAppId; + AppInfo appInfo = mApps.get(chan.mConfig); + if (appInfo == null) { + Log.e(TAG, "No AppInfo found for AppConfig: " + chan.mConfig); + break; + } + int appId = appInfo.mAppId; chan.mChannelId = connectChannelNative(devAddr, appId); if (chan.mChannelId == -1) { callHealthChannelCallback(chan.mConfig, chan.mDevice, @@ -241,6 +251,10 @@ public class HealthService extends ProfileService { regStatus == BluetoothHealth.APP_CONFIG_UNREGISTRATION_SUCCESS) { //unlink to death once app is unregistered AppInfo appInfo = mApps.get(appConfig); + if (appInfo == null){ + Log.e(TAG, "No AppInfo found for AppConfig " + appConfig); + break; + } appInfo.cleanup(); mApps.remove(appConfig); } @@ -254,7 +268,7 @@ public class HealthService extends ProfileService { findAppConfigByAppId(channelStateEvent.mAppId); int newState; newState = convertHalChannelState(channelStateEvent.mState); - if (newState == BluetoothHealth.STATE_CHANNEL_DISCONNECTED && + if (newState == BluetoothHealth.STATE_CHANNEL_DISCONNECTED || appConfig == null) { Log.e(TAG,"Disconnected for non existing app"); break; @@ -512,9 +526,15 @@ public class HealthService extends ProfileService { private void callStatusCallback(BluetoothHealthAppConfiguration config, int status) { if (VDBG) log ("Health Device Application: " + config + " State Change: status:" + status); - IBluetoothHealthCallback callback = (mApps.get(config)).mCallback; + AppInfo appInfo = mApps.get(config); + if (appInfo == null) { + Log.e(TAG, " No AppInfo found for AppConfig " + config); + return; + } + IBluetoothHealthCallback callback = appInfo.mCallback; if (callback == null) { Log.e(TAG, "Callback object null"); + return; } try { @@ -604,8 +624,12 @@ public class HealthService extends ProfileService { Log.e(TAG, "Exception while duping: " + e); } } - - IBluetoothHealthCallback callback = (mApps.get(config)).mCallback; + AppInfo appInfo = mApps.get(config); + if (appInfo == null) { + Log.e(TAG, "No AppInfo found for AppConfig " + config); + return; + } + IBluetoothHealthCallback callback = appInfo.mCallback; if (callback == null) { Log.e(TAG, "No callback found for config: " + config); return; diff --git a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java index edd352018..0bec29172 100644 --- a/src/com/android/bluetooth/hfp/HeadsetStateMachine.java +++ b/src/com/android/bluetooth/hfp/HeadsetStateMachine.java @@ -1218,6 +1218,12 @@ final class HeadsetStateMachine extends StateMachine { switch (state) { case HeadsetHalConstants.AUDIO_STATE_CONNECTED: + if (!isScoAcceptable()) { + Log.e(TAG,"Audio Connected without any listener"); + disconnectAudioNative(getByteAddress(device)); + break; + } + // TODO(BT) should I save the state for next broadcast as the prevState? mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; setAudioParameters(device); /*Set proper Audio Paramters.*/ @@ -1458,7 +1464,9 @@ final class HeadsetStateMachine extends StateMachine { processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); break; case INTENT_SCO_VOLUME_CHANGED: - processIntentScoVolume((Intent) message.obj, mActiveScoDevice); + if (mActiveScoDevice != null) { + processIntentScoVolume((Intent) message.obj, mActiveScoDevice); + } break; case CALL_STATE_CHANGED: processCallState((HeadsetCallState) message.obj, ((message.arg1 == 1)?true:false)); @@ -1839,6 +1847,11 @@ final class HeadsetStateMachine extends StateMachine { processLocalVrEvent(HeadsetHalConstants.VR_STATE_STOPPED); } break; + case INTENT_SCO_VOLUME_CHANGED: + if (mActiveScoDevice != null) { + processIntentScoVolume((Intent) message.obj, mActiveScoDevice); + } + break; case INTENT_BATTERY_CHANGED: processIntentBatteryChanged((Intent) message.obj); break; @@ -2184,6 +2197,11 @@ final class HeadsetStateMachine extends StateMachine { switch (state) { case HeadsetHalConstants.AUDIO_STATE_CONNECTED: + if (!isScoAcceptable()) { + Log.e(TAG,"Audio Connected without any listener"); + disconnectAudioNative(getByteAddress(device)); + break; + } mAudioState = BluetoothHeadset.STATE_AUDIO_CONNECTED; setAudioParameters(device); /* Set proper Audio Parameters. */ mAudioManager.setBluetoothScoOn(true); @@ -2250,6 +2268,16 @@ final class HeadsetStateMachine extends StateMachine { Log.e(TAG, "Handsfree phone proxy null for query phone state"); } } + + private void processIntentScoVolume(Intent intent, BluetoothDevice device) { + int volumeValue = intent.getIntExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, 0); + if (mPhoneState.getSpeakerVolume() != volumeValue) { + mPhoneState.setSpeakerVolume(volumeValue); + setVolumeNative(HeadsetHalConstants.VOLUME_TYPE_SPK, + volumeValue, getByteAddress(device)); + } + } + private void processMultiHFDisconnect(BluetoothDevice device) { log("MultiHFPending state: processMultiHFDisconnect"); if (mActiveScoDevice != null && mActiveScoDevice.equals(device)) { @@ -2390,10 +2418,16 @@ final class HeadsetStateMachine extends StateMachine { } public boolean isBluetoothVoiceDialingEnabled( BluetoothDevice device) { - int RemoteBrsf = mHeadsetBrsf.get(device); - Log.d(TAG, "isBluetoothVoiceDialingEnabled mRemoteBrsf: " + RemoteBrsf + - "supported: " + (RemoteBrsf & BRSF_HF_VOICE_REG_ACT)); - return ((RemoteBrsf & BRSF_HF_VOICE_REG_ACT) != 0x0) ? true : false; + int remoteBrsf = 0; + if (mHeadsetBrsf != null && !mHeadsetBrsf.isEmpty()) { + remoteBrsf = mHeadsetBrsf.get(device); + } else { + Log.e(TAG,"remote device supported features not found"); + return false; + } + Log.d(TAG, "isBluetoothVoiceDialingEnabled mRemoteBrsf: " + remoteBrsf + + "supported: " + (remoteBrsf & BRSF_HF_VOICE_REG_ACT)); + return ((remoteBrsf & BRSF_HF_VOICE_REG_ACT) != 0x0) ? true : false; } int getAudioState(BluetoothDevice device) { @@ -2495,7 +2529,11 @@ final class HeadsetStateMachine extends StateMachine { // Whereas for VoiceDial we want to activate the SCO connection but we are still // in MODE_NORMAL and hence the need to explicitly suspend the A2DP stream mAudioManager.setParameters("A2dpSuspended=true"); - connectAudioNative(getByteAddress(device)); + if (device != null) { + connectAudioNative(getByteAddress(device)); + } else { + Log.e(TAG, "device not found for VR"); + } } if (mStartVoiceRecognitionWakeLock.isHeld()) { @@ -2663,9 +2701,15 @@ final class HeadsetStateMachine extends StateMachine { { // 1. update nrec value // 2. update headset name + int mCodec = 0; + int mNrec = 0; HashMap<String, Integer> AudioParam = mHeadsetAudioParam.get(device); - int mCodec = AudioParam.get("codec"); - int mNrec = AudioParam.get("NREC"); + if (AudioParam != null && !AudioParam.isEmpty()) { + mCodec = AudioParam.get("codec"); + mNrec = AudioParam.get("NREC"); + } else { + Log.e(TAG,"setAudioParameters: AudioParam not found"); + } if (mCodec != WBS_CODEC) { Log.d(TAG, "Use NBS PCM samples:" + device); mAudioManager.setParameters(HEADSET_WBS + "=off"); @@ -3002,8 +3046,7 @@ final class HeadsetStateMachine extends StateMachine { mPhoneState.setCallState(callState.mCallState); mPhoneState.setNumber(callState.mNumber); mPhoneState.setType(callState.mType); - if (mDialingOut) { - if (callState.mCallState == + if (mDialingOut && callState.mCallState == HeadsetHalConstants.CALL_STATE_DIALING) { BluetoothDevice device = getDeviceForMessage(DIALING_OUT_TIMEOUT); if (device == null) { @@ -3012,11 +3055,7 @@ final class HeadsetStateMachine extends StateMachine { atResponseCodeNative(HeadsetHalConstants.AT_RESPONSE_OK, 0, getByteAddress(device)); removeMessages(DIALING_OUT_TIMEOUT); - } else if (callState.mCallState == - HeadsetHalConstants.CALL_STATE_ACTIVE || callState.mCallState - == HeadsetHalConstants.CALL_STATE_IDLE) { mDialingOut = false; - } } /* Set ActiveScoDevice to null when call ends */ @@ -3087,18 +3126,27 @@ final class HeadsetStateMachine extends StateMachine { // 0 disable noice reduction private void processNoiceReductionEvent(int enable, BluetoothDevice device) { HashMap<String, Integer> AudioParamNrec = mHeadsetAudioParam.get(device); - if (enable == 1) - AudioParamNrec.put("NREC", 1); - else - AudioParamNrec.put("NREC", 0); - log("NREC value for device :" + device + " is: " + AudioParamNrec.get("NREC")); + if (AudioParamNrec != null && !AudioParamNrec.isEmpty()) { + if (enable == 1) + AudioParamNrec.put("NREC", 1); + else + AudioParamNrec.put("NREC", 0); + log("NREC value for device :" + device + " is: " + + AudioParamNrec.get("NREC")); + } else { + Log.e(TAG,"processNoiceReductionEvent: AudioParamNrec is null "); + } } // 2 - WBS on // 1 - NBS on private void processWBSEvent(int enable, BluetoothDevice device) { HashMap<String, Integer> AudioParamCodec = mHeadsetAudioParam.get(device); - AudioParamCodec.put("codec", enable); + if (AudioParamCodec != null && !AudioParamCodec.isEmpty()) { + AudioParamCodec.put("codec", enable); + } else { + Log.e(TAG,"processWBSEvent: AudioParamNrec is null "); + } if (enable == 2) { Log.d(TAG, "AudioManager.setParameters bt_wbs=on for " + device.getName() + " - " + device.getAddress()); @@ -3615,6 +3663,14 @@ final class HeadsetStateMachine extends StateMachine { (mPhoneState.getCallState() != HeadsetHalConstants.CALL_STATE_IDLE)); } + // Accept incoming SCO only when there is active call, VR activated, + // active VOIP call and Incoming call with inband ringtone supported + private boolean isScoAcceptable() { + return (((mPhoneState.getCallState() == HeadsetHalConstants.CALL_STATE_INCOMING) && + ((mLocalBrsf & BRSF_AG_IN_BAND_RING) != 0)) || mVoiceRecognitionStarted || + isInCall()); + } + boolean isConnected() { IState currentState = getCurrentState(); return (currentState == mConnected || currentState == mAudioOn); diff --git a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java index 10464b9ee..bf01bbf34 100644 --- a/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java +++ b/src/com/android/bluetooth/hfpclient/HeadsetClientStateMachine.java @@ -2149,6 +2149,32 @@ final class HeadsetClientStateMachine extends StateMachine { + event.valueInt); processAudioEvent(event.valueInt, event.device); break; + case EVENT_TYPE_RING_INDICATION: + /* PTS test case TC_HF_ICA_BV_05_I creates SCO even + * after disabling in-band ringtone, disconnect SCO + * if inband ringtone is disabled */ + Log.i(TAG,"Ring Indication in Audio connected state " + + "mInBandRingtone " + mInBandRingtone); + if (mInBandRingtone != + HeadsetClientHalConstants.IN_BAND_RING_NOT_PROVIDED) { + break; + } + if (disconnectAudioNative(getByteAddress(mCurrentDevice))) { + mAudioState = BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED; + //abandon audio focus + if (mAudioManager.getMode() != AudioManager.MODE_NORMAL) { + mAudioManager.setMode(AudioManager.MODE_NORMAL); + Log.d(TAG, "abandonAudioFocus"); + //abandon audio focus after the mode has been set back to normal + mAudioManager.abandonAudioFocusForCall(); + } + Log.d(TAG,"hfp_enable=false"); + mAudioManager.setParameters("hfp_enable=false"); + broadcastAudioState(mCurrentDevice, + BluetoothHeadsetClient.STATE_AUDIO_DISCONNECTED, + BluetoothHeadsetClient.STATE_AUDIO_CONNECTED); + } + break; default: return NOT_HANDLED; } diff --git a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java index 8160809c5..4a8caaafb 100644 --- a/src/com/android/bluetooth/map/BluetoothMapContentObserver.java +++ b/src/com/android/bluetooth/map/BluetoothMapContentObserver.java @@ -1045,13 +1045,13 @@ public class BluetoothMapContentObserver { Intent intent; intent = new Intent(ACTION_MESSAGE_DELIVERY, null); intent.putExtra("HANDLE", msgInfo.id); - deliveryIntents.add(PendingIntent.getBroadcast(mContext, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT)); + deliveryIntents.add(PendingIntent.getBroadcast(mContext,(int)System.currentTimeMillis(), + intent, PendingIntent.FLAG_UPDATE_CURRENT)); intent = new Intent(ACTION_MESSAGE_SENT, null); intent.putExtra("HANDLE", msgInfo.id); - sentIntents.add(PendingIntent.getBroadcast(mContext, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT)); + sentIntents.add(PendingIntent.getBroadcast(mContext, (int)System.currentTimeMillis(), + intent,PendingIntent.FLAG_UPDATE_CURRENT)); } Log.d(TAG, "sendMessage to " + msgInfo.phone); diff --git a/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java b/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java index bab40d9f8..59f77efea 100755 --- a/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java +++ b/src/com/android/bluetooth/map/BluetoothMapbMessageMmsEmail.java @@ -749,7 +749,8 @@ public class BluetoothMapbMessageMmsEmail extends BluetoothMapbMessage { if (beginMsg == -1) { throw new IllegalArgumentException("Ill-formatted bMessage, no BEGIN:MSG"); } - int endMsg = body.lastIndexOf("END:MSG", beginMsg); + //Last occurence of END:MSG + int endMsg = body.lastIndexOf("END:MSG"); if (endMsg == -1) { throw new IllegalArgumentException("Ill-formatted bMessage, no END:MSG"); } @@ -764,7 +765,8 @@ public class BluetoothMapbMessageMmsEmail extends BluetoothMapbMessage { int endVersionPos; if(rfc822Flag == 0){ if(mimeFlag == 0) { - endVersionPos = body.lastIndexOf("END:MSG", beginVersionPos) ; + //Last occurence of END:MSG + endVersionPos = body.lastIndexOf("END:MSG") ; if (endVersionPos != -1) { setEmailBody(body.substring(beginVersionPos, (endVersionPos - CRLF.length()))); } else { diff --git a/src/com/android/bluetooth/opp/BluetoothOppNotification.java b/src/com/android/bluetooth/opp/BluetoothOppNotification.java index cd4b58344..161cd576f 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppNotification.java +++ b/src/com/android/bluetooth/opp/BluetoothOppNotification.java @@ -45,6 +45,7 @@ import android.database.sqlite.SQLiteException; import android.content.res.Resources.NotFoundException; import android.net.Uri; import android.util.Log; +import android.os.PowerManager; import android.os.Handler; import android.os.Message; import android.os.Process; @@ -102,7 +103,7 @@ class BluetoothOppNotification { private NotificationUpdateThread mUpdateNotificationThread; - private int mPendingUpdate = 0; + private PowerManager mPowerManager; private static final int NOTIFICATION_ID_OUTBOUND = -1000005; @@ -111,8 +112,10 @@ class BluetoothOppNotification { private boolean mOutboundUpdateCompleteNotification = true; private boolean mInboundUpdateCompleteNotification = true; + private int confirmation = 0; private int mInboundActiveNotificationId = 0; private int mOutboundActiveNotificationId = 0; + private int mRunning = 0; private int mIncomingShownId = 0; @@ -148,6 +151,7 @@ class BluetoothOppNotification { mNotificationMgr = (NotificationManager)mContext .getSystemService(Context.NOTIFICATION_SERVICE); mNotifications = new HashMap<String, NotificationItem>(); + mPowerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); } /** @@ -155,14 +159,10 @@ class BluetoothOppNotification { */ public void updateNotification() { synchronized (BluetoothOppNotification.this) { - mPendingUpdate++; - if (mPendingUpdate > 1) { - if (V) Log.v(TAG, "update too frequent, put in queue"); - return; - } - if (!mHandler.hasMessages(NOTIFY)) { - if (V) Log.v(TAG, "send message"); - mHandler.sendMessage(mHandler.obtainMessage(NOTIFY)); + if (mUpdateNotificationThread == null) { + if (V) Log.v(TAG, "new notify thread!!!"); + mUpdateNotificationThread = new NotificationUpdateThread(); + mUpdateNotificationThread.start(); } } } @@ -170,45 +170,24 @@ class BluetoothOppNotification { public void btOffNotification() { if (V) Log.v(TAG, "Update Notification while BT is Turning OFF"); synchronized (BluetoothOppNotification.this) { + if (mUpdateNotificationThread != null) { + try { + mUpdateNotificationThread.interrupt(); + mUpdateNotificationThread.join(); + mUpdateNotificationThread = null; + } catch (InterruptedException ie) { + Log.e(TAG, "Notification thread join interrupted"); + } + } + updateActiveNotification(); - mPendingUpdate = 0; mInboundUpdateCompleteNotification = true; mOutboundUpdateCompleteNotification = true; updateCompletedNotification(); - mPendingUpdate = 0; cancelIncomingFileConfirmNotification(); } } - private static final int NOTIFY = 0; - // Use 1 second timer to limit notification frequency. - // 1. On the first notification, create the update thread. - // Buffer other updates. - // 2. Update thread will clear mPendingUpdate. - // 3. Handler sends a delayed message to self - // 4. Handler checks if there are any more updates after 1 second. - // 5. If there is an update, update it else stop. - private Handler mHandler = new Handler() { - public void handleMessage(Message msg) { - switch (msg.what) { - case NOTIFY: - synchronized (BluetoothOppNotification.this) { - if (mPendingUpdate > 0 && mUpdateNotificationThread == null) { - if (V) Log.v(TAG, "new notify threadi!"); - mUpdateNotificationThread = new NotificationUpdateThread(); - mUpdateNotificationThread.start(); - if (V) Log.v(TAG, "send delay message"); - mHandler.sendMessageDelayed(mHandler.obtainMessage(NOTIFY), 1000); - } else if (mPendingUpdate > 0) { - if (V) Log.v(TAG, "previous thread is not finished yet"); - mHandler.sendMessageDelayed(mHandler.obtainMessage(NOTIFY), 1000); - } - break; - } - } - } - }; - private class NotificationUpdateThread extends Thread { public NotificationUpdateThread() { @@ -218,18 +197,35 @@ class BluetoothOppNotification { @Override public void run() { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); - synchronized (BluetoothOppNotification.this) { - if (mUpdateNotificationThread != this) { - throw new IllegalStateException( - "multiple UpdateThreads in BluetoothOppNotification"); + do { + synchronized (BluetoothOppNotification.this) { + if (mUpdateNotificationThread != this) { + throw new IllegalStateException( + "multiple UpdateThreads in BluetoothOppNotification"); + } } - mPendingUpdate = 0; - } - updateActiveNotification(); - updateCompletedNotification(); - updateIncomingFileConfirmNotification(); + + updateActiveNotification(); + updateCompletedNotification(); + updateIncomingFileConfirmNotification(); + + try { + if ((confirmation == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED) + || mPowerManager.isScreenOn()) { + Thread.sleep(BluetoothShare.UI_UPDATE_INTERVAL); + } + } catch (InterruptedException e) { + if (V) Log.v(TAG, "NotificationThread sleep is interrupted (1), exiting"); + return; + } + + if (V) Log.v(TAG, "Running = " + mRunning); + } while ((mRunning > 0) && (mPowerManager.isScreenOn() + || (confirmation == BluetoothShare.USER_CONFIRMATION_HANDOVER_CONFIRMED))); + synchronized (BluetoothOppNotification.this) { mUpdateNotificationThread = null; + if (V) Log.v(TAG, "NotificationThread is stopped!!!"); } } } @@ -258,6 +254,7 @@ class BluetoothOppNotification { cursor = mContext.getContentResolver().query(BluetoothShare.CONTENT_URI, null, WHERE_RUNNING, null, BluetoothShare._ID); + mRunning = cursor.getCount(); } catch (SQLiteException e) { cursor = null; Log.e(TAG, "SQLite exception: " + e); @@ -317,7 +314,7 @@ class BluetoothOppNotification { int id = cursor.getInt(idIndex); long total = cursor.getLong(totalBytesIndex); long current = cursor.getLong(currentBytesIndex); - int confirmation = cursor.getInt(confirmIndex); + confirmation = cursor.getInt(confirmIndex); String destination = cursor.getString(destinationIndex); String fileName = cursor.getString(dataIndex); @@ -385,6 +382,7 @@ class BluetoothOppNotification { intent.putExtra(Constants.EXTRA_BT_OPP_TRANSFER_PROGRESS, progress); intent.putExtra(Constants.EXTRA_BT_OPP_ADDRESS, item.destination); mContext.sendBroadcast(intent, Constants.HANDOVER_STATUS_PERMISSION); + if (V) Log.v(TAG, "Handover OPP transfer is inprogress"); continue; } // Build the notification object diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java index 6dcf5ef9a..6684b1fd7 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java +++ b/src/com/android/bluetooth/opp/BluetoothOppObexClientSession.java @@ -85,6 +85,8 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { private Handler mCallback; + private PowerManager pm; + private long position; public BluetoothOppObexClientSession(Context context, ObexTransport transport) { @@ -133,7 +135,6 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { } private class ContentResolverUpdateThread extends Thread { - private static final int sSleepTime = 1000; private Uri contentUri; private Context mContext1; private volatile boolean interrupted = false; @@ -150,10 +151,12 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { ContentValues updateValues; while (true) { - updateValues = new ContentValues(); - updateValues.put(BluetoothShare.CURRENT_BYTES, position); - mContext1.getContentResolver().update(contentUri, updateValues, - null, null); + if (pm.isScreenOn()) { + updateValues = new ContentValues(); + updateValues.put(BluetoothShare.CURRENT_BYTES, position); + mContext1.getContentResolver().update(contentUri, updateValues, + null, null); + } /* Check if the Operation is interrupted before entering sleep */ if (interrupted == true) { @@ -162,7 +165,7 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { } try { - Thread.sleep(sSleepTime); + Thread.sleep(BluetoothShare.UI_UPDATE_INTERVAL); } catch (InterruptedException e1) { if (V) Log.v(TAG, "ContentResolverUpdateThread was interrupted (1), exiting"); return; @@ -208,7 +211,7 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { waitingForShare = true; mWaitingForRemote = false; mNumShares = initialNumShares; - PowerManager pm = (PowerManager)mContext1.getSystemService(Context.POWER_SERVICE); + pm = (PowerManager)mContext1.getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); } @@ -362,6 +365,7 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { if (status == BluetoothShare.STATUS_SUCCESS) { Message msg = Message.obtain(mCallback); msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE; + mInfo.mStatus = status; msg.obj = mInfo; msg.sendToTarget(); } else { @@ -628,7 +632,7 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { if (uiUpdateThread == null) { uiUpdateThread = new ContentResolverUpdateThread(mContext1, - contentUri); + contentUri); if (V) Log.v(TAG, "Worker for Updation : Created"); uiUpdateThread.start(); } @@ -664,8 +668,11 @@ public class BluetoothOppObexClientSession implements BluetoothOppObexSession { } else if (!mInterrupted && position == fileInfo.mLength) { long endTime = System.currentTimeMillis(); Log.i(TAG, "SendFile finished sending file " + fileInfo.mFileName - + " length " + fileInfo.mLength - + "Bytes in " + (endTime - beginTime) + "ms" ); + + " length " + fileInfo.mLength + " Bytes. Approx. throughput is " + + BluetoothShare.throughputInKbps(fileInfo.mLength, + (endTime - beginTime)) + + " Kbps"); + status = BluetoothShare.STATUS_SUCCESS; outputStream.close(); } else { error = true; diff --git a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java index 110305804..c7876409e 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java +++ b/src/com/android/bluetooth/opp/BluetoothOppObexServerSession.java @@ -110,20 +110,22 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen private BluetoothOppReceiveFileInfo mFileInfo; + private PowerManager pm; + private WakeLock mWakeLock; private WakeLock mPartialWakeLock; + private long position; + boolean mTimeoutMsgSent = false; boolean mTransferInProgress = false; - private int position; - public BluetoothOppObexServerSession(Context context, ObexTransport transport) { mContext = context; mTransport = transport; - PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); + pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, TAG); mPartialWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG); @@ -194,7 +196,6 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen private class ContentResolverUpdateThread extends Thread { - private static final int sSleepTime = 1000; private Uri contentUri; private Context mContext1; private volatile boolean interrupted = false; @@ -210,10 +211,12 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); ContentValues updateValues; while (true) { - updateValues = new ContentValues(); - updateValues.put(BluetoothShare.CURRENT_BYTES, position); - mContext1.getContentResolver().update(contentUri, updateValues, - null, null); + if (pm.isScreenOn()) { + updateValues = new ContentValues(); + updateValues.put(BluetoothShare.CURRENT_BYTES, position); + mContext1.getContentResolver().update(contentUri, updateValues, + null, null); + } /* Check if the Operation is interrupted before entering sleep */ if (interrupted == true) { @@ -222,7 +225,7 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen } try { - Thread.sleep(sSleepTime); + Thread.sleep(BluetoothShare.UI_UPDATE_INTERVAL); } catch (InterruptedException e1) { if (V) Log.v(TAG, "Server ContentResolverUpdateThread was interrupted (1), exiting"); return; @@ -381,9 +384,7 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen values.put(BluetoothShare.FILENAME_HINT, name); - if (length != null) { - values.put(BluetoothShare.TOTAL_BYTES, length.intValue()); - } + values.put(BluetoothShare.TOTAL_BYTES, length); values.put(BluetoothShare.MIMETYPE, mimeType); @@ -661,8 +662,9 @@ public class BluetoothOppObexServerSession extends ServerRequestHandler implemen if (position == fileInfo.mLength) { long endTime = System.currentTimeMillis(); Log.i(TAG, "Receiving file completed for " + fileInfo.mFileName - + " length " + fileInfo.mLength - + " Bytes in " + (endTime - beginTime) + "ms" ); + + " length " + fileInfo.mLength + " Bytes. Approx. throughput is " + + BluetoothShare.throughputInKbps(fileInfo.mLength, (endTime - beginTime)) + + " Kbps"); status = BluetoothShare.STATUS_SUCCESS; } else { Log.i(TAG, "Reading file failed at " + position + " of " + fileInfo.mLength); diff --git a/src/com/android/bluetooth/opp/BluetoothOppProvider.java b/src/com/android/bluetooth/opp/BluetoothOppProvider.java index 132b950df..a3f3ef30f 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppProvider.java +++ b/src/com/android/bluetooth/opp/BluetoothOppProvider.java @@ -228,6 +228,13 @@ public final class BluetoothOppProvider extends ContentProvider { } } + private static final void copyLong(String key, ContentValues from, ContentValues to) { + Long i = from.getAsLong(key); + if (i != null) { + to.put(key, i); + } + } + @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = mOpenHelper.getWritableDatabase(); @@ -245,8 +252,7 @@ public final class BluetoothOppProvider extends ContentProvider { copyString(BluetoothShare.DESTINATION, values, filteredValues); copyInteger(BluetoothShare.VISIBILITY, values, filteredValues); - copyInteger(BluetoothShare.TOTAL_BYTES, values, filteredValues); - + copyLong(BluetoothShare.TOTAL_BYTES, values, filteredValues); if (values.getAsInteger(BluetoothShare.VISIBILITY) == null) { filteredValues.put(BluetoothShare.VISIBILITY, BluetoothShare.VISIBILITY_VISIBLE); } diff --git a/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java index 4bd9c21fd..327261add 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java +++ b/src/com/android/bluetooth/opp/BluetoothOppReceiveFileInfo.java @@ -113,7 +113,7 @@ public class BluetoothOppReceiveFileInfo { try { if (metadataCursor.moveToFirst()) { hint = metadataCursor.getString(0); - length = metadataCursor.getInt(1); + length = metadataCursor.getLong(1); mimeType = metadataCursor.getString(2); } } finally { diff --git a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java index f060a04e6..3901e7a31 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +++ b/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java @@ -127,7 +127,7 @@ public class BluetoothOppSendFileInfo { try { if (metadataCursor.moveToFirst()) { fileName = metadataCursor.getString(0); - length = metadataCursor.getInt(1); + length = metadataCursor.getLong(1); if (D) Log.d(TAG, "fileName = " + fileName + " length = " + length); } } finally { diff --git a/src/com/android/bluetooth/opp/BluetoothOppService.java b/src/com/android/bluetooth/opp/BluetoothOppService.java index ad99c7e29..e0acd273c 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppService.java +++ b/src/com/android/bluetooth/opp/BluetoothOppService.java @@ -96,8 +96,6 @@ public class BluetoothOppService extends Service { /** Class to handle Notification Manager updates */ private BluetoothOppNotification mNotifier; - private boolean mPendingUpdate; - private UpdateThread mUpdateThread; private ArrayList<BluetoothOppShareInfo> mShares; @@ -418,6 +416,17 @@ public class BluetoothOppService extends Service { break; case BluetoothAdapter.STATE_TURNING_OFF: if (V) Log.v(TAG, "Receiver DISABLED_ACTION "); + + if (mUpdateThread != null) { + try { + mUpdateThread.interrupt(); + mUpdateThread.join(); + mUpdateThread = null; + } catch (InterruptedException ie) { + Log.e(TAG, "OPPService Thread join interrupted"); + } + } + mNotifier.btOffNotification(); //FIX: Don't block main thread /* @@ -439,10 +448,10 @@ public class BluetoothOppService extends Service { private void updateFromProvider() { synchronized (BluetoothOppService.this) { - mPendingUpdate = true; if ((mUpdateThread == null) && (mAdapter != null) && mAdapter.isEnabled()) { if (V) Log.v(TAG, "Starting a new thread"); + mPowerManager = (PowerManager)this.getSystemService(Context.POWER_SERVICE); mUpdateThread = new UpdateThread(); mUpdateThread.start(); } @@ -459,25 +468,14 @@ public class BluetoothOppService extends Service { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); boolean keepService = false; - for (;;) { + do { synchronized (BluetoothOppService.this) { if (mUpdateThread != this) { throw new IllegalStateException( "multiple UpdateThreads in BluetoothOppService"); } - if (V) Log.v(TAG, "pendingUpdate is " + mPendingUpdate + " keepUpdateThread is " - + keepService + " sListenStarted is " + mListenStarted); - if (!mPendingUpdate) { - mUpdateThread = null; - if (!keepService && !mListenStarted) { - if (V) Log.v(TAG, "Need to stop self"); - stopSelf(); - break; - } - if (V) Log.v(TAG, "***returning from updatethread***"); - return; - } - mPendingUpdate = false; + if (V) Log.v(TAG, "keepUpdateThread is " + keepService + " sListenStarted is " + + mListenStarted); } Cursor cursor; try { @@ -612,6 +610,39 @@ public class BluetoothOppService extends Service { cursor.close(); cursor = null; + + if (V) { + if (mServerSession != null) { + Log.v(TAG, "Server Session is active"); + } else { + Log.v(TAG, "No active Server Session"); + } + + if (mTransfer != null) { + Log.v(TAG, "Client Session is active"); + } else { + Log.v(TAG, "No active Client Session"); + } + } + + try { + if (((mServerSession != null) || (mTransfer != null)) + && mPowerManager.isScreenOn()) { + Thread.sleep(BluetoothShare.UI_UPDATE_INTERVAL); + } + } catch (InterruptedException e) { + if (V) Log.v(TAG, "OppService Thread sleep is interrupted (1), exiting"); + return; + } + } while (mPowerManager.isScreenOn() && ((mServerSession != null) || (mTransfer != null))); + + synchronized (BluetoothOppService.this) { + mUpdateThread = null; + if (!keepService && !mListenStarted) { + if (V) Log.v(TAG, "Need to stop self"); + stopSelf(); + } + if (V) Log.v(TAG, "***returning from updatethread***"); } } @@ -808,26 +839,12 @@ public class BluetoothOppService extends Service { info.mConfirm = newConfirm; int newStatus = cursor.getInt(statusColumn); int oldStatus = info.mStatus; - if (!BluetoothShare.isStatusCompleted(info.mStatus) - && BluetoothShare.isStatusCompleted(newStatus)) { + if (BluetoothShare.isStatusCompleted(info.mStatus)) { mNotifier.mNotificationMgr.cancel(info.mId); } if (V) Log.v(TAG," UpdateShare: oldStatus = " + oldStatus + " newStatus = " + newStatus); info.mStatus = newStatus; - if ((!BluetoothShare.isStatusCompleted(oldStatus)) - && (BluetoothShare.isStatusCompleted(newStatus))) { - if (V) Log.v(TAG," UpdateShare: Share Completed: oldStatus = " + oldStatus + " newStatus = " + newStatus); - try { - if((info.mDirection == BluetoothShare.DIRECTION_OUTBOUND) && (mTransfer != null)) { - mTransfer.markShareComplete(newStatus); - } else if (mServerTransfer != null) { - mServerTransfer.markShareComplete(newStatus); - } - } catch (Exception e) { - Log.e(TAG, "Exception: updateShare: oldStatus: " + oldStatus + " newStatus: " + newStatus); - } - } info.mTotalBytes = cursor.getLong(cursor.getColumnIndexOrThrow(BluetoothShare.TOTAL_BYTES)); info.mCurrentBytes = cursor.getLong(cursor .getColumnIndexOrThrow(BluetoothShare.CURRENT_BYTES)); diff --git a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java index 55d116f73..acdd188de 100644 --- a/src/com/android/bluetooth/opp/BluetoothOppTransfer.java +++ b/src/com/android/bluetooth/opp/BluetoothOppTransfer.java @@ -564,31 +564,7 @@ public class BluetoothOppTransfer implements BluetoothOppBatch.BluetoothOppBatch setConfirmed(); } } - public void markShareComplete(int newstatus) { - Log.d(TAG,"markShareComplete: newStatus = " + newstatus); - if (newstatus == BluetoothShare.STATUS_SUCCESS) { - Message msg = Message.obtain(mSessionHandler); - msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE; - msg.obj = mCurrentShare; - msg.sendToTarget(); - } else if ((newstatus == BluetoothShare.STATUS_FORBIDDEN) && - Constants.ZERO_LENGTH_FILE) { - /* Mark the status as success when a zero length file is rejected - * by the remote device. It allows us to continue the transfer if - * we have a batch and the file(s) are yet to be sent in the row. - */ - Message msg = Message.obtain(mSessionHandler); - msg.what = BluetoothOppObexSession.MSG_SHARE_COMPLETE; - msg.obj = mCurrentShare; - msg.sendToTarget(); - Constants.ZERO_LENGTH_FILE = false; - } else { - Message msg = Message.obtain(mSessionHandler); - msg.what = BluetoothOppObexSession.MSG_SESSION_ERROR; - msg.obj = mCurrentShare; - msg.sendToTarget(); - } - } + /** * Set transfer confirmed status. It should only be called for inbound * transfer diff --git a/src/com/android/bluetooth/opp/BluetoothShare.java b/src/com/android/bluetooth/opp/BluetoothShare.java index d0333257a..3242e7e34 100644 --- a/src/com/android/bluetooth/opp/BluetoothShare.java +++ b/src/com/android/bluetooth/opp/BluetoothShare.java @@ -421,4 +421,16 @@ public final class BluetoothShare implements BaseColumns { */ public static final int STATUS_CONNECTION_ERROR = 497; + /** + * Ongoing transfer progress update interval to 1 second + */ + public static final int UI_UPDATE_INTERVAL = 1000; + + /** + * Returns the throughput of the file transfer + */ + public static float throughputInKbps(long fileSize, long timeDuration) { + float throughput = (float)(fileSize * 8 * 1000) / (timeDuration * 1024); + return throughput; + } } |