1 /* 2 * Copyright (C) 2014 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.cts.splitapp; 18 19 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 20 import static org.xmlpull.v1.XmlPullParser.START_TAG; 21 22 import android.content.BroadcastReceiver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.content.IntentFilter; 26 import android.content.pm.ApplicationInfo; 27 import android.content.pm.PackageInfo; 28 import android.content.pm.PackageManager; 29 import android.content.pm.ProviderInfo; 30 import android.content.pm.ResolveInfo; 31 import android.content.res.Configuration; 32 import android.content.res.Resources; 33 import android.database.Cursor; 34 import android.database.sqlite.SQLiteDatabase; 35 import android.graphics.Bitmap; 36 import android.graphics.Canvas; 37 import android.graphics.drawable.Drawable; 38 import android.os.ConditionVariable; 39 import android.os.Environment; 40 import android.os.ParcelFileDescriptor; 41 import android.os.StatFs; 42 import android.system.Os; 43 import android.system.OsConstants; 44 import android.system.StructStat; 45 import android.test.AndroidTestCase; 46 import android.test.MoreAsserts; 47 import android.util.DisplayMetrics; 48 import android.util.Log; 49 50 import org.xmlpull.v1.XmlPullParser; 51 import org.xmlpull.v1.XmlPullParserException; 52 53 import java.io.BufferedReader; 54 import java.io.DataInputStream; 55 import java.io.DataOutputStream; 56 import java.io.File; 57 import java.io.FileDescriptor; 58 import java.io.FileInputStream; 59 import java.io.FileOutputStream; 60 import java.io.IOException; 61 import java.io.InputStreamReader; 62 import java.lang.reflect.Field; 63 import java.lang.reflect.Method; 64 import java.util.List; 65 import java.util.Locale; 66 67 public class SplitAppTest extends AndroidTestCase { 68 private static final String TAG = "SplitAppTest"; 69 private static final String PKG = "com.android.cts.splitapp"; 70 71 private static final long MB_IN_BYTES = 1 * 1024 * 1024; 72 73 public static boolean sFeatureTouched = false; 74 public static String sFeatureValue = null; 75 testNothing()76 public void testNothing() throws Exception { 77 } 78 testSingleBase()79 public void testSingleBase() throws Exception { 80 final Resources r = getContext().getResources(); 81 final PackageManager pm = getContext().getPackageManager(); 82 83 // Should have untouched resources from base 84 assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled)); 85 86 assertEquals("blue", r.getString(R.string.my_string1)); 87 assertEquals("purple", r.getString(R.string.my_string2)); 88 89 assertEquals(0xff00ff00, r.getColor(R.color.my_color)); 90 assertEquals(123, r.getInteger(R.integer.my_integer)); 91 92 assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta))); 93 94 // We know about drawable IDs, but they're stripped from base 95 try { 96 r.getDrawable(R.drawable.image); 97 fail("Unexpected drawable in base"); 98 } catch (Resources.NotFoundException expected) { 99 } 100 101 // Should have base assets 102 assertAssetContents(r, "file1.txt", "FILE1"); 103 assertAssetContents(r, "dir/dirfile1.txt", "DIRFILE1"); 104 105 try { 106 assertAssetContents(r, "file2.txt", null); 107 fail("Unexpected asset file2"); 108 } catch (IOException expected) { 109 } 110 111 // Should only have base manifest items 112 Intent intent = new Intent(Intent.ACTION_MAIN); 113 intent.addCategory(Intent.CATEGORY_LAUNCHER); 114 intent.setPackage(PKG); 115 116 List<ResolveInfo> result = pm.queryIntentActivities(intent, 0); 117 assertEquals(1, result.size()); 118 assertEquals("com.android.cts.splitapp.MyActivity", result.get(0).activityInfo.name); 119 120 // Receiver disabled by default in base 121 intent = new Intent(Intent.ACTION_DATE_CHANGED); 122 intent.setPackage(PKG); 123 124 result = pm.queryBroadcastReceivers(intent, 0); 125 assertEquals(0, result.size()); 126 127 // We shouldn't have any native code in base 128 try { 129 Native.add(2, 4); 130 fail("Unexpected native code in base"); 131 } catch (UnsatisfiedLinkError expected) { 132 } 133 } 134 testDensitySingle()135 public void testDensitySingle() throws Exception { 136 final Resources r = getContext().getResources(); 137 138 // We should still have base resources 139 assertEquals("blue", r.getString(R.string.my_string1)); 140 assertEquals("purple", r.getString(R.string.my_string2)); 141 142 // Now we know about drawables, but only mdpi 143 final Drawable d = r.getDrawable(R.drawable.image); 144 assertEquals(0xff7e00ff, getDrawableColor(d)); 145 } 146 testDensityAll()147 public void testDensityAll() throws Exception { 148 final Resources r = getContext().getResources(); 149 150 // We should still have base resources 151 assertEquals("blue", r.getString(R.string.my_string1)); 152 assertEquals("purple", r.getString(R.string.my_string2)); 153 154 // Pretend that we're at each density 155 updateDpi(r, DisplayMetrics.DENSITY_MEDIUM); 156 assertEquals(0xff7e00ff, getDrawableColor(r.getDrawable(R.drawable.image))); 157 158 updateDpi(r, DisplayMetrics.DENSITY_HIGH); 159 assertEquals(0xff00fcff, getDrawableColor(r.getDrawable(R.drawable.image))); 160 161 updateDpi(r, DisplayMetrics.DENSITY_XHIGH); 162 assertEquals(0xff80ff00, getDrawableColor(r.getDrawable(R.drawable.image))); 163 164 updateDpi(r, DisplayMetrics.DENSITY_XXHIGH); 165 assertEquals(0xffff0000, getDrawableColor(r.getDrawable(R.drawable.image))); 166 } 167 testDensityBest1()168 public void testDensityBest1() throws Exception { 169 final Resources r = getContext().getResources(); 170 171 // Pretend that we're really high density, but we only have mdpi installed 172 updateDpi(r, DisplayMetrics.DENSITY_XXHIGH); 173 assertEquals(0xff7e00ff, getDrawableColor(r.getDrawable(R.drawable.image))); 174 } 175 testDensityBest2()176 public void testDensityBest2() throws Exception { 177 final Resources r = getContext().getResources(); 178 179 // Pretend that we're really high density, and now we have better match 180 updateDpi(r, DisplayMetrics.DENSITY_XXHIGH); 181 assertEquals(0xffff0000, getDrawableColor(r.getDrawable(R.drawable.image))); 182 } 183 testApi()184 public void testApi() throws Exception { 185 final Resources r = getContext().getResources(); 186 final PackageManager pm = getContext().getPackageManager(); 187 188 // We should have updated boolean, different from base 189 assertEquals(true, r.getBoolean(R.bool.my_receiver_enabled)); 190 191 // Receiver should be enabled now 192 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); 193 intent.setPackage(PKG); 194 195 List<ResolveInfo> result = pm.queryBroadcastReceivers(intent, 0); 196 assertEquals(1, result.size()); 197 assertEquals("com.android.cts.splitapp.MyReceiver", result.get(0).activityInfo.name); 198 } 199 testLocale()200 public void testLocale() throws Exception { 201 final Resources r = getContext().getResources(); 202 203 updateLocale(r, Locale.ENGLISH); 204 assertEquals("blue", r.getString(R.string.my_string1)); 205 assertEquals("purple", r.getString(R.string.my_string2)); 206 207 updateLocale(r, Locale.GERMAN); 208 assertEquals("blau", r.getString(R.string.my_string1)); 209 assertEquals("purple", r.getString(R.string.my_string2)); 210 211 updateLocale(r, Locale.FRENCH); 212 assertEquals("blue", r.getString(R.string.my_string1)); 213 assertEquals("pourpre", r.getString(R.string.my_string2)); 214 } 215 testNative()216 public void testNative() throws Exception { 217 Log.d(TAG, "testNative() thinks it's using ABI " + Native.arch()); 218 219 // Make sure we can do the maths 220 assertEquals(11642, Native.add(4933, 6709)); 221 } 222 testFeatureBase()223 public void testFeatureBase() throws Exception { 224 final Resources r = getContext().getResources(); 225 final PackageManager pm = getContext().getPackageManager(); 226 227 // Should have untouched resources from base 228 assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled)); 229 230 assertEquals("blue", r.getString(R.string.my_string1)); 231 assertEquals("purple", r.getString(R.string.my_string2)); 232 233 assertEquals(0xff00ff00, r.getColor(R.color.my_color)); 234 assertEquals(123, r.getInteger(R.integer.my_integer)); 235 236 assertEquals("base", getXmlTestValue(r.getXml(R.xml.my_activity_meta))); 237 238 // And that we can access resources from feature 239 assertEquals("red", r.getString(r.getIdentifier("feature_string", "string", PKG))); 240 assertEquals(123, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG))); 241 242 final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR"); 243 final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null); 244 final int intId = (int) featR.getDeclaredField("feature_integer").get(null); 245 final int stringId = (int) featR.getDeclaredField("feature_string").get(null); 246 assertEquals(true, r.getBoolean(boolId)); 247 assertEquals(123, r.getInteger(intId)); 248 assertEquals("red", r.getString(stringId)); 249 250 // Should have both base and feature assets 251 assertAssetContents(r, "file1.txt", "FILE1"); 252 assertAssetContents(r, "file2.txt", "FILE2"); 253 assertAssetContents(r, "dir/dirfile1.txt", "DIRFILE1"); 254 assertAssetContents(r, "dir/dirfile2.txt", "DIRFILE2"); 255 256 // Should have both base and feature components 257 Intent intent = new Intent(Intent.ACTION_MAIN); 258 intent.addCategory(Intent.CATEGORY_LAUNCHER); 259 intent.setPackage(PKG); 260 List<ResolveInfo> result = pm.queryIntentActivities(intent, 0); 261 assertEquals(2, result.size()); 262 assertEquals("com.android.cts.splitapp.MyActivity", result.get(0).activityInfo.name); 263 assertEquals("com.android.cts.splitapp.FeatureActivity", result.get(1).activityInfo.name); 264 265 // Receiver only enabled in feature 266 intent = new Intent(Intent.ACTION_DATE_CHANGED); 267 intent.setPackage(PKG); 268 result = pm.queryBroadcastReceivers(intent, 0); 269 assertEquals(1, result.size()); 270 assertEquals("com.android.cts.splitapp.FeatureReceiver", result.get(0).activityInfo.name); 271 272 // And we should have a service 273 intent = new Intent("com.android.cts.splitapp.service"); 274 intent.setPackage(PKG); 275 result = pm.queryIntentServices(intent, 0); 276 assertEquals(1, result.size()); 277 assertEquals("com.android.cts.splitapp.FeatureService", result.get(0).serviceInfo.name); 278 279 // And a provider too 280 ProviderInfo info = pm.resolveContentProvider("com.android.cts.splitapp.provider", 0); 281 assertEquals("com.android.cts.splitapp.FeatureProvider", info.name); 282 283 // And assert that we spun up the provider in this process 284 final Class<?> provider = Class.forName("com.android.cts.splitapp.FeatureProvider"); 285 final Field field = provider.getDeclaredField("sCreated"); 286 assertTrue("Expected provider to have been created", (boolean) field.get(null)); 287 assertTrue("Expected provider to have touched us", sFeatureTouched); 288 assertEquals(r.getString(R.string.my_string1), sFeatureValue); 289 290 // Finally ensure that we can execute some code from split 291 final Class<?> logic = Class.forName("com.android.cts.splitapp.FeatureLogic"); 292 final Method method = logic.getDeclaredMethod("mult", new Class[] { 293 Integer.TYPE, Integer.TYPE }); 294 assertEquals(72, (int) method.invoke(null, 12, 6)); 295 296 // Make sure we didn't get an extra flag from feature split 297 assertTrue("Someone parsed application flag!", 298 (getContext().getApplicationInfo().flags & ApplicationInfo.FLAG_LARGE_HEAP) == 0); 299 300 // Make sure we have permission from base APK 301 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.CAMERA, null); 302 303 try { 304 // But no new permissions from the feature APK 305 getContext().enforceCallingOrSelfPermission(android.Manifest.permission.INTERNET, null); 306 fail("Whaaa, we somehow gained permission from feature?"); 307 } catch (SecurityException expected) { 308 } 309 } 310 testBaseInstalled()311 public void testBaseInstalled() throws Exception { 312 final ConditionVariable cv = new ConditionVariable(); 313 final BroadcastReceiver r = new BroadcastReceiver() { 314 @Override 315 public void onReceive(Context context, Intent intent) { 316 assertEquals(1, intent.getIntExtra("CREATE_COUNT", -1)); 317 assertEquals(0, intent.getIntExtra("NEW_INTENT_COUNT", -1)); 318 cv.open(); 319 } 320 }; 321 final IntentFilter filter = new IntentFilter("com.android.cts.norestart.BROADCAST"); 322 getContext().registerReceiver(r, filter); 323 final Intent i = new Intent("com.android.cts.norestart.START"); 324 i.addCategory(Intent.CATEGORY_DEFAULT); 325 getContext().startActivity(i); 326 assertTrue(cv.block(2000L)); 327 getContext().unregisterReceiver(r); 328 } 329 330 /** 331 * Tests a running activity remains active while a new feature split is installed. 332 * <p> 333 * Prior to running this test, the activity must be started. That is currently 334 * done in {@link #testBaseInstalled()}. 335 */ testFeatureInstalled()336 public void testFeatureInstalled() throws Exception { 337 final ConditionVariable cv = new ConditionVariable(); 338 final BroadcastReceiver r = new BroadcastReceiver() { 339 @Override 340 public void onReceive(Context context, Intent intent) { 341 assertEquals(1, intent.getIntExtra("CREATE_COUNT", -1)); 342 assertEquals(1, intent.getIntExtra("NEW_INTENT_COUNT", -1)); 343 cv.open(); 344 } 345 }; 346 final IntentFilter filter = new IntentFilter("com.android.cts.norestart.BROADCAST"); 347 getContext().registerReceiver(r, filter); 348 final Intent i = new Intent("com.android.cts.norestart.START"); 349 i.addCategory(Intent.CATEGORY_DEFAULT); 350 getContext().startActivity(i); 351 assertTrue(cv.block(2000L)); 352 getContext().unregisterReceiver(r); 353 } 354 testFeatureApi()355 public void testFeatureApi() throws Exception { 356 final Resources r = getContext().getResources(); 357 final PackageManager pm = getContext().getPackageManager(); 358 359 // Should have untouched resources from base 360 assertEquals(false, r.getBoolean(R.bool.my_receiver_enabled)); 361 362 // And that we can access resources from feature 363 assertEquals(321, r.getInteger(r.getIdentifier("feature_integer", "integer", PKG))); 364 365 final Class<?> featR = Class.forName("com.android.cts.splitapp.FeatureR"); 366 final int boolId = (int) featR.getDeclaredField("feature_receiver_enabled").get(null); 367 final int intId = (int) featR.getDeclaredField("feature_integer").get(null); 368 final int stringId = (int) featR.getDeclaredField("feature_string").get(null); 369 assertEquals(false, r.getBoolean(boolId)); 370 assertEquals(321, r.getInteger(intId)); 371 assertEquals("red", r.getString(stringId)); 372 373 // And now both receivers should be disabled 374 Intent intent = new Intent(Intent.ACTION_DATE_CHANGED); 375 intent.setPackage(PKG); 376 List<ResolveInfo> result = pm.queryBroadcastReceivers(intent, 0); 377 assertEquals(0, result.size()); 378 } 379 380 /** 381 * Write app data in a number of locations that expect to remain intact over 382 * long periods of time, such as across app moves. 383 */ testDataWrite()384 public void testDataWrite() throws Exception { 385 final String token = String.valueOf(android.os.Process.myUid()); 386 writeString(getContext().getFileStreamPath("my_int"), token); 387 388 final SQLiteDatabase db = getContext().openOrCreateDatabase("my_db", 389 Context.MODE_PRIVATE, null); 390 try { 391 db.execSQL("DROP TABLE IF EXISTS my_table"); 392 db.execSQL("CREATE TABLE my_table(value INTEGER)"); 393 db.execSQL("INSERT INTO my_table VALUES (101), (102), (103)"); 394 } finally { 395 db.close(); 396 } 397 } 398 399 /** 400 * Verify that data written by {@link #testDataWrite()} is still intact. 401 */ testDataRead()402 public void testDataRead() throws Exception { 403 final String token = String.valueOf(android.os.Process.myUid()); 404 assertEquals(token, readString(getContext().getFileStreamPath("my_int"))); 405 406 final SQLiteDatabase db = getContext().openOrCreateDatabase("my_db", 407 Context.MODE_PRIVATE, null); 408 try { 409 final Cursor cursor = db.query("my_table", null, null, null, null, null, "value ASC"); 410 try { 411 assertEquals(3, cursor.getCount()); 412 assertTrue(cursor.moveToPosition(0)); 413 assertEquals(101, cursor.getInt(0)); 414 assertTrue(cursor.moveToPosition(1)); 415 assertEquals(102, cursor.getInt(0)); 416 assertTrue(cursor.moveToPosition(2)); 417 assertEquals(103, cursor.getInt(0)); 418 } finally { 419 cursor.close(); 420 } 421 } finally { 422 db.close(); 423 } 424 } 425 426 /** 427 * Verify that app is installed on internal storage. 428 */ testDataInternal()429 public void testDataInternal() throws Exception { 430 final StructStat internal = Os.stat(Environment.getDataDirectory().getAbsolutePath()); 431 final StructStat actual = Os.stat(getContext().getFilesDir().getAbsolutePath()); 432 assertEquals(internal.st_dev, actual.st_dev); 433 } 434 435 /** 436 * Verify that app is not installed on internal storage. 437 */ testDataNotInternal()438 public void testDataNotInternal() throws Exception { 439 final StructStat internal = Os.stat(Environment.getDataDirectory().getAbsolutePath()); 440 final StructStat actual = Os.stat(getContext().getFilesDir().getAbsolutePath()); 441 MoreAsserts.assertNotEqual(internal.st_dev, actual.st_dev); 442 } 443 testPrimaryDataWrite()444 public void testPrimaryDataWrite() throws Exception { 445 final String token = String.valueOf(android.os.Process.myUid()); 446 writeString(new File(getContext().getExternalFilesDir(null), "my_ext"), token); 447 } 448 testPrimaryDataRead()449 public void testPrimaryDataRead() throws Exception { 450 final String token = String.valueOf(android.os.Process.myUid()); 451 assertEquals(token, readString(new File(getContext().getExternalFilesDir(null), "my_ext"))); 452 } 453 454 /** 455 * Verify shared storage behavior when on internal storage. 456 */ testPrimaryInternal()457 public void testPrimaryInternal() throws Exception { 458 assertTrue("emulated", Environment.isExternalStorageEmulated()); 459 assertFalse("removable", Environment.isExternalStorageRemovable()); 460 assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState()); 461 } 462 463 /** 464 * Verify shared storage behavior when on physical storage. 465 */ testPrimaryPhysical()466 public void testPrimaryPhysical() throws Exception { 467 assertFalse("emulated", Environment.isExternalStorageEmulated()); 468 assertTrue("removable", Environment.isExternalStorageRemovable()); 469 assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState()); 470 } 471 472 /** 473 * Verify shared storage behavior when on adopted storage. 474 */ testPrimaryAdopted()475 public void testPrimaryAdopted() throws Exception { 476 assertTrue("emulated", Environment.isExternalStorageEmulated()); 477 assertTrue("removable", Environment.isExternalStorageRemovable()); 478 assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState()); 479 } 480 481 /** 482 * Verify that shared storage is unmounted. 483 */ testPrimaryUnmounted()484 public void testPrimaryUnmounted() throws Exception { 485 MoreAsserts.assertNotEqual(Environment.MEDIA_MOUNTED, 486 Environment.getExternalStorageState()); 487 } 488 489 /** 490 * Verify that shared storage lives on same volume as app. 491 */ testPrimaryOnSameVolume()492 public void testPrimaryOnSameVolume() throws Exception { 493 final File current = getContext().getFilesDir(); 494 final File primary = Environment.getExternalStorageDirectory(); 495 496 // Shared storage may jump through another filesystem for permission 497 // enforcement, so we verify that total/free space are identical. 498 final long totalDelta = Math.abs(current.getTotalSpace() - primary.getTotalSpace()); 499 final long freeDelta = Math.abs(current.getFreeSpace() - primary.getFreeSpace()); 500 if (totalDelta > MB_IN_BYTES || freeDelta > MB_IN_BYTES) { 501 fail("Expected primary storage to be on same volume as app"); 502 } 503 } 504 testCodeCacheWrite()505 public void testCodeCacheWrite() throws Exception { 506 assertTrue(new File(getContext().getFilesDir(), "normal.raw").createNewFile()); 507 assertTrue(new File(getContext().getCodeCacheDir(), "cache.raw").createNewFile()); 508 } 509 testCodeCacheRead()510 public void testCodeCacheRead() throws Exception { 511 assertTrue(new File(getContext().getFilesDir(), "normal.raw").exists()); 512 assertFalse(new File(getContext().getCodeCacheDir(), "cache.raw").exists()); 513 } 514 testRevision0_0()515 public void testRevision0_0() throws Exception { 516 final PackageInfo info = getContext().getPackageManager() 517 .getPackageInfo(getContext().getPackageName(), 0); 518 assertEquals(0, info.baseRevisionCode); 519 assertEquals(1, info.splitRevisionCodes.length); 520 assertEquals(0, info.splitRevisionCodes[0]); 521 } 522 testRevision12_0()523 public void testRevision12_0() throws Exception { 524 final PackageInfo info = getContext().getPackageManager() 525 .getPackageInfo(getContext().getPackageName(), 0); 526 assertEquals(12, info.baseRevisionCode); 527 assertEquals(1, info.splitRevisionCodes.length); 528 assertEquals(0, info.splitRevisionCodes[0]); 529 } 530 testRevision0_12()531 public void testRevision0_12() throws Exception { 532 final PackageInfo info = getContext().getPackageManager() 533 .getPackageInfo(getContext().getPackageName(), 0); 534 assertEquals(0, info.baseRevisionCode); 535 assertEquals(1, info.splitRevisionCodes.length); 536 assertEquals(12, info.splitRevisionCodes[0]); 537 } 538 updateDpi(Resources r, int densityDpi)539 private static void updateDpi(Resources r, int densityDpi) { 540 final Configuration c = new Configuration(r.getConfiguration()); 541 c.densityDpi = densityDpi; 542 r.updateConfiguration(c, r.getDisplayMetrics()); 543 } 544 updateLocale(Resources r, Locale locale)545 private static void updateLocale(Resources r, Locale locale) { 546 final Configuration c = new Configuration(r.getConfiguration()); 547 c.locale = locale; 548 r.updateConfiguration(c, r.getDisplayMetrics()); 549 } 550 getDrawableColor(Drawable d)551 private static int getDrawableColor(Drawable d) { 552 final Bitmap bitmap = Bitmap.createBitmap(d.getIntrinsicWidth(), d.getIntrinsicHeight(), 553 Bitmap.Config.ARGB_8888); 554 final Canvas canvas = new Canvas(bitmap); 555 d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight()); 556 d.draw(canvas); 557 return bitmap.getPixel(0, 0); 558 } 559 getXmlTestValue(XmlPullParser in)560 private static String getXmlTestValue(XmlPullParser in) throws XmlPullParserException, 561 IOException { 562 int type; 563 while ((type = in.next()) != END_DOCUMENT) { 564 if (type == START_TAG) { 565 final String tag = in.getName(); 566 if ("tag".equals(tag)) { 567 return in.getAttributeValue(null, "value"); 568 } 569 } 570 } 571 return null; 572 } 573 assertAssetContents(Resources r, String path, String expected)574 private static void assertAssetContents(Resources r, String path, String expected) 575 throws IOException { 576 BufferedReader in = null; 577 try { 578 in = new BufferedReader(new InputStreamReader(r.getAssets().open(path))); 579 assertEquals(expected, in.readLine()); 580 } finally { 581 if (in != null) in.close(); 582 } 583 } 584 writeString(File file, String value)585 private static void writeString(File file, String value) throws IOException { 586 final DataOutputStream os = new DataOutputStream(new FileOutputStream(file)); 587 try { 588 os.writeUTF(value); 589 } finally { 590 os.close(); 591 } 592 } 593 readString(File file)594 private static String readString(File file) throws IOException { 595 final DataInputStream is = new DataInputStream(new FileInputStream(file)); 596 try { 597 return is.readUTF(); 598 } finally { 599 is.close(); 600 } 601 } 602 } 603