diff --git a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java index 71b4d8d4e71a6be88a217404a7b02585c26a3664..caf20b44e3fe0e4d6b2bbc57f80b2bf6e8993672 100644 --- a/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java +++ b/android/app/src/com/android/bluetooth/btservice/BondStateMachine.java @@ -567,6 +567,14 @@ final class BondStateMachine extends StateMachine { } mAdapterService.handleBondStateChanged(device, oldState, newState); + + if (newState == BluetoothDevice.BOND_NONE) { + // Remove the permissions for unbonded devices + mAdapterService.setMessageAccessPermission(device, BluetoothDevice.ACCESS_UNKNOWN); + mAdapterService.setPhonebookAccessPermission(device, BluetoothDevice.ACCESS_UNKNOWN); + mAdapterService.setSimAccessPermission(device, BluetoothDevice.ACCESS_UNKNOWN); + } + Intent intent = new Intent(BluetoothDevice.ACTION_BOND_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothDevice.EXTRA_BOND_STATE, newState); diff --git a/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java b/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java index d71e69910c439c2bb234f0d896eaf6fe47aa9cf6..8b0958171aa5223b9bc2e35d63fae2923ca6e4a4 100644 --- a/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +++ b/android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java @@ -32,6 +32,8 @@ package com.android.bluetooth.opp; +import static android.os.UserHandle.myUserId; + import android.bluetooth.BluetoothProfile; import android.bluetooth.BluetoothProtoEnums; import android.content.ContentResolver; @@ -41,6 +43,7 @@ import android.database.Cursor; import android.database.sqlite.SQLiteException; import android.net.Uri; import android.provider.OpenableColumns; +import android.text.TextUtils; import android.util.EventLog; import android.util.Log; @@ -53,6 +56,7 @@ import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; +import java.util.Objects; /** * This class stores information about a single sending file It will only be used for outbound @@ -127,6 +131,11 @@ public class BluetoothOppSendFileInfo { return SEND_FILE_INFO_ERROR; } + if (isContentUriForOtherUser(uri)) { + Log.e(TAG, "Uri: " + uri + " is invalid for user " + myUserId()); + return SEND_FILE_INFO_ERROR; + } + contentType = contentResolver.getType(uri); Cursor metadataCursor; try { @@ -337,6 +346,12 @@ public class BluetoothOppSendFileInfo { return new BluetoothOppSendFileInfo(fileName, contentType, length, is, 0); } + private static boolean isContentUriForOtherUser(Uri uri) { + String uriUserId = uri.getUserInfo(); + return !TextUtils.isEmpty(uriUserId) + && !Objects.equals(uriUserId, String.valueOf(myUserId())); + } + private static long getStreamSize(FileInputStream is) throws IOException { long length = 0; byte[] unused = new byte[4096]; diff --git a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java index de7de37b5d32019e823c787f9331421e7e587b0b..acb58272fbb0f4dd80951731a43aeae054fb61fa 100644 --- a/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java +++ b/android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java @@ -17,6 +17,8 @@ package com.android.bluetooth.opp; +import static android.os.UserHandle.myUserId; + import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; @@ -119,6 +121,110 @@ public class BluetoothOppSendFileInfoTest { assertThat(info).isEqualTo(BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR); } + @Test + public void generateFileInfo_withContentUriForOtherUser_returnsSendFileInfoError() + throws Exception { + String type = "image/jpeg"; + Uri uri = buildContentUriWithEncodedAuthority((myUserId() + 1) + "@media"); + + long fileLength = 1000; + String fileName = "pic.jpg"; + + FileInputStream fs = mock(FileInputStream.class); + AssetFileDescriptor fd = mock(AssetFileDescriptor.class); + doReturn(fileLength).when(fd).getLength(); + doReturn(fs).when(fd).createInputStream(); + + doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any()); + + mCursor = + new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); + mCursor.addRow(new Object[] {fileName, fileLength}); + + doReturn(mCursor) + .when(mCallProxy) + .contentResolverQuery(any(), eq(uri), any(), any(), any(), any()); + + BluetoothOppSendFileInfo info = + BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); + + assertThat(info).isEqualTo(BluetoothOppSendFileInfo.SEND_FILE_INFO_ERROR); + } + + @Test + public void generateFileInfo_withContentUriForImplicitUser_returnsInfoWithCorrectLength() + throws Exception { + String type = "image/jpeg"; + Uri uri = buildContentUriWithEncodedAuthority("media"); + + long fileLength = 1000; + String fileName = "pic.jpg"; + + FileInputStream fs = mock(FileInputStream.class); + AssetFileDescriptor fd = mock(AssetFileDescriptor.class); + doReturn(fileLength).when(fd).getLength(); + doReturn(fs).when(fd).createInputStream(); + + doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any()); + + mCursor = + new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); + mCursor.addRow(new Object[] {fileName, fileLength}); + + doReturn(mCursor) + .when(mCallProxy) + .contentResolverQuery(any(), eq(uri), any(), any(), any(), any()); + + BluetoothOppSendFileInfo info = + BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); + + assertThat(info.mInputStream).isEqualTo(fs); + assertThat(info.mFileName).isEqualTo(fileName); + assertThat(info.mLength).isEqualTo(fileLength); + assertThat(info.mStatus).isEqualTo(0); + } + + @Test + public void generateFileInfo_withContentUriForSameUser_returnsInfoWithCorrectLength() + throws Exception { + String type = "image/jpeg"; + Uri uri = buildContentUriWithEncodedAuthority(myUserId() + "@media"); + + long fileLength = 1000; + String fileName = "pic.jpg"; + + FileInputStream fs = mock(FileInputStream.class); + AssetFileDescriptor fd = mock(AssetFileDescriptor.class); + doReturn(fileLength).when(fd).getLength(); + doReturn(fs).when(fd).createInputStream(); + + doReturn(fd).when(mCallProxy).contentResolverOpenAssetFileDescriptor(any(), eq(uri), any()); + + mCursor = + new MatrixCursor(new String[] {OpenableColumns.DISPLAY_NAME, OpenableColumns.SIZE}); + mCursor.addRow(new Object[] {fileName, fileLength}); + + doReturn(mCursor) + .when(mCallProxy) + .contentResolverQuery(any(), eq(uri), any(), any(), any(), any()); + + BluetoothOppSendFileInfo info = + BluetoothOppSendFileInfo.generateFileInfo(mContext, uri, type, true); + + assertThat(info.mInputStream).isEqualTo(fs); + assertThat(info.mFileName).isEqualTo(fileName); + assertThat(info.mLength).isEqualTo(fileLength); + assertThat(info.mStatus).isEqualTo(0); + } + + private static Uri buildContentUriWithEncodedAuthority(String authority) { + return new Uri.Builder() + .scheme("content") + .encodedAuthority(authority) + .path("external/images/media/1") + .build(); + } + @Test public void generateFileInfo_withoutPermissionForAccessingUri_returnsSendFileInfoError() { String type = "text/plain";