From 80aea03d46272a47dd7e9ba5f98a70f2d67cfd84 Mon Sep 17 00:00:00 2001 From: John Spurlock Date: Wed, 18 Jul 2012 10:05:03 -0400 Subject: [PATCH 1/6] Fix systemui crash on Xoom startup Make new bugdroid drawable available to non-hdpi device builds. (moved to no-dpi for now) It was getting optimized out during the build process. Bug: 6828023 Change-Id: I31ac6ce2f50edc7f65d37b4b0f7513dd33a8b22d --- .../{drawable-hdpi => drawable-nodpi}/bugdroid.png | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename packages/SystemUI/res/{drawable-hdpi => drawable-nodpi}/bugdroid.png (100%) diff --git a/packages/SystemUI/res/drawable-hdpi/bugdroid.png b/packages/SystemUI/res/drawable-nodpi/bugdroid.png similarity index 100% rename from packages/SystemUI/res/drawable-hdpi/bugdroid.png rename to packages/SystemUI/res/drawable-nodpi/bugdroid.png From a70074ee4b32aa99ff55ba01c321194b9f5f83e6 Mon Sep 17 00:00:00 2001 From: Marco Nelissen Date: Wed, 9 Jan 2013 09:46:54 -0800 Subject: [PATCH 2/6] Fix media scan NPE b/7966288 Change-Id: I066bc9f00bccf070332d87d6f1bf3d6054b31136 --- media/java/android/media/MediaScanner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index fbbcb0a323e58..7768a61968b56 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -958,7 +958,9 @@ public class MediaScanner // If the rowId of the inserted file is needed, it gets inserted immediately, // bypassing the bulk inserter. if (inserter == null || needToSetSettings) { - inserter.flushAll(); + if (inserter != null) { + inserter.flushAll(); + } result = mMediaProvider.insert(tableUri, values); } else if (entry.mFormat == MtpConstants.FORMAT_ASSOCIATION) { inserter.insertwithPriority(tableUri, values); From b225548244cd79ad251194a6b1975350ba8bdfe5 Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 9 Jan 2013 15:54:56 -0800 Subject: [PATCH 3/6] Revert "App home directories are now 0700 for targetSdkVersion > 17" This reverts commit 92091fa9636403728fe94cc83400495a8612c2d3. Bug: 7966399 Bug: 7208882 --- api/current.txt | 1 - core/java/android/content/Context.java | 96 ++++++------------- core/java/android/os/Build.java | 11 --- .../java/com/android/server/pm/Installer.java | 18 +--- .../server/pm/PackageManagerService.java | 19 ++-- .../java/com/android/server/pm/Settings.java | 6 +- 6 files changed, 45 insertions(+), 106 deletions(-) diff --git a/api/current.txt b/api/current.txt index e4d5fd1f188c1..cf24f1347e51c 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15737,7 +15737,6 @@ package android.os { field public static final int ICE_CREAM_SANDWICH_MR1 = 15; // 0xf field public static final int JELLY_BEAN = 16; // 0x10 field public static final int JELLY_BEAN_MR1 = 17; // 0x11 - field public static final int K = 10000; // 0x2710 } public final class Bundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 257f84ef8e942..7aa2507975dc2 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -64,44 +64,30 @@ public abstract class Context { */ public static final int MODE_PRIVATE = 0x0000; /** + * @deprecated Creating world-readable files is very dangerous, and likely + * to cause security holes in applications. It is strongly discouraged; + * instead, applications should use more formal mechanism for interactions + * such as {@link ContentProvider}, {@link BroadcastReceiver}, and + * {@link android.app.Service}. There are no guarantees that this + * access mode will remain on a file, such as when it goes through a + * backup and restore. * File creation mode: allow all other applications to have read access * to the created file. - * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, world-readable files created within the - * application's home directory will not be accessible to other applications. - * - * @deprecated Creating world-readable files is very dangerous, and likely - * to cause security holes in applications. It is strongly discouraged; - * instead, applications should use more formal mechanism for interactions - * such as {@link ContentProvider}, {@link BroadcastReceiver}, and - * {@link android.app.Service}. There are no guarantees that this - * access mode will remain on a file, such as when it goes through a - * backup and restore. * @see #MODE_PRIVATE * @see #MODE_WORLD_WRITEABLE */ @Deprecated public static final int MODE_WORLD_READABLE = 0x0001; /** + * @deprecated Creating world-writable files is very dangerous, and likely + * to cause security holes in applications. It is strongly discouraged; + * instead, applications should use more formal mechanism for interactions + * such as {@link ContentProvider}, {@link BroadcastReceiver}, and + * {@link android.app.Service}. There are no guarantees that this + * access mode will remain on a file, such as when it goes through a + * backup and restore. * File creation mode: allow all other applications to have write access * to the created file. - * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, world-writable files created within the - * application's home directory will not be accessible to other applications. - * - * @deprecated Creating world-writable files is very dangerous, and likely - * to cause security holes in applications. It is strongly discouraged; - * instead, applications should use more formal mechanism for interactions - * such as {@link ContentProvider}, {@link BroadcastReceiver}, and - * {@link android.app.Service}. There are no guarantees that this - * access mode will remain on a file, such as when it goes through a - * backup and restore. * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE */ @@ -515,22 +501,18 @@ public abstract class Context { throws FileNotFoundException; /** - * Open a file associated with this Context's application package + * Open a private file associated with this Context's application package * for writing. Creates the file if it doesn't already exist. * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, files created with {@code mode} - * {@link #MODE_WORLD_READABLE} or {@link #MODE_WORLD_WRITEABLE} will not be - * accessible to other applications. - * - * @param name The name of the file to open; can not contain path separators. + * @param name The name of the file to open; can not contain path + * separators. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_APPEND} to append to an existing file, - * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control - * permissions. + * default operation, {@link #MODE_APPEND} to append to an existing file, + * {@link #MODE_WORLD_READABLE} and {@link #MODE_WORLD_WRITEABLE} to control + * permissions. + * * @return FileOutputStream Resulting output stream. + * * @see #MODE_APPEND * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE @@ -756,19 +738,14 @@ public abstract class Context { * application; you can only set the mode of the entire directory, not * of individual files. * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, world-readable / world-writable directories - * created using this method will not be accessible to other applications. - * * @param name Name of the directory to retrieve. This is a directory - * that is created as part of your application data. + * that is created as part of your application data. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the - * default operation, {@link #MODE_WORLD_READABLE} and - * {@link #MODE_WORLD_WRITEABLE} to control permissions. + * default operation, {@link #MODE_WORLD_READABLE} and + * {@link #MODE_WORLD_WRITEABLE} to control permissions. + * * @return Returns a File object for the requested directory. The directory - * will have been created if it does not already exist. + * will have been created if it does not already exist. * * @see #openFileOutput(String, int) */ @@ -778,13 +755,6 @@ public abstract class Context { * Open a new private SQLiteDatabase associated with this Context's * application package. Create the database file if it doesn't exist. * - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, {@code SQLiteDatabase}s created with {@code mode} - * {@link #MODE_WORLD_READABLE} or {@link #MODE_WORLD_WRITEABLE} will not be - * accessible to other applications. - * * @param name The name (unique in the application package) of the database. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} @@ -792,8 +762,10 @@ public abstract class Context { * Use {@link #MODE_ENABLE_WRITE_AHEAD_LOGGING} to enable write-ahead logging by default. * @param factory An optional factory class that is called to instantiate a * cursor when query is called. + * * @return The contents of a newly created database with the given name. * @throws android.database.sqlite.SQLiteException if the database file could not be opened. + * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE @@ -810,13 +782,6 @@ public abstract class Context { *

Accepts input param: a concrete instance of {@link DatabaseErrorHandler} to be * used to handle corruption when sqlite reports database corruption.

* - * Note: Applications with {@code targetSdkVersion} - * {@link android.os.Build.VERSION_CODES#K} or greater have home directories - * with {@code 0700} permissions. Because an application's home directory - * is no longer world-accessible, {@code SQLiteDatabase}s created with {@code mode} - * {@link #MODE_WORLD_READABLE} or {@link #MODE_WORLD_WRITEABLE} will not be - * accessible to other applications. - * * @param name The name (unique in the application package) of the database. * @param mode Operating mode. Use 0 or {@link #MODE_PRIVATE} for the * default operation, {@link #MODE_WORLD_READABLE} @@ -825,9 +790,10 @@ public abstract class Context { * @param factory An optional factory class that is called to instantiate a * cursor when query is called. * @param errorHandler the {@link DatabaseErrorHandler} to be used when sqlite reports database - * corruption. if null, {@link android.database.DefaultDatabaseErrorHandler} is assumed. + * corruption. if null, {@link android.database.DefaultDatabaseErrorHandler} is assumed. * @return The contents of a newly created database with the given name. * @throws android.database.sqlite.SQLiteException if the database file could not be opened. + * * @see #MODE_PRIVATE * @see #MODE_WORLD_READABLE * @see #MODE_WORLD_WRITEABLE diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index d69d2a658e180..a7f39d5ef1ac5 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -431,17 +431,6 @@ public class Build { * */ public static final int JELLY_BEAN_MR1 = 17; - - /** - * Android X.X: "K". Just "K" - * - *

Applications targeting this or a later release will get these - * new changes in behavior:

- *
    - *
  • Application home directory permissions are now {@code 0700}.
  • - *
- */ - public static final int K = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ diff --git a/services/java/com/android/server/pm/Installer.java b/services/java/com/android/server/pm/Installer.java index e96820f266977..02a2c1b0c7110 100644 --- a/services/java/com/android/server/pm/Installer.java +++ b/services/java/com/android/server/pm/Installer.java @@ -188,12 +188,7 @@ public final class Installer { } } - /** - * @param restrictHomeDir if {@code true}, installd will create the application's - * home directory with {@code 0700} permissions. If false, {@code 0751} will - * be used instead. - */ - public int install(String name, int uid, int gid, boolean restrictHomeDir) { + public int install(String name, int uid, int gid) { StringBuilder builder = new StringBuilder("install"); builder.append(' '); builder.append(name); @@ -201,8 +196,6 @@ public final class Installer { builder.append(uid); builder.append(' '); builder.append(gid); - builder.append(' '); - builder.append(restrictHomeDir); return execute(builder.toString()); } @@ -270,12 +263,7 @@ public final class Installer { return execute(builder.toString()); } - /** - * @param restrictHomeDir if {@code true}, installd will create the application's - * home directory with {@code 0700} permissions. If false, {@code 0751} will - * be used instead. - */ - public int createUserData(String name, int uid, int userId, boolean restrictHomeDir) { + public int createUserData(String name, int uid, int userId) { StringBuilder builder = new StringBuilder("mkuserdata"); builder.append(' '); builder.append(name); @@ -283,8 +271,6 @@ public final class Installer { builder.append(uid); builder.append(' '); builder.append(userId); - builder.append(' '); - builder.append(restrictHomeDir); return execute(builder.toString()); } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index aa2b461216f7e..2238f17621f1c 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -24,6 +24,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED; import static com.android.internal.util.ArrayUtils.appendInt; import static com.android.internal.util.ArrayUtils.removeInt; +import static libcore.io.OsConstants.S_ISLNK; import static libcore.io.OsConstants.S_IRWXU; import static libcore.io.OsConstants.S_IRGRP; import static libcore.io.OsConstants.S_IXGRP; @@ -110,6 +111,7 @@ import android.os.SystemClock; import android.os.SystemProperties; import android.os.UserHandle; import android.os.Environment.UserEnvironment; +import android.provider.Settings.Secure; import android.security.SystemKeyStore; import android.util.DisplayMetrics; import android.util.EventLog; @@ -146,11 +148,13 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import libcore.io.ErrnoException; import libcore.io.IoUtils; import libcore.io.Libcore; +import libcore.io.OsConstants; import libcore.io.StructStat; /** @@ -3578,17 +3582,16 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private int createDataDirsLI(String packageName, int uid, int targetSdkVersion) { + private int createDataDirsLI(String packageName, int uid) { int[] users = sUserManager.getUserIds(); - boolean restrictHomeDir = (targetSdkVersion >= Build.VERSION_CODES.K); - int res = mInstaller.install(packageName, uid, uid, restrictHomeDir); + int res = mInstaller.install(packageName, uid, uid); if (res < 0) { return res; } for (int user : users) { if (user != 0) { res = mInstaller.createUserData(packageName, - UserHandle.getUid(user, uid), user, restrictHomeDir); + UserHandle.getUid(user, uid), user); if (res < 0) { return res; } @@ -3982,8 +3985,7 @@ public class PackageManagerService extends IPackageManager.Stub { recovered = true; // And now re-install the app. - ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.targetSdkVersion); + ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret == -1) { // Ack should not happen! msg = prefix + pkg.packageName @@ -4028,9 +4030,8 @@ public class PackageManagerService extends IPackageManager.Stub { if ((parseFlags & PackageParser.PARSE_CHATTY) != 0) Log.v(TAG, "Want this data dir: " + dataPath); } - // invoke installer to do the actual installation - int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid, - pkg.applicationInfo.targetSdkVersion); + //invoke installer to do the actual installation + int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid); if (ret < 0) { // Error from installer mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE; diff --git a/services/java/com/android/server/pm/Settings.java b/services/java/com/android/server/pm/Settings.java index f791a6e6e148d..06f11bc9a4dab 100644 --- a/services/java/com/android/server/pm/Settings.java +++ b/services/java/com/android/server/pm/Settings.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; +import com.android.server.IntentResolver; import com.android.server.pm.PackageManagerService.DumpState; import org.xmlpull.v1.XmlPullParser; @@ -45,7 +46,6 @@ import android.content.pm.UserInfo; import android.content.pm.PackageUserState; import android.content.pm.VerifierDeviceIdentity; import android.os.Binder; -import android.os.Build; import android.os.Environment; import android.os.FileUtils; import android.os.Process; @@ -2335,11 +2335,9 @@ final class Settings { for (PackageSetting ps : mPackages.values()) { // Only system apps are initially installed. ps.setInstalled((ps.pkgFlags&ApplicationInfo.FLAG_SYSTEM) != 0, userHandle); - boolean restrictHomeDir = (ps.pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.K); // Need to create a data directory for all apps under this user. installer.createUserData(ps.name, - UserHandle.getUid(userHandle, ps.appId), userHandle, - restrictHomeDir); + UserHandle.getUid(userHandle, ps.appId), userHandle); } readDefaultPreferredAppsLPw(userHandle); writePackageRestrictionsLPr(userHandle); From f623b930d3661540f74370d3548ac0a056c3320b Mon Sep 17 00:00:00 2001 From: Nick Kralevich Date: Wed, 9 Jan 2013 16:03:14 -0800 Subject: [PATCH 4/6] Add K api version. Change-Id: I2b74e06f8bda8b23c5cc4280af4b18a5ad66a6c2 --- api/current.txt | 1 + core/java/android/os/Build.java | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/api/current.txt b/api/current.txt index cf24f1347e51c..e4d5fd1f188c1 100644 --- a/api/current.txt +++ b/api/current.txt @@ -15737,6 +15737,7 @@ package android.os { field public static final int ICE_CREAM_SANDWICH_MR1 = 15; // 0xf field public static final int JELLY_BEAN = 16; // 0x10 field public static final int JELLY_BEAN_MR1 = 17; // 0x11 + field public static final int K = 10000; // 0x2710 } public final class Bundle implements java.lang.Cloneable android.os.Parcelable { diff --git a/core/java/android/os/Build.java b/core/java/android/os/Build.java index a7f39d5ef1ac5..52c89e868cf04 100644 --- a/core/java/android/os/Build.java +++ b/core/java/android/os/Build.java @@ -431,6 +431,11 @@ public class Build { * */ public static final int JELLY_BEAN_MR1 = 17; + + /** + * Android X.X: "K". Just "K" + */ + public static final int K = CUR_DEVELOPMENT; } /** The type of build, like "user" or "eng". */ From 1efbb80d5e4ccc44419f823fb7bd3bd0e25b332d Mon Sep 17 00:00:00 2001 From: Laurent Tu Date: Thu, 24 Jan 2013 15:10:24 -0800 Subject: [PATCH 5/6] Fix GpsLocationProvider.enable/disable/isEnabled Fix the issue where GpsLocationProvider.isEnabled() returns true when it is really false (and the other way around), when the handler hasn't processed the enable/disable messages yet. This can be systematically reproduced when the caller code is using the same thread as the thread of the handler in GpsLocationProvider. For example, this was happening in LocationManagerService.switchUser(). It would start by disabling all the providers (with updateProviderListenersLocked()), then re-enable them in updateProvidersLocked() only when isEnalbed()==false, which was in the wrong state since the GpsLocationProvider.ENABLE message hadn't been processed yet. As a result, the GpsLocationProvider was disabled upon startup of the phone. This is a slight problem for the enable() contract, which specifies that getStatus() must be handled, getStatus() will be handled but might have slighty not-up-to-date info in this case. Bug: 8028017 Change-Id: Iff91a11cc150e9029a6db85b64a10a926e12b0ba --- .../server/location/GpsLocationProvider.java | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java index 12c14bfa626c4..da150d9e6ca64 100644 --- a/services/java/com/android/server/location/GpsLocationProvider.java +++ b/services/java/com/android/server/location/GpsLocationProvider.java @@ -704,17 +704,17 @@ public class GpsLocationProvider implements LocationProviderInterface { */ @Override public void enable() { + synchronized (mLock) { + if (mEnabled) return; + mEnabled = true; + } + sendMessage(ENABLE, 1, null); } private void handleEnable() { if (DEBUG) Log.d(TAG, "handleEnable"); - synchronized (mLock) { - if (mEnabled) return; - mEnabled = true; - } - boolean enabled = native_init(); if (enabled) { @@ -740,17 +740,17 @@ public class GpsLocationProvider implements LocationProviderInterface { */ @Override public void disable() { + synchronized (mLock) { + if (!mEnabled) return; + mEnabled = false; + } + sendMessage(ENABLE, 0, null); } private void handleDisable() { if (DEBUG) Log.d(TAG, "handleDisable"); - synchronized (mLock) { - if (!mEnabled) return; - mEnabled = false; - } - stopNavigating(); mAlarmManager.cancel(mWakeupIntent); mAlarmManager.cancel(mTimeoutIntent); From 8c5eb4a3d94241665663bc2b2aaaa8a15e3bafbd Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 30 Jan 2013 10:58:40 -0800 Subject: [PATCH 6/6] Check text length when testing for newline. Also add tests to verify. Bug: 8102140 Change-Id: I7e5dbff53caeb50bfa0fb4ea5dce73e3c742986a --- .../internal/util/FastXmlSerializer.java | 2 +- .../internal/util/FastXmlSerializerTest.java | 47 +++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java diff --git a/core/java/com/android/internal/util/FastXmlSerializer.java b/core/java/com/android/internal/util/FastXmlSerializer.java index 99eea151879fd..7a040801a9a50 100644 --- a/core/java/com/android/internal/util/FastXmlSerializer.java +++ b/core/java/com/android/internal/util/FastXmlSerializer.java @@ -390,7 +390,7 @@ public class FastXmlSerializer implements XmlSerializer { } escapeAndAppendString(text); if (mIndent) { - mLineStart = text.charAt(text.length()-1) == '\n'; + mLineStart = text.length() > 0 && (text.charAt(text.length()-1) == '\n'); } return this; } diff --git a/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java new file mode 100644 index 0000000000000..be7116dffb899 --- /dev/null +++ b/core/tests/coretests/src/com/android/internal/util/FastXmlSerializerTest.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.internal.util; + +import junit.framework.TestCase; + +import org.xmlpull.v1.XmlSerializer; + +import java.io.ByteArrayOutputStream; + +/** + * Tests for {@link FastXmlSerializer} + */ +public class FastXmlSerializerTest extends TestCase { + public void testEmptyText() throws Exception { + final ByteArrayOutputStream stream = new ByteArrayOutputStream(); + + final XmlSerializer out = new FastXmlSerializer(); + out.setOutput(stream, "utf-8"); + out.startDocument(null, true); + out.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); + + out.startTag(null, "string"); + out.attribute(null, "name", "meow"); + out.text(""); + out.endTag(null, "string"); + + out.endDocument(); + + assertEquals("\n" + + "", stream.toString()); + } +}