Merge "DropBoxManagerService: Don't store redundant information" into oc-mr1-dev

This commit is contained in:
TreeHugger Robot
2017-08-10 00:18:56 +00:00
committed by Android (Google) Code Review
4 changed files with 468 additions and 96 deletions

View File

@@ -28,4 +28,12 @@ public class ObjectUtils {
public static <T> T firstNotNull(@Nullable T a, @NonNull T b) {
return a != null ? a : Preconditions.checkNotNull(b);
}
public static <T extends Comparable> int compare(@Nullable T a, @Nullable T b) {
if (a != null) {
return (b != null) ? a.compareTo(b) : 1;
} else {
return (b != null) ? -1 : 0;
}
}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright (C) 2017 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 android.test.AndroidTestCase;
import android.test.suitebuilder.annotation.SmallTest;
@SmallTest
public class ObjectUtilsTest extends AndroidTestCase {
public void testCompare() {
assertEquals(0, ObjectUtils.compare(null, null));
assertEquals(1, ObjectUtils.compare("a", null));
assertEquals(-1, ObjectUtils.compare(null, "a"));
assertEquals(0, ObjectUtils.compare("a", "a"));
assertEquals(-1, ObjectUtils.compare("a", "b"));
assertEquals(1, ObjectUtils.compare("b", "a"));
}
}

View File

@@ -29,18 +29,23 @@ import android.os.Debug;
import android.os.DropBoxManager;
import android.os.FileUtils;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.StatFs;
import android.os.SystemClock;
import android.os.UserHandle;
import android.provider.Settings;
import android.text.TextUtils;
import android.text.format.Time;
import android.util.ArrayMap;
import android.util.Slog;
import libcore.io.IoUtils;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.os.IDropBoxManagerService;
import com.android.internal.util.DumpUtils;
import com.android.internal.util.ObjectUtils;
import java.io.BufferedOutputStream;
import java.io.File;
@@ -52,7 +57,7 @@ import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Objects;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.zip.GZIPOutputStream;
@@ -87,7 +92,7 @@ public final class DropBoxManagerService extends SystemService {
// Accounting of all currently written log files (set in init()).
private FileList mAllFiles = null;
private HashMap<String, FileList> mFilesByTag = null;
private ArrayMap<String, FileList> mFilesByTag = null;
// Various bits of disk information
@@ -153,7 +158,7 @@ public final class DropBoxManagerService extends SystemService {
* @param context to use for receiving free space & gservices intents
*/
public DropBoxManagerService(final Context context) {
this(context, new File("/data/system/dropbox"));
this(context, new File("/data/system/dropbox"), FgThread.get().getLooper());
}
/**
@@ -163,11 +168,12 @@ public final class DropBoxManagerService extends SystemService {
* @param context to use for receiving free space & gservices intents
* @param path to store drop box entries in
*/
public DropBoxManagerService(final Context context, File path) {
@VisibleForTesting
public DropBoxManagerService(final Context context, File path, Looper looper) {
super(context);
mDropBoxDir = path;
mContentResolver = getContext().getContentResolver();
mHandler = new Handler() {
mHandler = new Handler(looper) {
@Override
public void handleMessage(Message msg) {
if (msg.what == MSG_SEND_BROADCAST) {
@@ -338,11 +344,12 @@ public final class DropBoxManagerService extends SystemService {
if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) {
return new DropBoxManager.Entry(entry.tag, entry.timestampMillis);
}
final File file = entry.getFile(mDropBoxDir);
try {
return new DropBoxManager.Entry(
entry.tag, entry.timestampMillis, entry.file, entry.flags);
entry.tag, entry.timestampMillis, file, entry.flags);
} catch (IOException e) {
Slog.e(TAG, "Can't read: " + entry.file, e);
Slog.wtf(TAG, "Can't read: " + file, e);
// Continue to next file
}
}
@@ -410,7 +417,9 @@ public final class DropBoxManagerService extends SystemService {
numFound++;
if (doPrint) out.append("========================================\n");
out.append(date).append(" ").append(entry.tag == null ? "(no tag)" : entry.tag);
if (entry.file == null) {
final File file = entry.getFile(mDropBoxDir);
if (file == null) {
out.append(" (no file)\n");
continue;
} else if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) {
@@ -420,12 +429,12 @@ public final class DropBoxManagerService extends SystemService {
out.append(" (");
if ((entry.flags & DropBoxManager.IS_GZIPPED) != 0) out.append("compressed ");
out.append((entry.flags & DropBoxManager.IS_TEXT) != 0 ? "text" : "data");
out.append(", ").append(entry.file.length()).append(" bytes)\n");
out.append(", ").append(file.length()).append(" bytes)\n");
}
if (doFile || (doPrint && (entry.flags & DropBoxManager.IS_TEXT) == 0)) {
if (!doPrint) out.append(" ");
out.append(entry.file.getPath()).append("\n");
out.append(file.getPath()).append("\n");
}
if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) {
@@ -433,7 +442,7 @@ public final class DropBoxManagerService extends SystemService {
InputStreamReader isr = null;
try {
dbe = new DropBoxManager.Entry(
entry.tag, entry.timestampMillis, entry.file, entry.flags);
entry.tag, entry.timestampMillis, file, entry.flags);
if (doPrint) {
isr = new InputStreamReader(dbe.getInputStream());
@@ -466,7 +475,7 @@ public final class DropBoxManagerService extends SystemService {
}
} catch (IOException e) {
out.append("*** ").append(e.toString()).append("\n");
Slog.e(TAG, "Can't read: " + entry.file, e);
Slog.e(TAG, "Can't read: " + file, e);
} finally {
if (dbe != null) dbe.close();
if (isr != null) {
@@ -509,29 +518,37 @@ public final class DropBoxManagerService extends SystemService {
}
}
/** Metadata describing an on-disk log file. */
private static final class EntryFile implements Comparable<EntryFile> {
/**
* Metadata describing an on-disk log file.
*
* Note its instances do no have knowledge on what directory they're stored, just to save
* 4/8 bytes per instance. Instead, {@link #getFile} takes a directory so it can build a
* fullpath.
*/
@VisibleForTesting
static final class EntryFile implements Comparable<EntryFile> {
public final String tag;
public final long timestampMillis;
public final int flags;
public final File file;
public final int blocks;
/** Sorts earlier EntryFile instances before later ones. */
public final int compareTo(EntryFile o) {
if (timestampMillis < o.timestampMillis) return -1;
if (timestampMillis > o.timestampMillis) return 1;
if (file != null && o.file != null) return file.compareTo(o.file);
if (o.file != null) return -1;
if (file != null) return 1;
if (this == o) return 0;
if (hashCode() < o.hashCode()) return -1;
if (hashCode() > o.hashCode()) return 1;
return 0;
int comp = Long.compare(timestampMillis, o.timestampMillis);
if (comp != 0) return comp;
comp = ObjectUtils.compare(tag, o.tag);
if (comp != 0) return comp;
comp = Integer.compare(flags, o.flags);
if (comp != 0) return comp;
return Integer.compare(hashCode(), o.hashCode());
}
/**
* Moves an existing temporary file to a new log filename.
*
* @param temp file to rename
* @param dir to store file in
* @param tag to use for new log file name
@@ -544,76 +561,94 @@ public final class DropBoxManagerService extends SystemService {
int flags, int blockSize) throws IOException {
if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException();
this.tag = tag;
this.tag = TextUtils.safeIntern(tag);
this.timestampMillis = timestampMillis;
this.flags = flags;
this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis +
((flags & DropBoxManager.IS_TEXT) != 0 ? ".txt" : ".dat") +
((flags & DropBoxManager.IS_GZIPPED) != 0 ? ".gz" : ""));
if (!temp.renameTo(this.file)) {
throw new IOException("Can't rename " + temp + " to " + this.file);
final File file = this.getFile(dir);
if (!temp.renameTo(file)) {
throw new IOException("Can't rename " + temp + " to " + file);
}
this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize);
this.blocks = (int) ((file.length() + blockSize - 1) / blockSize);
}
/**
* Creates a zero-length tombstone for a file whose contents were lost.
*
* @param dir to store file in
* @param tag to use for new log file name
* @param timestampMillis of log entry
* @throws IOException if the file can't be created.
*/
public EntryFile(File dir, String tag, long timestampMillis) throws IOException {
this.tag = tag;
this.tag = TextUtils.safeIntern(tag);
this.timestampMillis = timestampMillis;
this.flags = DropBoxManager.IS_EMPTY;
this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis + ".lost");
this.blocks = 0;
new FileOutputStream(this.file).close();
new FileOutputStream(getFile(dir)).close();
}
/**
* Extracts metadata from an existing on-disk log filename.
*
* Note when a filename is not recognizable, it will create an instance that
* {@link #hasFile()} would return false on, and also remove the file.
*
* @param file name of existing log file
* @param blockSize to use for space accounting
*/
public EntryFile(File file, int blockSize) {
this.file = file;
this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize);
boolean parseFailure = false;
String name = file.getName();
int at = name.lastIndexOf('@');
if (at < 0) {
this.tag = null;
this.timestampMillis = 0;
this.flags = DropBoxManager.IS_EMPTY;
return;
}
int flags = 0;
this.tag = Uri.decode(name.substring(0, at));
if (name.endsWith(".gz")) {
flags |= DropBoxManager.IS_GZIPPED;
name = name.substring(0, name.length() - 3);
}
if (name.endsWith(".lost")) {
flags |= DropBoxManager.IS_EMPTY;
name = name.substring(at + 1, name.length() - 5);
} else if (name.endsWith(".txt")) {
flags |= DropBoxManager.IS_TEXT;
name = name.substring(at + 1, name.length() - 4);
} else if (name.endsWith(".dat")) {
name = name.substring(at + 1, name.length() - 4);
String tag = null;
long millis = 0;
final int at = name.lastIndexOf('@');
if (at < 0) {
parseFailure = true;
} else {
tag = Uri.decode(name.substring(0, at));
if (name.endsWith(".gz")) {
flags |= DropBoxManager.IS_GZIPPED;
name = name.substring(0, name.length() - 3);
}
if (name.endsWith(".lost")) {
flags |= DropBoxManager.IS_EMPTY;
name = name.substring(at + 1, name.length() - 5);
} else if (name.endsWith(".txt")) {
flags |= DropBoxManager.IS_TEXT;
name = name.substring(at + 1, name.length() - 4);
} else if (name.endsWith(".dat")) {
name = name.substring(at + 1, name.length() - 4);
} else {
parseFailure = true;
}
if (!parseFailure) {
try {
millis = Long.parseLong(name);
} catch (NumberFormatException e) {
parseFailure = true;
}
}
}
if (parseFailure) {
Slog.wtf(TAG, "Invalid filename: " + file);
// Remove the file and return an empty instance.
file.delete();
this.tag = null;
this.flags = DropBoxManager.IS_EMPTY;
this.timestampMillis = 0;
this.blocks = 0;
return;
}
this.flags = flags;
long millis;
try { millis = Long.parseLong(name); } catch (NumberFormatException e) { millis = 0; }
this.blocks = (int) ((file.length() + blockSize - 1) / blockSize);
this.tag = TextUtils.safeIntern(tag);
this.flags = flags;
this.timestampMillis = millis;
}
@@ -625,9 +660,50 @@ public final class DropBoxManagerService extends SystemService {
this.tag = null;
this.timestampMillis = millis;
this.flags = DropBoxManager.IS_EMPTY;
this.file = null;
this.blocks = 0;
}
/**
* @return whether an entry actually has a backing file, or it's an empty "tombstone"
* entry.
*/
public boolean hasFile() {
return tag != null;
}
/** @return File extension for the flags. */
private String getExtension() {
if ((flags & DropBoxManager.IS_EMPTY) != 0) {
return ".lost";
}
return ((flags & DropBoxManager.IS_TEXT) != 0 ? ".txt" : ".dat") +
((flags & DropBoxManager.IS_GZIPPED) != 0 ? ".gz" : "");
}
/**
* @return filename for this entry without the pathname.
*/
public String getFilename() {
return hasFile() ? Uri.encode(tag) + "@" + timestampMillis + getExtension() : null;
}
/**
* Get a full-path {@link File} representing this entry.
* @param dir Parent directly. The caller needs to pass it because {@link EntryFile}s don't
* know in which directory they're stored.
*/
public File getFile(File dir) {
return hasFile() ? new File(dir, getFilename()) : null;
}
/**
* If an entry has a backing file, remove it.
*/
public void deleteFile(File dir) {
if (hasFile()) {
getFile(dir).delete();
}
}
}
///////////////////////////////////////////////////////////////////////////
@@ -651,7 +727,7 @@ public final class DropBoxManagerService extends SystemService {
if (files == null) throw new IOException("Can't list files: " + mDropBoxDir);
mAllFiles = new FileList();
mFilesByTag = new HashMap<String, FileList>();
mFilesByTag = new ArrayMap<>();
// Scan pre-existing files.
for (File file : files) {
@@ -662,16 +738,12 @@ public final class DropBoxManagerService extends SystemService {
}
EntryFile entry = new EntryFile(file, mBlockSize);
if (entry.tag == null) {
Slog.w(TAG, "Unrecognized file: " + file);
continue;
} else if (entry.timestampMillis == 0) {
Slog.w(TAG, "Invalid filename: " + file);
file.delete();
continue;
}
enrollEntry(entry);
if (entry.hasFile()) {
// Enroll only when the filename is valid. Otherwise the above constructor
// has removed the file already.
enrollEntry(entry);
}
}
}
}
@@ -684,11 +756,11 @@ public final class DropBoxManagerService extends SystemService {
// mFilesByTag is used for trimming, so don't list empty files.
// (Zero-length/lost files are trimmed by date from mAllFiles.)
if (entry.tag != null && entry.file != null && entry.blocks > 0) {
if (entry.hasFile() && entry.blocks > 0) {
FileList tagFiles = mFilesByTag.get(entry.tag);
if (tagFiles == null) {
tagFiles = new FileList();
mFilesByTag.put(entry.tag, tagFiles);
mFilesByTag.put(TextUtils.safeIntern(entry.tag), tagFiles);
}
tagFiles.contents.add(entry);
tagFiles.blocks += entry.blocks;
@@ -722,8 +794,8 @@ public final class DropBoxManagerService extends SystemService {
tagFiles.blocks -= late.blocks;
}
if ((late.flags & DropBoxManager.IS_EMPTY) == 0) {
enrollEntry(new EntryFile(
late.file, mDropBoxDir, late.tag, t++, late.flags, mBlockSize));
enrollEntry(new EntryFile(late.getFile(mDropBoxDir), mDropBoxDir,
late.tag, t++, late.flags, mBlockSize));
} else {
enrollEntry(new EntryFile(mDropBoxDir, late.tag, t++));
}
@@ -757,7 +829,7 @@ public final class DropBoxManagerService extends SystemService {
FileList tag = mFilesByTag.get(entry.tag);
if (tag != null && tag.contents.remove(entry)) tag.blocks -= entry.blocks;
if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks;
if (entry.file != null) entry.file.delete();
entry.deleteFile(mDropBoxDir);
}
// Compute overall quota (a fraction of available free space) in blocks.
@@ -823,7 +895,7 @@ public final class DropBoxManagerService extends SystemService {
if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks;
try {
if (entry.file != null) entry.file.delete();
entry.deleteFile(mDropBoxDir);
enrollEntry(new EntryFile(mDropBoxDir, entry.tag, entry.timestampMillis));
} catch (IOException e) {
Slog.e(TAG, "Can't write tombstone file", e);

View File

@@ -18,18 +18,20 @@ package com.android.server;
import android.content.ContentResolver;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.Intent;
import android.os.DropBoxManager;
import android.os.Looper;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
import android.os.Process;
import android.os.ServiceManager;
import android.os.StatFs;
import android.os.UserHandle;
import android.provider.Settings;
import android.test.AndroidTestCase;
import com.android.server.DropBoxManagerService;
import com.android.server.DropBoxManagerService.EntryFile;
import java.io.BufferedReader;
import java.io.File;
@@ -41,8 +43,28 @@ import java.io.InputStreamReader;
import java.util.Random;
import java.util.zip.GZIPOutputStream;
/** Test {@link DropBoxManager} functionality. */
/**
* Test {@link DropBoxManager} functionality.
*
* Run with:
* bit FrameworksServicesTests:com.android.server.DropBoxTest
*/
public class DropBoxTest extends AndroidTestCase {
private Context mContext;
@Override
protected void setUp() throws Exception {
super.setUp();
mContext = new ContextWrapper(super.getContext()) {
@Override
public void sendBroadcastAsUser(Intent intent,
UserHandle user, String receiverPermission) {
// Don't actually send broadcasts.
}
};
}
public void tearDown() throws Exception {
ContentResolver cr = getContext().getContentResolver();
Settings.Global.putString(cr, Settings.Global.DROPBOX_AGE_SECONDS, "");
@@ -51,9 +73,15 @@ public class DropBoxTest extends AndroidTestCase {
Settings.Global.putString(cr, Settings.Global.DROPBOX_TAG_PREFIX + "DropBoxTest", "");
}
@Override
public Context getContext() {
return mContext;
}
public void testAddText() throws Exception {
File dir = getEmptyDir("testAddText");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
@@ -89,15 +117,19 @@ public class DropBoxTest extends AndroidTestCase {
public void testAddData() throws Exception {
File dir = getEmptyDir("testAddData");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
Thread.sleep(1);
dropbox.addData("DropBoxTest", "TEST".getBytes(), 0);
Thread.sleep(1);
long after = System.currentTimeMillis();
DropBoxManager.Entry e = dropbox.getNextEntry("DropBoxTest", before);
assertTrue(null == dropbox.getNextEntry("DropBoxTest", e.getTimeMillis()));
assertNotNull(e);
assertNull(dropbox.getNextEntry("DropBoxTest", e.getTimeMillis()));
assertEquals("DropBoxTest", e.getTag());
assertTrue(e.getTimeMillis() >= before);
@@ -114,10 +146,12 @@ public class DropBoxTest extends AndroidTestCase {
File dir = getEmptyDir("testAddFile");
long before = System.currentTimeMillis();
File f0 = new File(dir, "f0.txt");
File f1 = new File(dir, "f1.txt.gz");
File f2 = new File(dir, "f2.dat");
File f3 = new File(dir, "f2.dat.gz");
File clientDir = getEmptyDir("testAddFile_client");
File f0 = new File(clientDir, "f0.txt");
File f1 = new File(clientDir, "f1.txt.gz");
File f2 = new File(clientDir, "f2.dat");
File f3 = new File(clientDir, "f2.dat.gz");
FileWriter w0 = new FileWriter(f0);
GZIPOutputStream gz1 = new GZIPOutputStream(new FileOutputStream(f1));
@@ -134,7 +168,8 @@ public class DropBoxTest extends AndroidTestCase {
os2.close();
gz3.close();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addFile("DropBoxTest", f0, DropBoxManager.IS_TEXT);
@@ -200,7 +235,8 @@ public class DropBoxTest extends AndroidTestCase {
// Tombstone in the far future
new FileOutputStream(new File(dir, "DropBoxTest@" + (before + 100002) + ".lost")).close();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
// Until a write, the timestamps are taken at face value
@@ -251,7 +287,8 @@ public class DropBoxTest extends AndroidTestCase {
public void testIsTagEnabled() throws Exception {
File dir = getEmptyDir("testIsTagEnabled");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
@@ -284,7 +321,8 @@ public class DropBoxTest extends AndroidTestCase {
public void testGetNextEntry() throws Exception {
File dir = getEmptyDir("testGetNextEntry");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
long before = System.currentTimeMillis();
@@ -346,7 +384,8 @@ public class DropBoxTest extends AndroidTestCase {
final int overhead = 64;
long before = System.currentTimeMillis();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
addRandomEntry(dropbox, "DropBoxTest0", blockSize - overhead);
@@ -440,7 +479,8 @@ public class DropBoxTest extends AndroidTestCase {
// Write one normal entry and another so big that it is instantly tombstoned
long before = System.currentTimeMillis();
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addText("DropBoxTest", "TEST");
@@ -471,7 +511,8 @@ public class DropBoxTest extends AndroidTestCase {
public void testFileCountLimits() throws Exception {
File dir = getEmptyDir("testFileCountLimits");
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addText("DropBoxTest", "TEST0");
dropbox.addText("DropBoxTest", "TEST1");
@@ -524,7 +565,8 @@ public class DropBoxTest extends AndroidTestCase {
File dir = new File(getEmptyDir("testCreateDropBoxManagerWith"), "InvalidDirectory");
new FileOutputStream(dir).close(); // Create an empty file
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir);
DropBoxManagerService service = new DropBoxManagerService(getContext(), dir,
Looper.getMainLooper());
DropBoxManager dropbox = new DropBoxManager(getContext(), service.getServiceStub());
dropbox.addText("DropBoxTest", "should be ignored");
@@ -735,6 +777,223 @@ public class DropBoxTest extends AndroidTestCase {
assertTrue(after < before + 20);
}
public void testEntryFile() throws Exception {
File fromDir = getEmptyDir("testEntryFile_from");
File toDir = getEmptyDir("testEntryFile_to");
{
File f = new File(fromDir, "f0.txt");
try (FileWriter w = new FileWriter(f)) {
w.write("abc");
}
EntryFile e = new EntryFile(f, toDir, "tag:!", 12345, DropBoxManager.IS_TEXT, 1024);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(DropBoxManager.IS_TEXT, e.flags);
assertEquals(1, e.blocks);
assertFalse(f.exists()); // Because it should be renamed.
assertTrue(e.hasFile());
assertEquals(new File(toDir, "tag%3A!@12345.txt"), e.getFile(toDir));
assertTrue(e.getFile(toDir).exists());
}
// Same test with gzip.
{
File f = new File(fromDir, "f0.txt.gz"); // It's a lie; it's not actually gz.
try (FileWriter w = new FileWriter(f)) {
w.write("abc");
}
EntryFile e = new EntryFile(f, toDir, "tag:!", 12345,
DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED, 1024);
assertEquals("tag:!", e.tag);
assertFalse(f.exists()); // Because it should be renamed.
assertTrue(e.hasFile());
assertEquals(new File(toDir, "tag%3A!@12345.txt.gz"), e.getFile(toDir));
assertTrue(e.getFile(toDir).exists());
}
// binary, gzip.
{
File f = new File(fromDir, "f0.dat.gz"); // It's a lie; it's not actually gz.
try (FileWriter w = new FileWriter(f)) {
w.write("abc");
}
EntryFile e = new EntryFile(f, toDir, "tag:!", 12345,
DropBoxManager.IS_GZIPPED, 1024);
assertEquals("tag:!", e.tag);
assertFalse(f.exists()); // Because it should be renamed.
assertTrue(e.hasFile());
assertEquals(new File(toDir, "tag%3A!@12345.dat.gz"), e.getFile(toDir));
assertTrue(e.getFile(toDir).exists());
}
// Tombstone.
{
EntryFile e = new EntryFile(toDir, "tag:!", 12345);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(DropBoxManager.IS_EMPTY, e.flags);
assertEquals(0, e.blocks);
assertTrue(e.hasFile());
assertEquals(new File(toDir, "tag%3A!@12345.lost"), e.getFile(toDir));
assertTrue(e.getFile(toDir).exists());
}
// From existing files.
{
File f = new File(fromDir, "tag%3A!@12345.dat");
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(0, e.flags);
assertEquals(0, e.blocks);
assertTrue(f.exists());
}
{
File f = new File(fromDir, "tag%3A!@12345.dat.gz");
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(DropBoxManager.IS_GZIPPED, e.flags);
assertEquals(0, e.blocks);
assertTrue(f.exists());
}
{
File f = new File(fromDir, "tag%3A!@12345.txt");
try (FileWriter w = new FileWriter(f)) {
w.write(new char[1024]);
}
EntryFile e = new EntryFile(f, 1024);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(DropBoxManager.IS_TEXT, e.flags);
assertEquals(1, e.blocks);
assertTrue(f.exists());
}
{
File f = new File(fromDir, "tag%3A!@12345.txt.gz");
try (FileWriter w = new FileWriter(f)) {
w.write(new char[1025]);
}
EntryFile e = new EntryFile(f, 1024);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(DropBoxManager.IS_TEXT | DropBoxManager.IS_GZIPPED, e.flags);
assertEquals(2, e.blocks);
assertTrue(f.exists());
}
{
File f = new File(fromDir, "tag%3A!@12345.lost");
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals("tag:!", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(DropBoxManager.IS_EMPTY, e.flags);
assertEquals(0, e.blocks);
assertTrue(f.exists());
}
{
File f = new File(fromDir, "@12345.dat"); // Empty tag -- this actually works.
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals("", e.tag);
assertEquals(12345, e.timestampMillis);
assertEquals(0, e.flags);
assertEquals(0, e.blocks);
assertTrue(f.exists());
}
// From invalid filenames.
{
File f = new File(fromDir, "tag.dat"); // No @.
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals(null, e.tag);
assertEquals(0, e.timestampMillis);
assertEquals(DropBoxManager.IS_EMPTY, e.flags);
assertEquals(0, e.blocks);
assertFalse(f.exists());
}
{
File f = new File(fromDir, "tag@.dat"); // Invalid timestamp.
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals(null, e.tag);
assertEquals(0, e.timestampMillis);
assertEquals(DropBoxManager.IS_EMPTY, e.flags);
assertEquals(0, e.blocks);
assertFalse(f.exists());
}
{
File f = new File(fromDir, "tag@12345.daxt"); // Invalid extension.
f.createNewFile();
EntryFile e = new EntryFile(f, 1024);
assertEquals(null, e.tag);
assertEquals(0, e.timestampMillis);
assertEquals(DropBoxManager.IS_EMPTY, e.flags);
assertEquals(0, e.blocks);
assertFalse(f.exists());
}
}
public void testCompareEntries() {
File dir = getEmptyDir("testCompareEntries");
assertEquals(-1,
new EntryFile(new File(dir, "aaa@100.dat"), 1).compareTo(
new EntryFile(new File(dir, "bbb@200.dat"), 1)));
assertEquals(1,
new EntryFile(new File(dir, "aaa@200.dat"), 1).compareTo(
new EntryFile(new File(dir, "bbb@100.dat"), 1)));
assertEquals(-1,
new EntryFile(new File(dir, "aaa@100.dat"), 1).compareTo(
new EntryFile(new File(dir, "bbb@100.dat"), 1)));
assertEquals(1,
new EntryFile(new File(dir, "bbb@100.dat"), 1).compareTo(
new EntryFile(new File(dir, "aaa@100.dat"), 1)));
}
private void addRandomEntry(DropBoxManager dropbox, String tag, int size) throws Exception {
byte[] bytes = new byte[size];
new Random(System.currentTimeMillis()).nextBytes(bytes);