1 /* 2 * Copyright 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.opp; 18 19 import static androidx.test.espresso.intent.Intents.intended; 20 import static androidx.test.espresso.intent.Intents.intending; 21 import static androidx.test.espresso.intent.matcher.IntentMatchers.anyIntent; 22 import static androidx.test.espresso.intent.matcher.IntentMatchers.hasComponent; 23 24 import static com.android.bluetooth.opp.BluetoothOppManager.ALLOWED_INSERT_SHARE_THREAD_NUMBER; 25 import static com.android.bluetooth.opp.BluetoothOppManager.OPP_PREFERENCE_FILE; 26 27 import static com.google.common.truth.Truth.assertThat; 28 29 import static org.mockito.Mockito.any; 30 import static org.mockito.Mockito.doReturn; 31 import static org.mockito.Mockito.eq; 32 import static org.mockito.Mockito.nullable; 33 import static org.mockito.Mockito.spy; 34 import static org.mockito.Mockito.timeout; 35 import static org.mockito.Mockito.verify; 36 37 import android.bluetooth.BluetoothDevice; 38 import android.bluetooth.BluetoothManager; 39 import android.content.ContentValues; 40 import android.content.Context; 41 import android.content.ContextWrapper; 42 import android.net.Uri; 43 import android.util.Log; 44 45 import androidx.test.espresso.intent.Intents; 46 import androidx.test.platform.app.InstrumentationRegistry; 47 import androidx.test.runner.AndroidJUnit4; 48 49 import com.android.bluetooth.BluetoothMethodProxy; 50 51 import org.junit.After; 52 import org.junit.Before; 53 import org.junit.Ignore; 54 import org.junit.Test; 55 import org.junit.runner.RunWith; 56 import org.mockito.MockitoAnnotations; 57 58 import java.util.ArrayList; 59 import java.util.List; 60 import java.util.concurrent.atomic.AtomicBoolean; 61 62 @RunWith(AndroidJUnit4.class) 63 public class BluetoothOppManagerTest { 64 Context mContext; 65 66 BluetoothMethodProxy mCallProxy; 67 68 @Before setUp()69 public void setUp() { 70 MockitoAnnotations.initMocks(this); 71 mContext = spy(new ContextWrapper( 72 InstrumentationRegistry.getInstrumentation().getTargetContext())); 73 74 mCallProxy = spy(BluetoothMethodProxy.getInstance()); 75 BluetoothMethodProxy.setInstanceForTesting(mCallProxy); 76 77 doReturn(null).when(mCallProxy).contentResolverInsert( 78 any(), eq(BluetoothShare.CONTENT_URI), any()); 79 80 Intents.init(); 81 } 82 83 @After tearDown()84 public void tearDown() { 85 BluetoothMethodProxy.setInstanceForTesting(null); 86 BluetoothOppUtility.sSendFileMap.clear(); 87 mContext.getSharedPreferences(OPP_PREFERENCE_FILE, 0).edit().clear().apply(); 88 BluetoothOppManager.sInstance = null; 89 90 Intents.release(); 91 } 92 93 @Test 94 public void restoreApplicationData_afterSavingSingleSendingFileInfo_containsSendingFileInfoSaved()95 restoreApplicationData_afterSavingSingleSendingFileInfo_containsSendingFileInfoSaved() { 96 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 97 bluetoothOppManager.mSendingFlag = true; 98 bluetoothOppManager.saveSendingFileInfo("text/plain", "content:///abc/xyz.txt", false, 99 true); 100 101 BluetoothOppManager.sInstance = null; 102 BluetoothOppManager restartedBluetoothOppManager = BluetoothOppManager.getInstance( 103 mContext); 104 assertThat(bluetoothOppManager.mSendingFlag).isEqualTo( 105 restartedBluetoothOppManager.mSendingFlag); 106 assertThat(bluetoothOppManager.mMultipleFlag).isEqualTo( 107 restartedBluetoothOppManager.mMultipleFlag); 108 assertThat(bluetoothOppManager.mUriOfSendingFile).isEqualTo( 109 restartedBluetoothOppManager.mUriOfSendingFile); 110 assertThat(bluetoothOppManager.mUrisOfSendingFiles).isEqualTo( 111 restartedBluetoothOppManager.mUrisOfSendingFiles); 112 assertThat(bluetoothOppManager.mMimeTypeOfSendingFile).isEqualTo( 113 restartedBluetoothOppManager.mMimeTypeOfSendingFile); 114 assertThat(bluetoothOppManager.mMimeTypeOfSendingFiles).isEqualTo( 115 restartedBluetoothOppManager.mMimeTypeOfSendingFiles); 116 } 117 118 @Test 119 public void restoreApplicationData_afterSavingMultipleSendingFileInfo_containsSendingFileInfoSaved()120 restoreApplicationData_afterSavingMultipleSendingFileInfo_containsSendingFileInfoSaved() { 121 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 122 bluetoothOppManager.mSendingFlag = true; 123 bluetoothOppManager.saveSendingFileInfo("text/plain", new ArrayList<Uri>( 124 List.of(Uri.parse("content:///abc/xyz.txt"), Uri.parse("content:///123" 125 + "/456.txt"))), 126 false, true); 127 128 BluetoothOppManager.sInstance = null; 129 BluetoothOppManager restartedBluetoothOppManager = BluetoothOppManager.getInstance( 130 mContext); 131 assertThat(bluetoothOppManager.mSendingFlag).isEqualTo( 132 restartedBluetoothOppManager.mSendingFlag); 133 assertThat(bluetoothOppManager.mMultipleFlag).isEqualTo( 134 restartedBluetoothOppManager.mMultipleFlag); 135 assertThat(bluetoothOppManager.mUriOfSendingFile).isEqualTo( 136 restartedBluetoothOppManager.mUriOfSendingFile); 137 assertThat(bluetoothOppManager.mUrisOfSendingFiles).isEqualTo( 138 restartedBluetoothOppManager.mUrisOfSendingFiles); 139 assertThat(bluetoothOppManager.mMimeTypeOfSendingFile).isEqualTo( 140 restartedBluetoothOppManager.mMimeTypeOfSendingFile); 141 assertThat(bluetoothOppManager.mMimeTypeOfSendingFiles).isEqualTo( 142 restartedBluetoothOppManager.mMimeTypeOfSendingFiles); 143 } 144 145 @Test isAcceptedList_inAcceptList_returnsTrue()146 public void isAcceptedList_inAcceptList_returnsTrue() { 147 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 148 String address1 = "AA:BB:CC:DD:EE:FF"; 149 String address2 = "00:11:22:33:44:55"; 150 151 bluetoothOppManager.addToAcceptlist(address1); 152 bluetoothOppManager.addToAcceptlist(address2); 153 assertThat(bluetoothOppManager.isAcceptlisted(address1)).isTrue(); 154 assertThat(bluetoothOppManager.isAcceptlisted(address2)).isTrue(); 155 } 156 157 @Test isAcceptedList_notInAcceptList_returnsFalse()158 public void isAcceptedList_notInAcceptList_returnsFalse() { 159 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 160 String address = "01:23:45:67:89:AB"; 161 162 assertThat(bluetoothOppManager.isAcceptlisted(address)).isFalse(); 163 164 bluetoothOppManager.addToAcceptlist(address); 165 assertThat(bluetoothOppManager.isAcceptlisted(address)).isTrue(); 166 } 167 168 @Test startTransfer_withMultipleUris_contentResolverInsertMultipleTimes()169 public void startTransfer_withMultipleUris_contentResolverInsertMultipleTimes() { 170 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 171 String address = "AA:BB:CC:DD:EE:FF"; 172 bluetoothOppManager.saveSendingFileInfo("text/plain", new ArrayList<Uri>( 173 List.of(Uri.parse("content:///abc/xyz.txt"), 174 Uri.parse("content:///a/b/c/d/x/y/z.docs"), 175 Uri.parse("content:///123/456.txt"))), false, true); 176 BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class)) 177 .getAdapter().getRemoteDevice(address); 178 bluetoothOppManager.startTransfer(device); 179 // add 2 files 180 verify(mCallProxy, timeout(5_000) 181 .times(3)).contentResolverInsert(any(), nullable(Uri.class), 182 nullable(ContentValues.class)); 183 } 184 185 @Test startTransfer_withOneUri_contentResolverInsertOnce()186 public void startTransfer_withOneUri_contentResolverInsertOnce() { 187 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 188 String address = "AA:BB:CC:DD:EE:FF"; 189 bluetoothOppManager.saveSendingFileInfo("text/plain", "content:///abc/xyz.txt", 190 false, true); 191 BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class)) 192 .getAdapter().getRemoteDevice(address); 193 bluetoothOppManager.startTransfer(device); 194 verify(mCallProxy, timeout(5_000).times(1)).contentResolverInsert(any(), 195 nullable(Uri.class), nullable(ContentValues.class)); 196 } 197 198 @Ignore("b/267270055") 199 @Test startTransferMoreThanAllowedInsertShareThreadNumberTimes_blockExceedingTransfer()200 public void startTransferMoreThanAllowedInsertShareThreadNumberTimes_blockExceedingTransfer() 201 throws InterruptedException { 202 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 203 String address = "AA:BB:CC:DD:EE:FF"; 204 bluetoothOppManager.saveSendingFileInfo("text/plain", "content:///abc/xyz.txt", 205 false, true); 206 BluetoothDevice device = (mContext.getSystemService(BluetoothManager.class)) 207 .getAdapter().getRemoteDevice(address); 208 209 AtomicBoolean intended = new AtomicBoolean(false); 210 intending(anyIntent()).respondWithFunction( 211 intent -> { 212 // verify that at least one exceeding thread is blocked 213 intended.set(true); 214 return null; 215 }); 216 217 // try flushing the transferring queue, 218 for (int i = 0; i < ALLOWED_INSERT_SHARE_THREAD_NUMBER + 15; i++) { 219 bluetoothOppManager.startTransfer(device); 220 } 221 222 // success at least ALLOWED_INSERT_SHARE_THREAD_NUMBER times 223 verify(mCallProxy, 224 timeout(5_000).atLeast(ALLOWED_INSERT_SHARE_THREAD_NUMBER)).contentResolverInsert( 225 any(), nullable(Uri.class), nullable(ContentValues.class)); 226 227 // there is at least a failed attempt 228 assertThat(intended.get()).isTrue(); 229 } 230 231 @Test isEnabled()232 public void isEnabled() { 233 BluetoothOppManager bluetoothOppManager = BluetoothOppManager.getInstance(mContext); 234 doReturn(true).when(mCallProxy).bluetoothAdapterIsEnabled(any()); 235 assertThat(bluetoothOppManager.isEnabled()).isTrue(); 236 doReturn(false).when(mCallProxy).bluetoothAdapterIsEnabled(any()); 237 assertThat(bluetoothOppManager.isEnabled()).isFalse(); 238 } 239 240 @Test cleanUpSendingFileInfo_fileInfoCleaned()241 public void cleanUpSendingFileInfo_fileInfoCleaned() { 242 BluetoothOppUtility.sSendFileMap.clear(); 243 Uri uri = Uri.parse("content:///a/new/folder/abc/xyz.txt"); 244 assertThat(BluetoothOppUtility.sSendFileMap.size()).isEqualTo(0); 245 BluetoothOppManager.getInstance(mContext).saveSendingFileInfo("text/plain", 246 uri.toString(), false, true); 247 assertThat(BluetoothOppUtility.sSendFileMap.size()).isEqualTo(1); 248 249 BluetoothOppManager.getInstance(mContext).cleanUpSendingFileInfo(); 250 assertThat(BluetoothOppUtility.sSendFileMap.size()).isEqualTo(0); 251 } 252 } 253