Loading android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +21 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -53,12 +56,13 @@ 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 * share. */ // Next tag value for ContentProfileErrorReportUtils.report(): 15 // Next tag value for ContentProfileErrorReportUtils.report(): 16 public class BluetoothOppSendFileInfo { private static final String TAG = "BluetoothOppSendFileInfo"; Loading Loading @@ -124,6 +128,16 @@ public class BluetoothOppSendFileInfo { return SEND_FILE_INFO_ERROR; } if (isContentUriForOtherUser(uri)) { Log.e(TAG, "Uri: " + uri + " is invalid for user " + myUserId()); ContentProfileErrorReportUtils.report( BluetoothProfile.OPP, BluetoothProtoEnums.BLUETOOTH_OPP_SEND_FILE_INFO, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR, 15); return SEND_FILE_INFO_ERROR; } contentType = contentResolver.getType(uri); Cursor metadataCursor; try { Loading Loading @@ -353,6 +367,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]; Loading android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java +106 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,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; Loading Loading @@ -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"; Loading Loading
android/app/src/com/android/bluetooth/opp/BluetoothOppSendFileInfo.java +21 −1 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading @@ -53,12 +56,13 @@ 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 * share. */ // Next tag value for ContentProfileErrorReportUtils.report(): 15 // Next tag value for ContentProfileErrorReportUtils.report(): 16 public class BluetoothOppSendFileInfo { private static final String TAG = "BluetoothOppSendFileInfo"; Loading Loading @@ -124,6 +128,16 @@ public class BluetoothOppSendFileInfo { return SEND_FILE_INFO_ERROR; } if (isContentUriForOtherUser(uri)) { Log.e(TAG, "Uri: " + uri + " is invalid for user " + myUserId()); ContentProfileErrorReportUtils.report( BluetoothProfile.OPP, BluetoothProtoEnums.BLUETOOTH_OPP_SEND_FILE_INFO, BluetoothStatsLog.BLUETOOTH_CONTENT_PROFILE_ERROR_REPORTED__TYPE__LOG_ERROR, 15); return SEND_FILE_INFO_ERROR; } contentType = contentResolver.getType(uri); Cursor metadataCursor; try { Loading Loading @@ -353,6 +367,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]; Loading
android/app/tests/unit/src/com/android/bluetooth/opp/BluetoothOppSendFileInfoTest.java +106 −0 Original line number Diff line number Diff line Loading @@ -16,6 +16,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; Loading Loading @@ -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"; Loading