1 /* 2 * Copyright (C) 2019 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 android.dropboxmanager.cts; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertTrue; 21 import static org.junit.Assert.fail; 22 23 import android.content.BroadcastReceiver; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.content.IntentFilter; 28 import android.os.DropBoxManager; 29 import android.os.SystemClock; 30 import android.platform.test.annotations.AppModeFull; 31 import android.provider.Settings; 32 import android.support.test.uiautomator.UiDevice; 33 import android.util.Log; 34 35 import androidx.test.InstrumentationRegistry; 36 import androidx.test.filters.LargeTest; 37 import androidx.test.runner.AndroidJUnit4; 38 39 import org.junit.After; 40 import org.junit.Before; 41 import org.junit.Test; 42 import org.junit.runner.RunWith; 43 44 import java.io.IOException; 45 import java.text.MessageFormat; 46 import java.util.ArrayList; 47 import java.util.List; 48 import java.util.concurrent.CountDownLatch; 49 import java.util.concurrent.TimeUnit; 50 51 import com.android.compatibility.common.util.AmUtils; 52 import com.android.compatibility.common.util.SystemUtil; 53 import com.android.internal.annotations.GuardedBy; 54 55 /** 56 * Tests DropBox entry management 57 */ 58 @LargeTest 59 @RunWith(AndroidJUnit4.class) 60 public class DropBoxTests { 61 private static final String ENABLED_TAG = "DropBoxTestsEnabledTag"; 62 private static final String LOW_PRIORITY_TAG = "DropBoxTestsLowPriorityTag"; 63 private static final String ANOTHER_LOW_PRIORITY_TAG = "AnotherDropBoxTestsLowPriorityTag"; 64 private static final long BROADCAST_RATE_LIMIT = 1000L; 65 private static final long BROADCAST_DELAY_ALLOWED_ERROR = 200L; 66 67 private static final String SET_RATE_LIMIT_SHELL_COMMAND = "cmd dropbox set-rate-limit {0}"; 68 private static final String ADD_LOW_PRIORITY_SHELL_COMMAND = 69 "cmd dropbox add-low-priority {0}"; 70 private static final String RESTORE_DEFAULTS_SHELL_COMMAND = "cmd dropbox restore-defaults"; 71 72 private Context mContext; 73 private DropBoxManager mDropBoxManager; 74 75 private CountDownLatch mEnabledTagLatch = new CountDownLatch(0); 76 private CountDownLatch mLowPriorityTagLatch = new CountDownLatch(0); 77 private CountDownLatch mAnotherLowPriorityTagLatch = new CountDownLatch(0); 78 79 private ArrayList<DropBoxEntryAddedData> mEnabledBuffer; 80 private ArrayList<DropBoxEntryAddedData> mLowPriorityBuffer; 81 private ArrayList<DropBoxEntryAddedData> mAnotherLowPriorityBuffer; 82 83 public static class DropBoxEntryAddedData { 84 String tag; 85 long time; 86 int droppedCount; 87 long received; 88 } 89 90 private final BroadcastReceiver mDropBoxEntryAddedReceiver = new BroadcastReceiver() { 91 @Override 92 public void onReceive(Context context, Intent intent) { 93 final DropBoxEntryAddedData data = new DropBoxEntryAddedData(); 94 data.tag = intent.getStringExtra(DropBoxManager.EXTRA_TAG); 95 data.time = intent.getLongExtra(DropBoxManager.EXTRA_TIME, 0); 96 data.droppedCount = intent.getIntExtra(DropBoxManager.EXTRA_DROPPED_COUNT, 0); 97 data.received = SystemClock.elapsedRealtime(); 98 if (ENABLED_TAG.equals(data.tag)) { 99 mEnabledBuffer.add(data); 100 mEnabledTagLatch.countDown(); 101 } else if (LOW_PRIORITY_TAG.equals(data.tag)) { 102 mLowPriorityBuffer.add(data); 103 mLowPriorityTagLatch.countDown(); 104 } else if (ANOTHER_LOW_PRIORITY_TAG.equals(data.tag)) { 105 mAnotherLowPriorityBuffer.add(data); 106 mAnotherLowPriorityTagLatch.countDown(); 107 } 108 } 109 }; 110 111 @Before setUp()112 public void setUp() throws Exception { 113 mContext = InstrumentationRegistry.getTargetContext(); 114 mDropBoxManager = mContext.getSystemService(DropBoxManager.class); 115 116 AmUtils.waitForBroadcastIdle(); 117 118 final IntentFilter intentFilter = new IntentFilter(); 119 intentFilter.addAction(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED); 120 mContext.registerReceiver(mDropBoxEntryAddedReceiver, intentFilter); 121 122 setTagLowPriority(LOW_PRIORITY_TAG); 123 setTagLowPriority(ANOTHER_LOW_PRIORITY_TAG); 124 125 setBroadcastRateLimitSetting(BROADCAST_RATE_LIMIT); 126 } 127 128 @After tearDown()129 public void tearDown() throws Exception { 130 mContext.unregisterReceiver(mDropBoxEntryAddedReceiver); 131 132 // Restore dropbox defaults 133 restoreDropboxDefaults(); 134 } 135 sendExcessiveDropBoxEntries(String tag, int count, long delayPerEntry)136 private void sendExcessiveDropBoxEntries(String tag, int count, long delayPerEntry) 137 throws Exception { 138 int i = 0; 139 mDropBoxManager.addText(tag, String.valueOf(i++)); 140 for (; i < count; i++) { 141 Thread.sleep(delayPerEntry); 142 mDropBoxManager.addText(tag, String.valueOf(i)); 143 } 144 } 145 146 /** 147 * A single DropBox entry for a low priority tag should have their 148 * ACTION_DROPBOX_ENTRY_ADDED broadcasts delayed 149 */ 150 @Test testLowPrioritySingleEntry()151 public void testLowPrioritySingleEntry() throws Exception { 152 final int nLowPriorityEntries = 1; 153 154 mLowPriorityTagLatch = new CountDownLatch(nLowPriorityEntries); 155 mLowPriorityBuffer = new ArrayList(nLowPriorityEntries); 156 157 final long startTime = SystemClock.elapsedRealtime(); 158 mDropBoxManager.addText(LOW_PRIORITY_TAG, "test"); 159 160 assertTrue(mLowPriorityTagLatch.await(BROADCAST_RATE_LIMIT * 3 / 2, 161 TimeUnit.MILLISECONDS)); 162 final long endTime = SystemClock.elapsedRealtime(); 163 164 assertEqualsWithinDelta("Broadcast not received at expected time", BROADCAST_RATE_LIMIT, 165 endTime - startTime, BROADCAST_DELAY_ALLOWED_ERROR); 166 167 assertEquals("A single broadcast should be sent for a single low priority dropbox entry", 168 1, mLowPriorityBuffer.size()); 169 DropBoxEntryAddedData data = mLowPriorityBuffer.get(0); 170 assertEquals("Dropped broadcast count should be 0", 171 0, data.droppedCount); 172 } 173 174 /** 175 * Many contemporary DropBox entries for a low priority tag should have their 176 * ACTION_DROPBOX_ENTRY_ADDED broadcasts collapsed into one broadcast 177 */ 178 @Test testLowPriorityRapidEntryLimiting()179 public void testLowPriorityRapidEntryLimiting() throws Exception { 180 final int nLowPriorityEntries = 10; 181 182 mLowPriorityTagLatch = new CountDownLatch(1); 183 mLowPriorityBuffer = new ArrayList(nLowPriorityEntries * 2); 184 185 // add several low priority entries in quick sucession 186 final long startTime = SystemClock.elapsedRealtime(); 187 sendExcessiveDropBoxEntries(LOW_PRIORITY_TAG, nLowPriorityEntries, 0); 188 assertTrue(mLowPriorityTagLatch.await(BROADCAST_RATE_LIMIT * 3 / 2, 189 TimeUnit.MILLISECONDS)); 190 final long endTime = SystemClock.elapsedRealtime(); 191 192 assertEqualsWithinDelta("Broadcast not received at expected time", BROADCAST_RATE_LIMIT, 193 endTime - startTime, BROADCAST_DELAY_ALLOWED_ERROR); 194 195 assertEquals("Many low priority dropbox entries within the rate limit period should " + 196 "result in 1 broadcast", 1, mLowPriorityBuffer.size()); 197 DropBoxEntryAddedData data = mLowPriorityBuffer.get(0); 198 assertEquals("All but one of the low priority broadcasts should have been dropped", 199 nLowPriorityEntries - 1, data.droppedCount); 200 } 201 202 /** 203 * Many DropBox entries for a low priority tag should have their 204 * ACTION_DROPBOX_ENTRY_ADDED broadcasts collapsed into a few broadcast 205 */ 206 @Test testLowPrioritySustainedRapidEntryLimiting()207 public void testLowPrioritySustainedRapidEntryLimiting() throws Exception { 208 final int nLowPriorityEntries = 10; 209 210 mLowPriorityTagLatch = new CountDownLatch(2); 211 mLowPriorityBuffer = new ArrayList(nLowPriorityEntries * 2); 212 213 // add several low priority entries across the rate limit period 214 final long startTime = SystemClock.elapsedRealtime(); 215 sendExcessiveDropBoxEntries(LOW_PRIORITY_TAG, nLowPriorityEntries, 216 BROADCAST_RATE_LIMIT * 3 / 2 / nLowPriorityEntries); 217 assertTrue(mLowPriorityTagLatch.await(BROADCAST_RATE_LIMIT * 5 / 2, 218 TimeUnit.MILLISECONDS)); 219 final long endTime = SystemClock.elapsedRealtime(); 220 221 assertEqualsWithinDelta("Broadcast not received at expected time", 222 BROADCAST_RATE_LIMIT * 2, endTime - startTime, BROADCAST_DELAY_ALLOWED_ERROR * 2); 223 224 assertEquals("Many low priority dropbox entries across two rate limit periods should " + 225 "result in 2 broadcasts", 2, mLowPriorityBuffer.size()); 226 DropBoxEntryAddedData data = mLowPriorityBuffer.get(0); 227 int droppedCount = data.droppedCount; 228 data = mLowPriorityBuffer.get(1); 229 droppedCount += data.droppedCount; 230 assertEquals("All but two of the low priority broadcasts should have been dropped", 231 nLowPriorityEntries - 2, droppedCount); 232 } 233 234 /** 235 * Many contemporary DropBox entries from multiple low priority tag should have their 236 * ACTION_DROPBOX_ENTRY_ADDED broadcasts collapsed into seperate broadcasts per tag. 237 * Different tags should not interfer with each others' broadcasts 238 */ 239 @Test testMultipleLowPriorityRateLimiting()240 public void testMultipleLowPriorityRateLimiting() throws Exception { 241 final int nLowPriorityEntries = 10; 242 final int nOtherEntries = 10; 243 244 mLowPriorityTagLatch = new CountDownLatch(1); 245 mLowPriorityBuffer = new ArrayList(nLowPriorityEntries * 2); 246 mAnotherLowPriorityTagLatch = new CountDownLatch(1); 247 mAnotherLowPriorityBuffer = new ArrayList(nOtherEntries * 2); 248 249 final long delayTime = BROADCAST_RATE_LIMIT / 2; 250 251 // add several low priority entries across multiple tags 252 final long firstEntryTime = SystemClock.elapsedRealtime(); 253 sendExcessiveDropBoxEntries(LOW_PRIORITY_TAG, nLowPriorityEntries, 0); 254 Thread.sleep(delayTime); 255 final long startTime = SystemClock.elapsedRealtime(); 256 sendExcessiveDropBoxEntries(ANOTHER_LOW_PRIORITY_TAG, nOtherEntries, 0); 257 assertTrue(mAnotherLowPriorityTagLatch.await(BROADCAST_RATE_LIMIT * 3 / 2, 258 TimeUnit.MILLISECONDS)); 259 final long endTime = SystemClock.elapsedRealtime(); 260 261 assertEqualsWithinDelta("Broadcast not received at expected time", BROADCAST_RATE_LIMIT, 262 endTime - startTime, BROADCAST_DELAY_ALLOWED_ERROR); 263 264 assertEquals("Many low priority dropbox entries within the rate limit period should " + 265 "result in 1 broadcast for " + LOW_PRIORITY_TAG, 1, mLowPriorityBuffer.size()); 266 assertEquals("Many low priority dropbox entries within the rate limit period should " + 267 "result in 1 broadcastfor " + ANOTHER_LOW_PRIORITY_TAG, 1, 268 mAnotherLowPriorityBuffer.size()); 269 DropBoxEntryAddedData data = mLowPriorityBuffer.get(0); 270 DropBoxEntryAddedData anotherData = mAnotherLowPriorityBuffer.get(0); 271 assertEquals("All but one of the low priority broadcasts should have been dropped for " + 272 LOW_PRIORITY_TAG, nLowPriorityEntries - 1, data.droppedCount); 273 assertEquals("All but one of the low priority broadcasts should have been dropped for " + 274 ANOTHER_LOW_PRIORITY_TAG, nOtherEntries - 1, anotherData.droppedCount); 275 276 final long startTimeDelta = startTime - firstEntryTime; 277 final long receivedTimeDelta = anotherData.received - data.received; 278 final long errorMargin = receivedTimeDelta - startTimeDelta; 279 280 // Received time delta should be around start time delta (20% margin of error) 281 if (errorMargin < -startTimeDelta / 5 || errorMargin > startTimeDelta / 5 ) { 282 fail("Multiple low priority entry tags interfered with each others delayed broadcast" + 283 "\nstartTimeDelta = " + String.valueOf(startTimeDelta) + 284 "\nreceivedTimeDelta = " + String.valueOf(receivedTimeDelta)); 285 } 286 } 287 288 /** 289 * Broadcasts for regular priority DropBox entries should not be throttled and they should not 290 * interfere with the throttling of low priority Dropbox entry broadcasts. 291 */ 292 @Test testLowPriorityRateLimitingWithEnabledEntries()293 public void testLowPriorityRateLimitingWithEnabledEntries() throws Exception { 294 295 final int nLowPriorityEntries = 10; 296 final int nEnabledEntries = 10; 297 298 mLowPriorityTagLatch = new CountDownLatch(1); 299 mLowPriorityBuffer = new ArrayList(nLowPriorityEntries * 2); 300 mEnabledTagLatch = new CountDownLatch(nEnabledEntries); 301 mEnabledBuffer = new ArrayList(nEnabledEntries * 2); 302 303 final long startTimeDelta = BROADCAST_RATE_LIMIT / 2; 304 305 final long startTime = SystemClock.elapsedRealtime(); 306 // add several low priority and enabled entries 307 sendExcessiveDropBoxEntries(LOW_PRIORITY_TAG, nLowPriorityEntries, 0); 308 sendExcessiveDropBoxEntries(ENABLED_TAG, nEnabledEntries, 0); 309 assertTrue(mLowPriorityTagLatch.await(BROADCAST_RATE_LIMIT * 3 / 2, 310 TimeUnit.MILLISECONDS)); 311 final long endTime = SystemClock.elapsedRealtime(); 312 313 assertEqualsWithinDelta("Broadcast not received at expected time", BROADCAST_RATE_LIMIT, 314 endTime - startTime, BROADCAST_DELAY_ALLOWED_ERROR); 315 316 assertEquals("Broadcasts for enabled tags should not be limited", nEnabledEntries, 317 mEnabledBuffer.size()); 318 319 assertEquals("Many low priority dropbox entries within the rate limit period should " + 320 "result in 1 broadcast for " + LOW_PRIORITY_TAG, 1, mLowPriorityBuffer.size()); 321 DropBoxEntryAddedData data = mLowPriorityBuffer.get(0); 322 assertEquals("All but one of the low priority broadcasts should have been dropped " + 323 LOW_PRIORITY_TAG, nLowPriorityEntries - 1, data.droppedCount); 324 325 for (int i = 0; i < nEnabledEntries; i++) { 326 DropBoxEntryAddedData enabledData = mEnabledBuffer.get(i); 327 assertEquals("Enabled tag broadcasts should not be dropped", 0, 328 enabledData.droppedCount); 329 } 330 } 331 setTagLowPriority(String tag)332 private void setTagLowPriority(String tag) throws IOException { 333 final String putCmd = MessageFormat.format(ADD_LOW_PRIORITY_SHELL_COMMAND, tag); 334 SystemUtil.runShellCommand(putCmd); 335 } 336 setBroadcastRateLimitSetting(long period)337 private void setBroadcastRateLimitSetting(long period) throws IOException { 338 final String putCmd = MessageFormat.format(SET_RATE_LIMIT_SHELL_COMMAND, 339 String.valueOf(period)); 340 SystemUtil.runShellCommand(putCmd); 341 } 342 restoreDropboxDefaults()343 private void restoreDropboxDefaults() throws IOException { 344 SystemUtil.runShellCommand(RESTORE_DEFAULTS_SHELL_COMMAND); 345 } 346 assertEqualsWithinDelta(String msg, long expected, long actual, long delta)347 private void assertEqualsWithinDelta(String msg, long expected, long actual, long delta) { 348 if (expected - actual > delta || actual - expected > delta) { 349 assertEquals(msg, expected, actual); 350 } 351 } 352 } 353