summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/app/SearchDialog.java4
-rw-r--r--core/java/android/backup/BackupDataOutput.java1
-rw-r--r--core/java/android/backup/RestoreHelper.java3
-rw-r--r--core/java/android/backup/RestoreHelperBase.java71
-rw-r--r--core/java/android/backup/RestoreHelperDispatcher.java16
-rw-r--r--core/java/android/content/res/Resources.java31
-rwxr-xr-xcore/java/android/speech/tts/TextToSpeech.java22
-rw-r--r--core/java/android/test/InstrumentationTestCase.java24
-rw-r--r--core/java/android/text/format/DateUtils.java6
-rw-r--r--core/java/android/util/DisplayMetrics.java12
-rw-r--r--core/java/android/util/LongSparseArray.java342
-rw-r--r--core/java/com/android/internal/backup/LocalTransport.java44
12 files changed, 495 insertions, 81 deletions
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java
index b6c8385..3a3a983 100644
--- a/core/java/android/app/SearchDialog.java
+++ b/core/java/android/app/SearchDialog.java
@@ -134,9 +134,7 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS
private SuggestionsAdapter mSuggestionsAdapter;
// Whether to rewrite queries when selecting suggestions
- // TODO: This is disabled because of problems with persistent selections
- // causing non-user-initiated rewrites.
- private static final boolean REWRITE_QUERIES = false;
+ private static final boolean REWRITE_QUERIES = true;
// The query entered by the user. This is not changed when selecting a suggestion
// that modifies the contents of the text field. But if the user then edits
diff --git a/core/java/android/backup/BackupDataOutput.java b/core/java/android/backup/BackupDataOutput.java
index a6d5bec..05e667e 100644
--- a/core/java/android/backup/BackupDataOutput.java
+++ b/core/java/android/backup/BackupDataOutput.java
@@ -36,6 +36,7 @@ public class BackupDataOutput {
}
}
+ // A dataSize of -1 indicates that the record under this key should be deleted
public int writeEntityHeader(String key, int dataSize) throws IOException {
int result = writeEntityHeader_native(mBackupWriter, key, dataSize);
if (result >= 0) {
diff --git a/core/java/android/backup/RestoreHelper.java b/core/java/android/backup/RestoreHelper.java
index ac7d8ee..e47869c 100644
--- a/core/java/android/backup/RestoreHelper.java
+++ b/core/java/android/backup/RestoreHelper.java
@@ -16,6 +16,8 @@
package android.backup;
+import android.os.ParcelFileDescriptor;
+
import java.io.InputStream;
/** @hide */
@@ -27,5 +29,6 @@ public interface RestoreHelper {
* <code>dataSize</code> bytes from <code>data</code>.
*/
public void restoreEntity(BackupDataInputStream data);
+ public void writeSnapshot(ParcelFileDescriptor fd);
}
diff --git a/core/java/android/backup/RestoreHelperBase.java b/core/java/android/backup/RestoreHelperBase.java
index 894c9af..93a8fef 100644
--- a/core/java/android/backup/RestoreHelperBase.java
+++ b/core/java/android/backup/RestoreHelperBase.java
@@ -17,69 +17,66 @@
package android.backup;
import android.content.Context;
+import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.InputStream;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileOutputStream;
-import java.io.IOException;
class RestoreHelperBase {
private static final String TAG = "RestoreHelperBase";
- private static final int BUF_SIZE = 8 * 1024;
+ int mPtr;
Context mContext;
- byte[] mBuf = new byte[BUF_SIZE];
boolean mExceptionLogged;
RestoreHelperBase(Context context) {
+ mPtr = ctor();
mContext = context;
}
- void writeFile(File f, InputStream in) {
- boolean success = false;
- FileOutputStream out = null;
+ protected void finalize() throws Throwable {
try {
- // Create the enclosing directory.
- File parent = f.getParentFile();
- parent.mkdirs();
+ dtor(mPtr);
+ } finally {
+ super.finalize();
+ }
+ }
- // Copy the file.
- int sum = 0;
- out = new FileOutputStream(f);
- byte[] buf = mBuf;
- int amt;
- while ((amt = in.read(buf)) > 0) {
- out.write(buf, 0, amt);
- sum += amt;
- }
+ void writeFile(File f, InputStream in) {
+ if (!(in instanceof BackupDataInputStream)) {
+ throw new IllegalStateException("input stream must be a BackupDataInputStream");
+ }
+ int result = -1;
- // TODO: Set the permissions of the file.
+ // Create the enclosing directory.
+ File parent = f.getParentFile();
+ parent.mkdirs();
- // We're done
- success = true;
- out = null;
- } catch (IOException ex) {
- // Bail on this entity. Only log one exception per helper object.
+ result = writeFile_native(mPtr, f.getAbsolutePath(),
+ ((BackupDataInputStream)in).mData.mBackupReader);
+ if (result != 0) {
+ // Bail on this entity. Only log one failure per helper object.
if (!mExceptionLogged) {
Log.e(TAG, "Failed restoring file '" + f + "' for app '"
- + mContext.getPackageName() + '\'', ex);
+ + mContext.getPackageName() + "\' result=0x"
+ + Integer.toHexString(result));
mExceptionLogged = true;
}
}
- finally {
- if (out != null) {
- try {
- out.close();
- } catch (IOException ex) {
- }
- }
- if (!success) {
- // Something didn't work out, delete the file
- f.delete();
- }
- }
}
+
+ public void writeSnapshot(ParcelFileDescriptor fd) {
+ int result = writeSnapshot_native(mPtr, fd.getFileDescriptor());
+ // TODO: Do something with the error.
+ }
+
+ private static native int ctor();
+ private static native void dtor(int ptr);
+ private static native int writeFile_native(int ptr, String filename, int backupReader);
+ private static native int writeSnapshot_native(int ptr, FileDescriptor fd);
}
diff --git a/core/java/android/backup/RestoreHelperDispatcher.java b/core/java/android/backup/RestoreHelperDispatcher.java
index 5928914..4861775 100644
--- a/core/java/android/backup/RestoreHelperDispatcher.java
+++ b/core/java/android/backup/RestoreHelperDispatcher.java
@@ -16,10 +16,12 @@
package android.backup;
+import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.IOException;
import java.util.HashMap;
+import java.util.Map;
/** @hide */
public class RestoreHelperDispatcher {
@@ -31,7 +33,7 @@ public class RestoreHelperDispatcher {
mHelpers.put(keyPrefix, helper);
}
- public void dispatch(BackupDataInput input) throws IOException {
+ public void dispatch(BackupDataInput input, ParcelFileDescriptor newState) throws IOException {
boolean alreadyComplained = false;
BackupDataInputStream stream = new BackupDataInputStream(input);
@@ -60,5 +62,17 @@ public class RestoreHelperDispatcher {
}
input.skipEntityData(); // In case they didn't consume the data.
}
+
+ if (mHelpers.size() > 1) {
+ throw new RuntimeException("RestoreHelperDispatcher won't get your your"
+ + " data in the right order yet.");
+ }
+
+ // Write out the state files
+ for (RestoreHelper helper: mHelpers.values()) {
+ // TODO: Write a header for the state
+ helper.writeSnapshot(newState);
+ }
}
}
+
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
index 2f63820..71dbd38 100644
--- a/core/java/android/content/res/Resources.java
+++ b/core/java/android/content/res/Resources.java
@@ -22,11 +22,9 @@ import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
-import android.app.ActivityThread.PackageInfo;
import android.content.pm.ApplicationInfo;
import android.graphics.Movie;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import android.os.SystemProperties;
@@ -35,6 +33,7 @@ import android.util.DisplayMetrics;
import android.util.Log;
import android.util.SparseArray;
import android.util.TypedValue;
+import android.util.LongSparseArray;
import java.io.IOException;
import java.io.InputStream;
@@ -59,19 +58,19 @@ public class Resources {
// Information about preloaded resources. Note that they are not
// protected by a lock, because while preloading in zygote we are all
// single-threaded, and after that these are immutable.
- private static final SparseArray<Drawable.ConstantState> sPreloadedDrawables
- = new SparseArray<Drawable.ConstantState>();
+ private static final LongSparseArray<Drawable.ConstantState> sPreloadedDrawables
+ = new LongSparseArray<Drawable.ConstantState>();
private static final SparseArray<ColorStateList> mPreloadedColorStateLists
= new SparseArray<ColorStateList>();
private static boolean mPreloaded;
- private final SparseArray<Drawable.ConstantState> mPreloadedDrawables;
+ private final LongSparseArray<Drawable.ConstantState> mPreloadedDrawables;
/*package*/ final TypedValue mTmpValue = new TypedValue();
// These are protected by the mTmpValue lock.
- private final SparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
- = new SparseArray<WeakReference<Drawable.ConstantState> >();
+ private final LongSparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
+ = new LongSparseArray<WeakReference<Drawable.ConstantState> >();
private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache
= new SparseArray<WeakReference<ColorStateList> >();
private boolean mPreloading;
@@ -89,20 +88,20 @@ public class Resources {
private final CompatibilityInfo mCompatibilityInfo;
- private static final SparseArray<Object> EMPTY_ARRAY = new SparseArray<Object>() {
+ private static final LongSparseArray<Object> EMPTY_ARRAY = new LongSparseArray<Object>() {
@Override
- public void put(int k, Object o) {
+ public void put(long k, Object o) {
throw new UnsupportedOperationException();
}
@Override
- public void append(int k, Object o) {
+ public void append(long k, Object o) {
throw new UnsupportedOperationException();
}
};
@SuppressWarnings("unchecked")
- private static <T> SparseArray<T> emptySparseArray() {
- return (SparseArray<T>) EMPTY_ARRAY;
+ private static <T> LongSparseArray<T> emptySparseArray() {
+ return (LongSparseArray<T>) EMPTY_ARRAY;
}
/**
@@ -1315,14 +1314,14 @@ public class Resources {
configChanges, cs.getChangingConfigurations())) {
if (DEBUG_CONFIG) {
Log.d(TAG, "FLUSHING #0x"
- + Integer.toHexString(mDrawableCache.keyAt(i))
+ + Long.toHexString(mDrawableCache.keyAt(i))
+ " / " + cs + " with changes: 0x"
+ Integer.toHexString(cs.getChangingConfigurations()));
}
mDrawableCache.setValueAt(i, null);
} else if (DEBUG_CONFIG) {
Log.d(TAG, "(Keeping #0x"
- + Integer.toHexString(mDrawableCache.keyAt(i))
+ + Long.toHexString(mDrawableCache.keyAt(i))
+ " / " + cs + " with changes: 0x"
+ Integer.toHexString(cs.getChangingConfigurations())
+ ")");
@@ -1653,7 +1652,7 @@ public class Resources {
}
}
- final int key = (value.assetCookie << 24) | value.data;
+ final long key = (((long) value.assetCookie) << 32) | value.data;
Drawable dr = getCachedDrawable(key);
if (dr != null) {
@@ -1733,7 +1732,7 @@ public class Resources {
return dr;
}
- private Drawable getCachedDrawable(int key) {
+ private Drawable getCachedDrawable(long key) {
synchronized (mTmpValue) {
WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key);
if (wr != null) { // we have the key
diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java
index 8f58194..9fc143d 100755
--- a/core/java/android/speech/tts/TextToSpeech.java
+++ b/core/java/android/speech/tts/TextToSpeech.java
@@ -81,6 +81,28 @@ public class TextToSpeech {
}
/**
+ * Internal constants for the TTS functionality
+ *
+ * {@hide}
+ */
+ public class Engine {
+ // default values for a TTS engine when settings are not found in the provider
+ public static final int FALLBACK_TTS_DEFAULT_RATE = 100; // 1x
+ public static final int FALLBACK_TTS_DEFAULT_PITCH = 100;// 1x
+ public static final int FALLBACK_TTS_USE_DEFAULTS = 0; // false
+ public static final String FALLBACK_TTS_DEFAULT_LANG = "eng";
+ public static final String FALLBACK_TTS_DEFAULT_COUNTRY = "";
+ public static final String FALLBACK_TTS_DEFAULT_VARIANT = "";
+
+ // return codes for a TTS engine's check data activity
+ public static final int CHECK_VOICE_DATA_PASS = 1;
+ public static final int CHECK_VOICE_DATA_FAIL = 0;
+ public static final int CHECK_VOICE_DATA_BAD_DATA = -1;
+ public static final int CHECK_VOICE_DATA_MISSING_DATA = -2;
+ public static final int CHECK_VOICE_DATA_MISSING_DATA_NO_SDCARD = -3;
+ }
+
+ /**
* Connection needed for the TTS.
*/
private ServiceConnection mServiceConnection;
diff --git a/core/java/android/test/InstrumentationTestCase.java b/core/java/android/test/InstrumentationTestCase.java
index 470ab0d..2145d7c 100644
--- a/core/java/android/test/InstrumentationTestCase.java
+++ b/core/java/android/test/InstrumentationTestCase.java
@@ -241,7 +241,13 @@ public class InstrumentationTestCase extends TestCase {
try {
final Field keyCodeField = KeyEvent.class.getField("KEYCODE_" + key);
final int keyCode = keyCodeField.getInt(null);
- instrumentation.sendKeyDownUpSync(keyCode);
+ try {
+ instrumentation.sendKeyDownUpSync(keyCode);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
} catch (NoSuchFieldException e) {
Log.w("ActivityTestCase", "Unknown keycode: KEYCODE_" + key);
break;
@@ -266,7 +272,13 @@ public class InstrumentationTestCase extends TestCase {
final Instrumentation instrumentation = getInstrumentation();
for (int i = 0; i < count; i++) {
- instrumentation.sendKeyDownUpSync(keys[i]);
+ try {
+ instrumentation.sendKeyDownUpSync(keys[i]);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
}
instrumentation.waitForIdleSync();
@@ -292,7 +304,13 @@ public class InstrumentationTestCase extends TestCase {
final int keyCount = keys[i];
final int keyCode = keys[i + 1];
for (int j = 0; j < keyCount; j++) {
- instrumentation.sendKeyDownUpSync(keyCode);
+ try {
+ instrumentation.sendKeyDownUpSync(keyCode);
+ } catch (SecurityException e) {
+ // Ignore security exceptions that are now thrown
+ // when trying to send to another app, to retain
+ // compatibility with existing tests.
+ }
}
}
diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java
index bccb3a6..1a4eb69 100644
--- a/core/java/android/text/format/DateUtils.java
+++ b/core/java/android/text/format/DateUtils.java
@@ -184,6 +184,9 @@ public class DateUtils
*/
public static final String HOUR_MINUTE_24 = "%H:%M";
public static final String MONTH_FORMAT = "%B";
+ /**
+ * This is not actually a useful month name in all locales.
+ */
public static final String ABBREV_MONTH_FORMAT = "%b";
public static final String NUMERIC_MONTH_FORMAT = "%m";
public static final String MONTH_DAY_FORMAT = "%-d";
@@ -1444,7 +1447,8 @@ public class DateUtils
if (numericDate) {
monthFormat = NUMERIC_MONTH_FORMAT;
} else if (abbrevMonth) {
- monthFormat = ABBREV_MONTH_FORMAT;
+ monthFormat =
+ res.getString(com.android.internal.R.string.short_format_month);
} else {
monthFormat = MONTH_FORMAT;
}
diff --git a/core/java/android/util/DisplayMetrics.java b/core/java/android/util/DisplayMetrics.java
index 245148d..a095913 100644
--- a/core/java/android/util/DisplayMetrics.java
+++ b/core/java/android/util/DisplayMetrics.java
@@ -37,8 +37,7 @@ public class DisplayMetrics {
* The device's density.
* @hide
*/
- public static final int DEVICE_DENSITY = SystemProperties.getInt("ro.sf.lcd_density",
- DEFAULT_DENSITY);
+ public static final int DEVICE_DENSITY = getDeviceDensity();
/**
* The absolute width of the display in pixels.
@@ -161,4 +160,13 @@ public class DisplayMetrics {
", height=" + heightPixels + ", scaledDensity=" + scaledDensity +
", xdpi=" + xdpi + ", ydpi=" + ydpi + "}";
}
+
+ private static int getDeviceDensity() {
+ // qemu.sf.lcd_density can be used to override ro.sf.lcd_density
+ // when running in the emulator, allowing for dynamic configurations.
+ // The reason for this is that ro.sf.lcd_density is write-once and is
+ // set by the init process when it parses build.prop before anything else.
+ return SystemProperties.getInt("qemu.sf.lcd_density",
+ SystemProperties.getInt("ro.sf.lcd_density", DEFAULT_DENSITY));
+ }
}
diff --git a/core/java/android/util/LongSparseArray.java b/core/java/android/util/LongSparseArray.java
new file mode 100644
index 0000000..d90045f
--- /dev/null
+++ b/core/java/android/util/LongSparseArray.java
@@ -0,0 +1,342 @@
+/*
+ * Copyright (C) 2009 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 android.util;
+
+import com.android.internal.util.ArrayUtils;
+
+/**
+ * SparseArrays map longs to Objects. Unlike a normal array of Objects,
+ * there can be gaps in the indices. It is intended to be more efficient
+ * than using a HashMap to map Longs to Objects.
+ *
+ * @hide
+ */
+public class LongSparseArray<E> {
+ private static final Object DELETED = new Object();
+ private boolean mGarbage = false;
+
+ /**
+ * Creates a new SparseArray containing no mappings.
+ */
+ public LongSparseArray() {
+ this(10);
+ }
+
+ /**
+ * Creates a new SparseArray containing no mappings that will not
+ * require any additional memory allocation to store the specified
+ * number of mappings.
+ */
+ public LongSparseArray(int initialCapacity) {
+ initialCapacity = ArrayUtils.idealIntArraySize(initialCapacity);
+
+ mKeys = new long[initialCapacity];
+ mValues = new Object[initialCapacity];
+ mSize = 0;
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or <code>null</code>
+ * if no such mapping has been made.
+ */
+ public E get(long key) {
+ return get(key, null);
+ }
+
+ /**
+ * Gets the Object mapped from the specified key, or the specified Object
+ * if no such mapping has been made.
+ */
+ public E get(long key, E valueIfKeyNotFound) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i < 0 || mValues[i] == DELETED) {
+ return valueIfKeyNotFound;
+ } else {
+ return (E) mValues[i];
+ }
+ }
+
+ /**
+ * Removes the mapping from the specified key, if there was any.
+ */
+ public void delete(long key) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ if (mValues[i] != DELETED) {
+ mValues[i] = DELETED;
+ mGarbage = true;
+ }
+ }
+ }
+
+ /**
+ * Alias for {@link #delete(long)}.
+ */
+ public void remove(long key) {
+ delete(key);
+ }
+
+ private void gc() {
+ // Log.e("SparseArray", "gc start with " + mSize);
+
+ int n = mSize;
+ int o = 0;
+ long[] keys = mKeys;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ Object val = values[i];
+
+ if (val != DELETED) {
+ if (i != o) {
+ keys[o] = keys[i];
+ values[o] = val;
+ }
+
+ o++;
+ }
+ }
+
+ mGarbage = false;
+ mSize = o;
+
+ // Log.e("SparseArray", "gc end with " + mSize);
+ }
+
+ /**
+ * Adds a mapping from the specified key to the specified value,
+ * replacing the previous mapping from the specified key if there
+ * was one.
+ */
+ public void put(long key, E value) {
+ int i = binarySearch(mKeys, 0, mSize, key);
+
+ if (i >= 0) {
+ mValues[i] = value;
+ } else {
+ i = ~i;
+
+ if (i < mSize && mValues[i] == DELETED) {
+ mKeys[i] = key;
+ mValues[i] = value;
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+
+ // Search again because indices may have changed.
+ i = ~binarySearch(mKeys, 0, mSize, key);
+ }
+
+ if (mSize >= mKeys.length) {
+ int n = ArrayUtils.idealIntArraySize(mSize + 1);
+
+ long[] nkeys = new long[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ if (mSize - i != 0) {
+ // Log.e("SparseArray", "move " + (mSize - i));
+ System.arraycopy(mKeys, i, mKeys, i + 1, mSize - i);
+ System.arraycopy(mValues, i, mValues, i + 1, mSize - i);
+ }
+
+ mKeys[i] = key;
+ mValues[i] = value;
+ mSize++;
+ }
+ }
+
+ /**
+ * Returns the number of key-value mappings that this SparseArray
+ * currently stores.
+ */
+ public int size() {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mSize;
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the key from the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public long keyAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return mKeys[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, returns
+ * the value from the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public E valueAt(int index) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return (E) mValues[index];
+ }
+
+ /**
+ * Given an index in the range <code>0...size()-1</code>, sets a new
+ * value for the <code>index</code>th key-value mapping that this
+ * SparseArray stores.
+ */
+ public void setValueAt(int index, E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ mValues[index] = value;
+ }
+
+ /**
+ * Returns the index for which {@link #keyAt} would return the
+ * specified key, or a negative number if the specified
+ * key is not mapped.
+ */
+ public int indexOfKey(long key) {
+ if (mGarbage) {
+ gc();
+ }
+
+ return binarySearch(mKeys, 0, mSize, key);
+ }
+
+ /**
+ * Returns an index for which {@link #valueAt} would return the
+ * specified key, or a negative number if no keys map to the
+ * specified value.
+ * Beware that this is a linear search, unlike lookups by key,
+ * and that multiple keys can map to the same value and this will
+ * find only one of them.
+ */
+ public int indexOfValue(E value) {
+ if (mGarbage) {
+ gc();
+ }
+
+ for (int i = 0; i < mSize; i++)
+ if (mValues[i] == value)
+ return i;
+
+ return -1;
+ }
+
+ /**
+ * Removes all key-value mappings from this SparseArray.
+ */
+ public void clear() {
+ int n = mSize;
+ Object[] values = mValues;
+
+ for (int i = 0; i < n; i++) {
+ values[i] = null;
+ }
+
+ mSize = 0;
+ mGarbage = false;
+ }
+
+ /**
+ * Puts a key/value pair into the array, optimizing for the case where
+ * the key is greater than all existing keys in the array.
+ */
+ public void append(long key, E value) {
+ if (mSize != 0 && key <= mKeys[mSize - 1]) {
+ put(key, value);
+ return;
+ }
+
+ if (mGarbage && mSize >= mKeys.length) {
+ gc();
+ }
+
+ int pos = mSize;
+ if (pos >= mKeys.length) {
+ int n = ArrayUtils.idealIntArraySize(pos + 1);
+
+ long[] nkeys = new long[n];
+ Object[] nvalues = new Object[n];
+
+ // Log.e("SparseArray", "grow " + mKeys.length + " to " + n);
+ System.arraycopy(mKeys, 0, nkeys, 0, mKeys.length);
+ System.arraycopy(mValues, 0, nvalues, 0, mValues.length);
+
+ mKeys = nkeys;
+ mValues = nvalues;
+ }
+
+ mKeys[pos] = key;
+ mValues[pos] = value;
+ mSize = pos + 1;
+ }
+
+ private static int binarySearch(long[] a, int start, int len, long key) {
+ int high = start + len, low = start - 1, guess;
+
+ while (high - low > 1) {
+ guess = (high + low) / 2;
+
+ if (a[guess] < key)
+ low = guess;
+ else
+ high = guess;
+ }
+
+ if (high == start + len)
+ return ~(start + len);
+ else if (a[high] == key)
+ return high;
+ else
+ return ~high;
+ }
+
+ private void checkIntegrity() {
+ for (int i = 1; i < mSize; i++) {
+ if (mKeys[i] <= mKeys[i - 1]) {
+ for (int j = 0; j < mSize; j++) {
+ Log.e("FAIL", j + ": " + mKeys[j] + " -> " + mValues[j]);
+ }
+
+ throw new RuntimeException();
+ }
+ }
+ }
+
+ private long[] mKeys;
+ private Object[] mValues;
+ private int mSize;
+} \ No newline at end of file
diff --git a/core/java/com/android/internal/backup/LocalTransport.java b/core/java/com/android/internal/backup/LocalTransport.java
index 5caa015..3ef8666 100644
--- a/core/java/com/android/internal/backup/LocalTransport.java
+++ b/core/java/com/android/internal/backup/LocalTransport.java
@@ -80,28 +80,35 @@ public class LocalTransport extends IBackupTransport.Stub {
byte[] buf = new byte[bufSize];
while (changeSet.readNextHeader()) {
String key = changeSet.getKey();
+ String base64Key = new String(Base64.encode(key.getBytes()));
+ File entityFile = new File(packageDir, base64Key);
+
int dataSize = changeSet.getDataSize();
- String base64Key = new String(Base64.encode(key.getBytes()));
if (DEBUG) Log.v(TAG, "Got change set key=" + key + " size=" + dataSize
+ " key64=" + base64Key);
- if (dataSize > bufSize) {
- bufSize = dataSize;
- buf = new byte[bufSize];
- }
- changeSet.readEntityData(buf, 0, dataSize);
- if (DEBUG) Log.v(TAG, " + data size " + dataSize);
- File entityFile = new File(packageDir, base64Key);
- FileOutputStream entity = new FileOutputStream(entityFile);
- try {
- entity.write(buf, 0, dataSize);
- } catch (IOException e) {
- Log.e(TAG, "Unable to update key file "
- + entityFile.getAbsolutePath());
- err = -1;
- } finally {
- entity.close();
+ if (dataSize >= 0) {
+ FileOutputStream entity = new FileOutputStream(entityFile);
+
+ if (dataSize > bufSize) {
+ bufSize = dataSize;
+ buf = new byte[bufSize];
+ }
+ changeSet.readEntityData(buf, 0, dataSize);
+ if (DEBUG) Log.v(TAG, " data size " + dataSize);
+
+ try {
+ entity.write(buf, 0, dataSize);
+ } catch (IOException e) {
+ Log.e(TAG, "Unable to update key file "
+ + entityFile.getAbsolutePath());
+ err = -1;
+ } finally {
+ entity.close();
+ }
+ } else {
+ entityFile.delete();
}
}
} catch (IOException e) {
@@ -172,7 +179,8 @@ public class LocalTransport extends IBackupTransport.Stub {
int size = (int) f.length();
byte[] buf = new byte[size];
in.read(buf);
- out.writeEntityHeader(f.getName(), size);
+ String key = new String(Base64.decode(f.getName()));
+ out.writeEntityHeader(key, size);
out.writeEntityData(buf, size);
}
} catch (Exception e) {