MediaResourceGetterTest.java revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2014 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5package org.chromium.content.browser; 6 7import android.annotation.SuppressLint; 8import android.content.Context; 9import android.content.pm.PackageManager; 10import android.media.MediaMetadataRetriever; 11import android.net.ConnectivityManager; 12import android.test.InstrumentationTestCase; 13import android.test.mock.MockContext; 14import android.test.suitebuilder.annotation.SmallTest; 15import android.util.SparseArray; 16 17import org.chromium.content.browser.MediaResourceGetter.MediaMetadata; 18 19import java.io.File; 20import java.util.Collections; 21import java.util.HashMap; 22import java.util.Map; 23 24/** 25 * Tests for MediaResourceGetter. 26 */ 27@SuppressLint("SdCardPath") 28public class MediaResourceGetterTest extends InstrumentationTestCase { 29 private static final String TEST_HTTP_URL = "http://example.com"; 30 private static final String TEST_USER_AGENT = // Anything, really 31 "Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 " + 32 "(KHTML, like Gecko) Chrome/32.0.1667.0 Safari/537.36"; 33 private static final String TEST_FILE_PATH = "/mnt/sdcard/test"; 34 private static final String TEST_FILE_URL = "file://" + TEST_FILE_PATH; 35 private static final String TEST_COOKIES = "yum yum yum!"; 36 private static final MediaMetadata sEmptyMetadata = new MediaMetadata(0, 0, 0, false); 37 private static final String sExternalStorageDirectory = "/test_external_storage"; 38 39 private static final Map<String,String> sHeadersCookieOnly; 40 private static final Map<String,String> sHeadersCookieAndUA; 41 private static final Map<String,String> sHeadersUAOnly; 42 static { 43 Map<String,String> headers = new HashMap<String, String>(); 44 headers.put("Cookie", TEST_COOKIES); 45 sHeadersCookieOnly = Collections.unmodifiableMap(headers); 46 47 headers = new HashMap<String, String>(); 48 headers.put("User-Agent", TEST_USER_AGENT); 49 sHeadersUAOnly = Collections.unmodifiableMap(headers); 50 51 headers = new HashMap<String, String>(); 52 headers.putAll(sHeadersCookieOnly); 53 headers.putAll(sHeadersUAOnly); 54 sHeadersCookieAndUA = Collections.unmodifiableMap(headers); 55 } 56 57 private static class FakeFile extends File { 58 final boolean mExists; 59 60 public FakeFile(String path, boolean exists) { 61 super(path); 62 mExists = exists; 63 } 64 65 @Override 66 public int hashCode() { 67 final int prime = 31; 68 int result = super.hashCode(); 69 result = prime * result + (mExists ? 1231 : 1237); 70 return result; 71 } 72 73 @Override 74 public boolean equals(Object obj) { 75 if (this == obj) 76 return true; 77 if (!super.equals(obj)) 78 return false; 79 if (getClass() != obj.getClass()) 80 return false; 81 FakeFile other = (FakeFile) obj; 82 if (mExists != other.mExists) 83 return false; 84 return true; 85 } 86 87 @Override 88 public boolean exists() { 89 return mExists; 90 } 91 } 92 93 /** 94 * Helper class that fakes functionality we cannot perform without real 95 * media resources. The class being faked here has been architected 96 * carefully to allow most of the functionality to be tested. What remains 97 * here is overrides of trivial functionality. 98 */ 99 private static class FakeMediaResourceGetter extends MediaResourceGetter { 100 // Read these back in tests to ensure proper values passed through 101 String mUri = null; 102 Map<String,String> mHeaders = null; 103 String mPath = null; 104 int mFd; 105 long mOffset; 106 long mLength; 107 108 // Write these before tests to configure functionality 109 SparseArray<String> mMetadata = null; 110 Integer mNetworkType = null; 111 boolean mThrowExceptionInConfigure = false; 112 boolean mThrowExceptionInExtract = false; 113 boolean mFileExists = false; 114 115 // Can't use a real MediaMetadataRetriever as we have no media 116 @Override 117 public void configure(int fd, long offset, long length) { 118 if (mThrowExceptionInConfigure) { 119 throw new RuntimeException("test exception"); 120 } 121 mFd = fd; 122 mOffset = offset; 123 mLength = length; 124 } 125 126 // Can't use a real MediaMetadataRetriever as we have no media 127 @Override 128 public void configure(String uri, Map<String, String> headers) { 129 if (mThrowExceptionInConfigure) { 130 throw new RuntimeException("test exception"); 131 } 132 mUri = uri; 133 mHeaders = headers; 134 } 135 136 // Can't use a real MediaMetadataRetriever as we have no media 137 @Override 138 public void configure(String path) { 139 if (mThrowExceptionInConfigure) { 140 throw new RuntimeException("test exception"); 141 } 142 mPath = path; 143 } 144 145 // Can't use a real MediaMetadataRetriever as we have no media 146 @Override 147 public String extractMetadata(int key) { 148 if (mThrowExceptionInExtract) { 149 throw new RuntimeException("test exception"); 150 } 151 if (mMetadata == null) { 152 return null; 153 } 154 return mMetadata.get(key); 155 } 156 157 // Can't use a real NetworkInfo object because there is no way to 158 // mock the ConnectivityManager in Android. 159 @Override 160 Integer getNetworkType(Context context) { 161 return mNetworkType; 162 } 163 164 // Can't use Environment.getExternalStorageDirectory because it could 165 // be inconsistent depending upon the state of the real or emulated 166 // device upon which we are testing. 167 @Override 168 String getExternalStorageDirectory() { 169 return sExternalStorageDirectory; 170 } 171 172 // Can't use regular File because we need to control the return from 173 // File.exists() on arbitrary paths. 174 @Override 175 File uriToFile(String path) { 176 FakeFile result = new FakeFile(path, mFileExists); 177 return result; 178 } 179 180 /** 181 * Convenience method to bind a metadata value to a key. 182 * @param key the key 183 * @param value the value 184 */ 185 void bind(int key, String value) { 186 if (mMetadata == null) { 187 mMetadata = new SparseArray<String>(); 188 } 189 mMetadata.put(key, value); 190 } 191 192 } 193 194 /** 195 * Helper class to control the result of permission checks. 196 */ 197 private static class InternalMockContext extends MockContext { 198 boolean allowPermission = false; 199 @Override 200 public int checkCallingOrSelfPermission(String permission) { 201 assertEquals(android.Manifest.permission.ACCESS_NETWORK_STATE, 202 permission); 203 return allowPermission ? PackageManager.PERMISSION_GRANTED : 204 PackageManager.PERMISSION_DENIED; 205 } 206 } 207 208 // Our test objects. 209 private FakeMediaResourceGetter mFakeMRG; 210 private InternalMockContext mMockContext; 211 212 @Override 213 protected void setUp() throws Exception { 214 super.setUp(); 215 mFakeMRG = new FakeMediaResourceGetter(); 216 mMockContext = new InternalMockContext(); 217 } 218 219 @SmallTest 220 public void testMediaMetadataEquals() { 221 assertEquals(sEmptyMetadata, sEmptyMetadata); 222 assertEquals(sEmptyMetadata, new MediaMetadata(0, 0, 0, false)); 223 assertFalse(sEmptyMetadata.equals(null)); 224 assertFalse(sEmptyMetadata.equals("test")); 225 assertFalse(sEmptyMetadata.equals(new MediaMetadata(1, 0, 0, false))); 226 assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 1, 0, false))); 227 assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 1, false))); 228 assertFalse(sEmptyMetadata.equals(new MediaMetadata(0, 0, 0, true))); 229 } 230 231 @SmallTest 232 public void testMediaMetadataHashCode() { 233 assertEquals(sEmptyMetadata.hashCode(), sEmptyMetadata.hashCode()); 234 assertEquals(sEmptyMetadata.hashCode(), new MediaMetadata(0, 0, 0, false).hashCode()); 235 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(1, 0, 0, false).hashCode()); 236 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 1, 0, false).hashCode()); 237 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 1, false).hashCode()); 238 assertFalse(sEmptyMetadata.hashCode() == new MediaMetadata(0, 0, 0, true).hashCode()); 239 } 240 241 @SmallTest 242 public void testMediaMetadataGetters() { 243 MediaMetadata data = new MediaMetadata(1, 2, 3, true); 244 assertEquals(1, data.getDurationInMilliseconds()); 245 assertEquals(2, data.getWidth()); 246 assertEquals(3, data.getHeight()); 247 assertTrue(data.isSuccess()); 248 249 // Validate no overlap of test values with defaults 250 data = new MediaMetadata(4, 5, 6, false); 251 assertEquals(4, data.getDurationInMilliseconds()); 252 assertEquals(5, data.getWidth()); 253 assertEquals(6, data.getHeight()); 254 assertFalse(data.isSuccess()); 255 } 256 257 @SmallTest 258 public void testConfigure_Net_NoPermissions() { 259 mMockContext.allowPermission = false; 260 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 261 TEST_COOKIES, TEST_USER_AGENT)); 262 } 263 264 @SmallTest 265 public void testConfigure_Net_NoActiveNetwork() { 266 mMockContext.allowPermission = true; 267 mFakeMRG.mNetworkType = null; 268 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 269 TEST_COOKIES, TEST_USER_AGENT)); 270 } 271 272 @SmallTest 273 public void testConfigure_Net_Disallowed_Mobile() { 274 mMockContext.allowPermission = true; 275 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_MOBILE; 276 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 277 TEST_COOKIES, TEST_USER_AGENT)); 278 } 279 280 @SmallTest 281 public void testConfigure_Net_Disallowed_Wimax() { 282 mMockContext.allowPermission = true; 283 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIMAX; 284 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 285 TEST_COOKIES, TEST_USER_AGENT)); 286 } 287 288 @SmallTest 289 public void testConfigure_Net_Allowed_Ethernet_Cookies_NoUA() { 290 mMockContext.allowPermission = true; 291 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; 292 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 293 TEST_COOKIES, null)); 294 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri); 295 assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders); 296 assertNull(mFakeMRG.mPath); 297 } 298 299 @SmallTest 300 public void testConfigure_Net_Allowed_Wifi_Cookies_NoUA() { 301 mMockContext.allowPermission = true; 302 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_WIFI; 303 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 304 TEST_COOKIES, null)); 305 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri); 306 assertEquals(sHeadersCookieOnly, mFakeMRG.mHeaders); 307 assertNull(mFakeMRG.mPath); 308 } 309 310 @SmallTest 311 public void testConfigure_Net_Allowed_Ethernet_NoCookies_NoUA() { 312 mMockContext.allowPermission = true; 313 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; 314 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 315 "", null)); 316 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri); 317 assertEquals(Collections.emptyMap(), mFakeMRG.mHeaders); 318 assertNull(mFakeMRG.mPath); 319 } 320 321 @SmallTest 322 public void testConfigure_Net_Allowed_Ethernet_Cookies_WithUA() { 323 mMockContext.allowPermission = true; 324 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; 325 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 326 TEST_COOKIES, TEST_USER_AGENT)); 327 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri); 328 assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders); 329 assertNull(mFakeMRG.mPath); 330 } 331 332 @SmallTest 333 public void testConfigure_Net_Allowed_Ethernet_NoCookies_WithUA() { 334 mMockContext.allowPermission = true; 335 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; 336 assertTrue(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 337 "", TEST_USER_AGENT)); 338 assertEquals(TEST_HTTP_URL, mFakeMRG.mUri); 339 assertEquals(sHeadersUAOnly, mFakeMRG.mHeaders); 340 assertNull(mFakeMRG.mPath); 341 } 342 343 @SmallTest 344 public void testConfigure_Net_Allowed_Ethernet_Exception() { 345 mMockContext.allowPermission = true; 346 mFakeMRG.mThrowExceptionInConfigure = true; 347 mFakeMRG.mNetworkType = ConnectivityManager.TYPE_ETHERNET; 348 assertFalse(mFakeMRG.configure(mMockContext, TEST_HTTP_URL, 349 "", TEST_USER_AGENT)); 350 assertNull(mFakeMRG.mUri); 351 assertNull(mFakeMRG.mHeaders); 352 } 353 354 @SmallTest 355 public void testConfigure_Net_Allowed_LocalHost_WithNoNetwork() { 356 String[] localHostUrls = { 357 "http://LocalHost", 358 "https://127.0.0.1/", 359 "http://[::1]:8888/", 360 }; 361 mMockContext.allowPermission = true; 362 mFakeMRG.mNetworkType = null; 363 for (String localHostUrl : localHostUrls) { 364 assertTrue(mFakeMRG.configure(mMockContext, localHostUrl, 365 TEST_COOKIES, TEST_USER_AGENT)); 366 assertEquals(localHostUrl, mFakeMRG.mUri); 367 assertEquals(sHeadersCookieAndUA, mFakeMRG.mHeaders); 368 assertNull(mFakeMRG.mPath); 369 } 370 } 371 372 @SmallTest 373 public void testConfigure_File_Allowed_MntSdcard() { 374 final String path = "/mnt/sdcard/test"; 375 final String url = "file://" + path; 376 mFakeMRG.mFileExists = true; 377 assertTrue(mFakeMRG.configure(mMockContext, url, "", null)); 378 assertEquals(path, mFakeMRG.mPath); 379 assertNull(mFakeMRG.mUri); 380 assertNull(mFakeMRG.mHeaders); 381 } 382 383 @SmallTest 384 public void testConfigure_File_Allowed_Sdcard() { 385 final String path = "/sdcard/test"; 386 final String url = "file://" + path; 387 mFakeMRG.mFileExists = true; 388 assertTrue(mFakeMRG.configure(mMockContext, url, "", null)); 389 assertEquals(path, mFakeMRG.mPath); 390 assertNull(mFakeMRG.mUri); 391 assertNull(mFakeMRG.mHeaders); 392 } 393 394 @SmallTest 395 public void testConfigure_File_Allowed_Sdcard_DoesntExist() { 396 final String path = "/sdcard/test"; 397 final String url = "file://" + path; 398 mFakeMRG.mFileExists = false; 399 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 400 assertNull(mFakeMRG.mPath); 401 } 402 403 @SmallTest 404 public void testConfigure_File_Allowed_ExternalStorage() { 405 final String path = sExternalStorageDirectory + "/test"; 406 final String url = "file://" + path; 407 mFakeMRG.mFileExists = true; 408 assertTrue(mFakeMRG.configure(mMockContext, url, "", null)); 409 assertEquals(path, mFakeMRG.mPath); 410 assertNull(mFakeMRG.mUri); 411 assertNull(mFakeMRG.mHeaders); 412 } 413 414 @SmallTest 415 public void testConfigure_File_Disallowed_Innocent() { 416 final String path = "/malicious/path"; 417 final String url = "file://" + path; 418 mFakeMRG.mFileExists = true; 419 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 420 assertNull(mFakeMRG.mPath); 421 } 422 423 @SmallTest 424 public void testConfigure_File_Disallowed_Malicious() { 425 final String path = "/mnt/sdcard/../../data"; 426 final String url = "file://" + path; 427 mFakeMRG.mFileExists = true; 428 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 429 assertNull(mFakeMRG.mPath); 430 } 431 432 @SmallTest 433 public void testConfigure_File_Allowed_Exception() { 434 final String path = "/mnt/sdcard/test"; 435 final String url = "file://" + path; 436 mFakeMRG.mFileExists = true; 437 mFakeMRG.mThrowExceptionInConfigure = true; 438 assertFalse(mFakeMRG.configure(mMockContext, url, "", null)); 439 assertNull(mFakeMRG.mPath); 440 } 441 442 @SmallTest 443 public void testExtract_NoMetadata() { 444 mFakeMRG.mFileExists = true; 445 assertEquals(sEmptyMetadata, mFakeMRG.extract( 446 mMockContext, TEST_FILE_URL, null, null)); 447 assertEquals("configured successfully when we shouldn't have", 448 TEST_FILE_PATH, mFakeMRG.mPath); // tricky 449 } 450 451 @SmallTest 452 public void testExtract_WithMetadata_ValidDuration() { 453 mFakeMRG.mFileExists = true; 454 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 455 final MediaMetadata expected = new MediaMetadata(1, 0, 0, true); 456 assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 457 } 458 459 @SmallTest 460 public void testExtract_WithMetadata_InvalidDuration() { 461 mFakeMRG.mFileExists = true; 462 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "i am not an integer"); 463 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 464 } 465 466 @SmallTest 467 public void testExtract_WithMetadata_ValidDuration_HasVideo_NoWidth_NoHeight() { 468 mFakeMRG.mFileExists = true; 469 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 470 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 471 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 472 } 473 474 @SmallTest 475 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_NoHeight() { 476 mFakeMRG.mFileExists = true; 477 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 478 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 479 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1"); 480 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 481 } 482 483 @SmallTest 484 public void testExtract_WithMetadata_ValidDuration_HasVideo_InvalidWidth_NoHeight() { 485 mFakeMRG.mFileExists = true; 486 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 487 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 488 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "i am not an integer"); 489 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 490 } 491 492 @SmallTest 493 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_ValidHeight() { 494 mFakeMRG.mFileExists = true; 495 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 496 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 497 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2"); 498 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3"); 499 final MediaMetadata expected = new MediaMetadata(1, 2, 3, true); 500 assertEquals(expected, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 501 } 502 503 @SmallTest 504 public void testExtract_WithMetadata_ValidDuration_HasVideo_ValidWidth_InvalidHeight() { 505 mFakeMRG.mFileExists = true; 506 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 507 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 508 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "1"); 509 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "i am not an integer"); 510 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 511 } 512 513 @SmallTest 514 public void testExtract_WithMetadata_ValidDuration_ExceptionInExtract() { 515 mFakeMRG.mFileExists = true; 516 mFakeMRG.mThrowExceptionInExtract = true; 517 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 518 assertEquals(sEmptyMetadata, mFakeMRG.extract(mMockContext, TEST_FILE_URL, null, null)); 519 } 520 521 @SmallTest 522 public void testExtractFromFileDescriptor_ValidMetadata() { 523 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_DURATION, "1"); 524 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_HAS_VIDEO, "yes"); 525 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH, "2"); 526 mFakeMRG.bind(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT, "3"); 527 final MediaMetadata expected = new MediaMetadata(1, 2, 3, true); 528 int fd = 1234; 529 long offset = 1000; 530 long length = 9000; 531 assertEquals(expected, mFakeMRG.extract(fd, offset, length)); 532 assertEquals(fd, mFakeMRG.mFd); 533 assertEquals(offset, mFakeMRG.mOffset); 534 assertEquals(length, mFakeMRG.mLength); 535 } 536 537 @SmallTest 538 public void testAndroidDeviceOk_BadModel_BadVersion() { 539 assertFalse(MediaResourceGetter.androidDeviceOk( 540 "GT-I9100", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1)); 541 } 542 543 @SmallTest 544 public void testAndroidDeviceOk_BadModel_GoodVersion() { 545 assertTrue(MediaResourceGetter.androidDeviceOk( 546 "GT-I9100", android.os.Build.VERSION_CODES.JELLY_BEAN)); 547 } 548 549 @SmallTest 550 public void testAndroidDeviceOk_GoodModel_AnyVersion() { 551 assertTrue(MediaResourceGetter.androidDeviceOk( 552 "Happy Device", android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH)); 553 } 554} 555