14410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor/*
24410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * Copyright (C) 2009 The Android Open Source Project
34410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor *
44410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * Licensed under the Apache License, Version 2.0 (the "License");
54410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * you may not use this file except in compliance with the License.
64410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * You may obtain a copy of the License at
74410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor *
84410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor *      http://www.apache.org/licenses/LICENSE-2.0
94410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor *
104410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * Unless required by applicable law or agreed to in writing, software
114410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * distributed under the License is distributed on an "AS IS" BASIS,
124410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * See the License for the specific language governing permissions and
144410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor * limitations under the License.
154410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor */
164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
174410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorpackage com.android.server;
184410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
194410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.content.BroadcastReceiver;
204410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.content.ContentResolver;
214410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.content.Context;
224410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.content.Intent;
234410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.content.IntentFilter;
244410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.content.pm.PackageManager;
2543866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongkerimport android.database.ContentObserver;
264410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.net.Uri;
27911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkeyimport android.os.Binder;
283d40df335e4c0df972720271a84277077f168f65Dan Egnorimport android.os.Debug;
29f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnorimport android.os.DropBoxManager;
308bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackbornimport android.os.FileUtils;
3143866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongkerimport android.os.Handler;
3226caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautnerimport android.os.Message;
334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.os.StatFs;
344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.os.SystemClock;
355ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackbornimport android.os.UserHandle;
364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport android.provider.Settings;
373d40df335e4c0df972720271a84277077f168f65Dan Egnorimport android.text.format.Time;
388a9b22056b13477f59df934928c00c58b5871c95Joe Onoratoimport android.util.Slog;
394410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
40f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnorimport com.android.internal.os.IDropBoxManagerService;
41952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor
4289647b1172cdf40a3681922150122b0bd2ea83f1Brad Fitzpatrickimport java.io.BufferedOutputStream;
434410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.File;
444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.FileDescriptor;
454410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.FileOutputStream;
464410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.IOException;
47952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnorimport java.io.InputStream;
484410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.InputStreamReader;
494410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.OutputStream;
504410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.io.PrintWriter;
514410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.util.ArrayList;
524410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.util.HashMap;
534410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.util.SortedSet;
544410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.util.TreeSet;
554410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnorimport java.util.zip.GZIPOutputStream;
564410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor/**
58f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor * Implementation of {@link IDropBoxManagerService} using the filesystem.
59f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor * Clients use {@link DropBoxManager} to access this service.
604410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor */
61f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnorpublic final class DropBoxManagerService extends IDropBoxManagerService.Stub {
62f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor    private static final String TAG = "DropBoxManagerService";
634410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private static final int DEFAULT_AGE_SECONDS = 3 * 86400;
643a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor    private static final int DEFAULT_MAX_FILES = 1000;
653a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor    private static final int DEFAULT_QUOTA_KB = 5 * 1024;
663a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor    private static final int DEFAULT_QUOTA_PERCENT = 10;
673a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor    private static final int DEFAULT_RESERVE_PERCENT = 10;
684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private static final int QUOTA_RESCAN_MILLIS = 5000;
694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7026caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner    // mHandler 'what' value.
7126caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner    private static final int MSG_SEND_BROADCAST = 1;
7226caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner
733d40df335e4c0df972720271a84277077f168f65Dan Egnor    private static final boolean PROFILE_DUMP = false;
743d40df335e4c0df972720271a84277077f168f65Dan Egnor
754410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    // TODO: This implementation currently uses one file per entry, which is
764410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    // inefficient for smallish entries -- consider using a single queue file
774410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    // per tag (or even globally) instead.
784410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    // The cached context and derived objects
804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
814410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private final Context mContext;
824410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private final ContentResolver mContentResolver;
834410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private final File mDropBoxDir;
844410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
854410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    // Accounting of all currently written log files (set in init()).
864410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
874410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private FileList mAllFiles = null;
884410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private HashMap<String, FileList> mFilesByTag = null;
894410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    // Various bits of disk information
914410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
924410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private StatFs mStatFs = null;
934410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private int mBlockSize = 0;
944410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private int mCachedQuotaBlocks = 0;  // Space we can use: computed from free space, etc.
954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private long mCachedQuotaUptimeMillis = 0;
964410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
9734165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick    private volatile boolean mBooted = false;
9834165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick
9926caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner    // Provide a way to perform sendBroadcast asynchronously to avoid deadlocks.
10026caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner    private final Handler mHandler;
10126caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner
1024410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** Receives events that might indicate a need to clean up files. */
1034410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
1044410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        @Override
1054410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public void onReceive(Context context, Intent intent) {
10634165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick            if (intent != null && Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
10734165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick                mBooted = true;
10834165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick                return;
10934165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick            }
11034165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick
11134165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick            // Else, for ACTION_DEVICE_STORAGE_LOW:
1124410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            mCachedQuotaUptimeMillis = 0;  // Force a re-check of quota size
1133a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor
1143a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor            // Run the initialization in the background (not this main thread).
1153a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor            // The init() and trimToFit() methods are synchronized, so they still
1163a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor            // block other users -- but at least the onReceive() call can finish.
1173a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor            new Thread() {
1183a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                public void run() {
1193a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                    try {
1203a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                        init();
1213a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                        trimToFit();
1223a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                    } catch (IOException e) {
1233a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                        Slog.e(TAG, "Can't init", e);
1243a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                    }
1253a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor                }
1263a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor            }.start();
1274410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
1284410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    };
1294410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1304410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /**
1314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     * Creates an instance of managed drop box storage.  Normally there is one of these
1324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     * run by the system, but others can be created for testing and other purposes.
1334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     *
1344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     * @param context to use for receiving free space & gservices intents
1354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     * @param path to store drop box entries in
1364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     */
13743866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker    public DropBoxManagerService(final Context context, File path) {
1384410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        mDropBoxDir = path;
1394410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1404410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // Set up intent receivers
1414410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        mContext = context;
1424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        mContentResolver = context.getContentResolver();
14334165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick
14434165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick        IntentFilter filter = new IntentFilter();
14534165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick        filter.addAction(Intent.ACTION_DEVICE_STORAGE_LOW);
14634165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick        filter.addAction(Intent.ACTION_BOOT_COMPLETED);
14734165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick        context.registerReceiver(mReceiver, filter);
14843866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker
14943866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker        mContentResolver.registerContentObserver(
150625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey            Settings.Global.CONTENT_URI, true,
15143866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker            new ContentObserver(new Handler()) {
15226caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner                @Override
15343866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker                public void onChange(boolean selfChange) {
15443866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker                    mReceiver.onReceive(context, (Intent) null);
15543866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker                }
15643866e0c48bb0effe8805afd62b253e50ca7d591Doug Zongker            });
1574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
15826caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner        mHandler = new Handler() {
15926caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            @Override
16026caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            public void handleMessage(Message msg) {
16126caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner                if (msg.what == MSG_SEND_BROADCAST) {
1625ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackborn                    mContext.sendBroadcastAsUser((Intent)msg.obj, UserHandle.OWNER,
1635ac72a29593ab9a20337a2225df52bdf4754be02Dianne Hackborn                            android.Manifest.permission.READ_LOGS);
16426caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner                }
16526caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            }
16626caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner        };
16726caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner
1684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // The real work gets done lazily in init() -- that way service creation always
1694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // succeeds, and things like disk problems cause individual method failures.
1704410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
1714410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1724410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** Unregisters broadcast receivers and any other hooks -- for test instances */
1734410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    public void stop() {
1744410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        mContext.unregisterReceiver(mReceiver);
1754410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
1764410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
17726caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner    @Override
178f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor    public void add(DropBoxManager.Entry entry) {
1794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        File temp = null;
1804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        OutputStream output = null;
181952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor        final String tag = entry.getTag();
1824410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        try {
183952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor            int flags = entry.getFlags();
184f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException();
1854410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1864410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            init();
1874410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (!isTagEnabled(tag)) return;
1884410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            long max = trimToFit();
1894410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            long lastTrim = System.currentTimeMillis();
1904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1914410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            byte[] buffer = new byte[mBlockSize];
192952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor            InputStream input = entry.getInputStream();
1934410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1944410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // First, accumulate up to one block worth of data in memory before
1954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // deciding whether to compress the data or not.
1964410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
1974410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int read = 0;
1984410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            while (read < buffer.length) {
1994410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                int n = input.read(buffer, read, buffer.length - read);
2004410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (n <= 0) break;
2014410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                read += n;
2024410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
2034410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2044410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // If we have at least one block, compress it -- otherwise, just write
2054410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // the data in uncompressed form.
2064410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2074410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            temp = new File(mDropBoxDir, "drop" + Thread.currentThread().getId() + ".tmp");
20889647b1172cdf40a3681922150122b0bd2ea83f1Brad Fitzpatrick            int bufferSize = mBlockSize;
20989647b1172cdf40a3681922150122b0bd2ea83f1Brad Fitzpatrick            if (bufferSize > 4096) bufferSize = 4096;
21089647b1172cdf40a3681922150122b0bd2ea83f1Brad Fitzpatrick            if (bufferSize < 512) bufferSize = 512;
2118bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            FileOutputStream foutput = new FileOutputStream(temp);
2128bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn            output = new BufferedOutputStream(foutput, bufferSize);
213f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            if (read == buffer.length && ((flags & DropBoxManager.IS_GZIPPED) == 0)) {
2144410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                output = new GZIPOutputStream(output);
215f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                flags = flags | DropBoxManager.IS_GZIPPED;
2164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
2174410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2184410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            do {
2194410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                output.write(buffer, 0, read);
2204410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2214410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                long now = System.currentTimeMillis();
2224410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (now - lastTrim > 30 * 1000) {
2234410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    max = trimToFit();  // In case data dribbles in slowly
2244410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    lastTrim = now;
2254410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
2264410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2274410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                read = input.read(buffer);
2284410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (read <= 0) {
2298bdf5935c0db4a66ab33a10b43398d2523cfa15dDianne Hackborn                    FileUtils.sync(foutput);
2304410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    output.close();  // Get a final size measurement
2314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    output = null;
2324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                } else {
2334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    output.flush();  // So the size measurement is pseudo-reasonable
2344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
2354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                long len = temp.length();
2374410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (len > max) {
2388a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.w(TAG, "Dropping: " + tag + " (" + temp.length() + " > " + max + " bytes)");
2394410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    temp.delete();
2404410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    temp = null;  // Pass temp = null to createEntry() to leave a tombstone
2414410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    break;
2424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
2434410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } while (read > 0);
2444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
245b247536aa3d458750edbc6b45b2348a994d83426Hakan Still            long time = createEntry(temp, tag, flags);
2464410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            temp = null;
247b247536aa3d458750edbc6b45b2348a994d83426Hakan Still
24826caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            final Intent dropboxIntent = new Intent(DropBoxManager.ACTION_DROPBOX_ENTRY_ADDED);
249b247536aa3d458750edbc6b45b2348a994d83426Hakan Still            dropboxIntent.putExtra(DropBoxManager.EXTRA_TAG, tag);
250b247536aa3d458750edbc6b45b2348a994d83426Hakan Still            dropboxIntent.putExtra(DropBoxManager.EXTRA_TIME, time);
25134165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick            if (!mBooted) {
25234165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick                dropboxIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
25334165c6967833dfe6ff54a9097eb4afa1054184dBrad Fitzpatrick            }
25426caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            // Call sendBroadcast after returning from this call to avoid deadlock. In particular
25526caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            // the caller may be holding the WindowManagerService lock but sendBroadcast requires a
25626caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            // lock in ActivityManagerService. ActivityManagerService has been caught holding that
25726caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            // very lock while waiting for the WindowManagerService lock.
25826caf7adf62fa36dc3fa7c194ff38520d1003f11Craig Mautner            mHandler.sendMessage(mHandler.obtainMessage(MSG_SEND_BROADCAST, dropboxIntent));
2594410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        } catch (IOException e) {
2608a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.e(TAG, "Can't write: " + tag, e);
2614410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        } finally {
2624410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            try { if (output != null) output.close(); } catch (IOException e) {}
263952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor            entry.close();
2644410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (temp != null) temp.delete();
2654410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
2664410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
2674410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    public boolean isTagEnabled(String tag) {
269911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey        final long token = Binder.clearCallingIdentity();
270911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey        try {
271911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey            return !"disabled".equals(Settings.Global.getString(
272911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey                    mContentResolver, Settings.Global.DROPBOX_TAG_PREFIX + tag));
273911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey        } finally {
274911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey            Binder.restoreCallingIdentity(token);
275911d7f411f36f2279aae44c89ff1d33a29140046Jeff Sharkey        }
2764410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
2774410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
278f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor    public synchronized DropBoxManager.Entry getNextEntry(String tag, long millis) {
2794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.READ_LOGS)
2804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                != PackageManager.PERMISSION_GRANTED) {
2814410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            throw new SecurityException("READ_LOGS permission required");
2824410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
2834410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
2844410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        try {
2854410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            init();
2864410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        } catch (IOException e) {
2878a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.e(TAG, "Can't init", e);
2884410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            return null;
2894410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
2904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
291b3b06fc39b032288d33f663b687d78bec7f1d724Dan Egnor        FileList list = tag == null ? mAllFiles : mFilesByTag.get(tag);
292b3b06fc39b032288d33f663b687d78bec7f1d724Dan Egnor        if (list == null) return null;
293b3b06fc39b032288d33f663b687d78bec7f1d724Dan Egnor
294b3b06fc39b032288d33f663b687d78bec7f1d724Dan Egnor        for (EntryFile entry : list.contents.tailSet(new EntryFile(millis + 1))) {
2954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (entry.tag == null) continue;
296f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) {
297f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                return new DropBoxManager.Entry(entry.tag, entry.timestampMillis);
298952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor            }
2994410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            try {
300f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                return new DropBoxManager.Entry(
301f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                        entry.tag, entry.timestampMillis, entry.file, entry.flags);
3024410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } catch (IOException e) {
3038a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                Slog.e(TAG, "Can't read: " + entry.file, e);
3044410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                // Continue to next file
3054410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
3064410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
3074410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3084410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        return null;
3094410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
3104410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3114410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    public synchronized void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3124410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
3134410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                != PackageManager.PERMISSION_GRANTED) {
314f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            pw.println("Permission Denial: Can't dump DropBoxManagerService");
3154410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            return;
3164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
3174410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3184410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        try {
3194410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            init();
3204410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        } catch (IOException e) {
3214410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            pw.println("Can't initialize: " + e);
3228a9b22056b13477f59df934928c00c58b5871c95Joe Onorato            Slog.e(TAG, "Can't init", e);
3234410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            return;
3244410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
3254410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3263d40df335e4c0df972720271a84277077f168f65Dan Egnor        if (PROFILE_DUMP) Debug.startMethodTracing("/data/trace/dropbox.dump");
3273d40df335e4c0df972720271a84277077f168f65Dan Egnor
3285ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor        StringBuilder out = new StringBuilder();
3294410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        boolean doPrint = false, doFile = false;
3304410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        ArrayList<String> searchArgs = new ArrayList<String>();
3314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        for (int i = 0; args != null && i < args.length; i++) {
3324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (args[i].equals("-p") || args[i].equals("--print")) {
3334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                doPrint = true;
3344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else if (args[i].equals("-f") || args[i].equals("--file")) {
3354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                doFile = true;
3364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else if (args[i].startsWith("-")) {
3375ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append("Unknown argument: ").append(args[i]).append("\n");
3384410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else {
3394410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                searchArgs.add(args[i]);
3404410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
3414410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
3424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3435ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor        out.append("Drop box contents: ").append(mAllFiles.contents.size()).append(" entries\n");
3444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3454410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (!searchArgs.isEmpty()) {
3465ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            out.append("Searching for:");
3475ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            for (String a : searchArgs) out.append(" ").append(a);
3485ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            out.append("\n");
3494410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
3504410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3513d40df335e4c0df972720271a84277077f168f65Dan Egnor        int numFound = 0, numArgs = searchArgs.size();
3523d40df335e4c0df972720271a84277077f168f65Dan Egnor        Time time = new Time();
3535ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor        out.append("\n");
3544410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        for (EntryFile entry : mAllFiles.contents) {
3553d40df335e4c0df972720271a84277077f168f65Dan Egnor            time.set(entry.timestampMillis);
3563d40df335e4c0df972720271a84277077f168f65Dan Egnor            String date = time.format("%Y-%m-%d %H:%M:%S");
3574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            boolean match = true;
3583d40df335e4c0df972720271a84277077f168f65Dan Egnor            for (int i = 0; i < numArgs && match; i++) {
3593d40df335e4c0df972720271a84277077f168f65Dan Egnor                String arg = searchArgs.get(i);
3603d40df335e4c0df972720271a84277077f168f65Dan Egnor                match = (date.contains(arg) || arg.equals(entry.tag));
3613d40df335e4c0df972720271a84277077f168f65Dan Egnor            }
3624410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (!match) continue;
3634410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3644410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            numFound++;
36542471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor            if (doPrint) out.append("========================================\n");
3665ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            out.append(date).append(" ").append(entry.tag == null ? "(no tag)" : entry.tag);
3674410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (entry.file == null) {
3685ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append(" (no file)\n");
3694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                continue;
370f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            } else if ((entry.flags & DropBoxManager.IS_EMPTY) != 0) {
3715ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append(" (contents lost)\n");
3724410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                continue;
3734410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else {
3745ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append(" (");
3755ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                if ((entry.flags & DropBoxManager.IS_GZIPPED) != 0) out.append("compressed ");
3765ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append((entry.flags & DropBoxManager.IS_TEXT) != 0 ? "text" : "data");
3775ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append(", ").append(entry.file.length()).append(" bytes)\n");
3784410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
3794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
380f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            if (doFile || (doPrint && (entry.flags & DropBoxManager.IS_TEXT) == 0)) {
3815ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                if (!doPrint) out.append("    ");
3825ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                out.append(entry.file.getPath()).append("\n");
3834410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
3844410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
385f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            if ((entry.flags & DropBoxManager.IS_TEXT) != 0 && (doPrint || !doFile)) {
386f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                DropBoxManager.Entry dbe = null;
3870c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                InputStreamReader isr = null;
3884410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                try {
389f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                    dbe = new DropBoxManager.Entry(
3904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                             entry.tag, entry.timestampMillis, entry.file, entry.flags);
3914410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
3924410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    if (doPrint) {
3930c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                        isr = new InputStreamReader(dbe.getInputStream());
3944410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        char[] buf = new char[4096];
3954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        boolean newline = false;
3964410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        for (;;) {
3970c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                            int n = isr.read(buf);
3984410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                            if (n <= 0) break;
3995ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                            out.append(buf, 0, n);
4004410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                            newline = (buf[n - 1] == '\n');
40142471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor
40242471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor                            // Flush periodically when printing to avoid out-of-memory.
40342471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor                            if (out.length() > 65536) {
40442471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor                                pw.write(out.toString());
40542471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor                                out.setLength(0);
40642471dd5552a346dd82a58a663159875ccc4fb79Dan Egnor                            }
4074410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        }
4085ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                        if (!newline) out.append("\n");
4094410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    } else {
4104410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        String text = dbe.getText(70);
4114410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        boolean truncated = (text.length() == 70);
4125ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                        out.append("    ").append(text.trim().replace('\n', '/'));
4135ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                        if (truncated) out.append(" ...");
4145ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                        out.append("\n");
4154410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    }
4164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                } catch (IOException e) {
4175ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor                    out.append("*** ").append(e.toString()).append("\n");
4188a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.e(TAG, "Can't read: " + entry.file, e);
4194410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                } finally {
4204410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    if (dbe != null) dbe.close();
4210c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                    if (isr != null) {
4220c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                        try {
4230c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                            isr.close();
4240c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                        } catch (IOException unused) {
4250c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                        }
4260c8224000db6a3c876f1d3717975a22d10ecddecBrad Fitzpatrick                    }
4274410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
4284410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
4294410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4305ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            if (doPrint) out.append("\n");
4314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
4324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4335ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor        if (numFound == 0) out.append("(No entries found.)\n");
4344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (args == null || args.length == 0) {
4365ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            if (!doPrint) out.append("\n");
4375ec249abe376898d3249e978b44fd2ee0cc8c573Dan Egnor            out.append("Usage: dumpsys dropbox [--print|--file] [YYYY-mm-dd] [HH:MM:SS] [tag]\n");
4384410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
4393d40df335e4c0df972720271a84277077f168f65Dan Egnor
4403d40df335e4c0df972720271a84277077f168f65Dan Egnor        pw.write(out.toString());
4413d40df335e4c0df972720271a84277077f168f65Dan Egnor        if (PROFILE_DUMP) Debug.stopMethodTracing();
4424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
4434410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    ///////////////////////////////////////////////////////////////////////////
4454410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4464410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** Chronologically sorted list of {@link #EntryFile} */
4474410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private static final class FileList implements Comparable<FileList> {
4484410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public int blocks = 0;
4494410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final TreeSet<EntryFile> contents = new TreeSet<EntryFile>();
4504410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4514410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        /** Sorts bigger FileList instances before smaller ones. */
4524410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final int compareTo(FileList o) {
4534410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (blocks != o.blocks) return o.blocks - blocks;
4544410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (this == o) return 0;
4554410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (hashCode() < o.hashCode()) return -1;
4564410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (hashCode() > o.hashCode()) return 1;
4574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            return 0;
4584410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
4594410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
4604410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4614410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** Metadata describing an on-disk log file. */
4624410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private static final class EntryFile implements Comparable<EntryFile> {
4634410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final String tag;
4644410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final long timestampMillis;
4654410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final int flags;
4664410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final File file;
4674410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final int blocks;
4684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        /** Sorts earlier EntryFile instances before later ones. */
4704410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public final int compareTo(EntryFile o) {
4714410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (timestampMillis < o.timestampMillis) return -1;
4724410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (timestampMillis > o.timestampMillis) return 1;
4734410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (file != null && o.file != null) return file.compareTo(o.file);
4744410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (o.file != null) return -1;
4754410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (file != null) return 1;
4764410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (this == o) return 0;
4774410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (hashCode() < o.hashCode()) return -1;
4784410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (hashCode() > o.hashCode()) return 1;
4794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            return 0;
4804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
4814410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4824410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        /**
4834410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * Moves an existing temporary file to a new log filename.
4844410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param temp file to rename
4854410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param dir to store file in
4864410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param tag to use for new log file name
4874410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param timestampMillis of log entry
488952402704a175ba27f6c89dff1ada634c5ce5626Dan Egnor         * @param flags for the entry data
4894410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param blockSize to use for space accounting
4904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @throws IOException if the file can't be moved
4914410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         */
4924410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public EntryFile(File temp, File dir, String tag,long timestampMillis,
4934410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                         int flags, int blockSize) throws IOException {
494f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            if ((flags & DropBoxManager.IS_EMPTY) != 0) throw new IllegalArgumentException();
4954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
4964410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.tag = tag;
4974410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.timestampMillis = timestampMillis;
4984410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.flags = flags;
4994410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis +
500f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                    ((flags & DropBoxManager.IS_TEXT) != 0 ? ".txt" : ".dat") +
501f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                    ((flags & DropBoxManager.IS_GZIPPED) != 0 ? ".gz" : ""));
5024410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5034410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (!temp.renameTo(this.file)) {
5044410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                throw new IOException("Can't rename " + temp + " to " + this.file);
5054410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
5064410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize);
5074410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
5084410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5094410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        /**
5104410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * Creates a zero-length tombstone for a file whose contents were lost.
5114410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param dir to store file in
5124410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param tag to use for new log file name
5134410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param timestampMillis of log entry
5144410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @throws IOException if the file can't be created.
5154410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         */
5164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public EntryFile(File dir, String tag, long timestampMillis) throws IOException {
5174410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.tag = tag;
5184410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.timestampMillis = timestampMillis;
519f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            this.flags = DropBoxManager.IS_EMPTY;
5204410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.file = new File(dir, Uri.encode(tag) + "@" + timestampMillis + ".lost");
5214410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.blocks = 0;
5224410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            new FileOutputStream(this.file).close();
5234410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
5244410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5254410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        /**
5264410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * Extracts metadata from an existing on-disk log filename.
5274410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param file name of existing log file
5284410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param blockSize to use for space accounting
5294410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         */
5304410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public EntryFile(File file, int blockSize) {
5314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.file = file;
5324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.blocks = (int) ((this.file.length() + blockSize - 1) / blockSize);
5334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            String name = file.getName();
5354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int at = name.lastIndexOf('@');
5364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (at < 0) {
5374410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                this.tag = null;
5384410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                this.timestampMillis = 0;
539f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                this.flags = DropBoxManager.IS_EMPTY;
5404410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                return;
5414410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
5424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5434410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int flags = 0;
5444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.tag = Uri.decode(name.substring(0, at));
5454410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (name.endsWith(".gz")) {
546f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                flags |= DropBoxManager.IS_GZIPPED;
5474410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                name = name.substring(0, name.length() - 3);
5484410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
5494410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (name.endsWith(".lost")) {
550f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                flags |= DropBoxManager.IS_EMPTY;
5514410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                name = name.substring(at + 1, name.length() - 5);
5524410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else if (name.endsWith(".txt")) {
553f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                flags |= DropBoxManager.IS_TEXT;
5544410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                name = name.substring(at + 1, name.length() - 4);
5554410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else if (name.endsWith(".dat")) {
5564410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                name = name.substring(at + 1, name.length() - 4);
5574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } else {
558f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                this.flags = DropBoxManager.IS_EMPTY;
5594410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                this.timestampMillis = 0;
5604410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                return;
5614410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
5624410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.flags = flags;
5634410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5644410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            long millis;
5654410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            try { millis = Long.valueOf(name); } catch (NumberFormatException e) { millis = 0; }
5664410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.timestampMillis = millis;
5674410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
5684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        /**
5704410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * Creates a EntryFile object with only a timestamp for comparison purposes.
5714410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         * @param timestampMillis to compare with.
5724410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor         */
5734410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        public EntryFile(long millis) {
5744410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.tag = null;
5754410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.timestampMillis = millis;
576f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor            this.flags = DropBoxManager.IS_EMPTY;
5774410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.file = null;
5784410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            this.blocks = 0;
5794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
5804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
5814410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5824410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    ///////////////////////////////////////////////////////////////////////////
5834410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5844410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** If never run before, scans disk contents to build in-memory tracking data. */
5854410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private synchronized void init() throws IOException {
5864410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (mStatFs == null) {
5874410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (!mDropBoxDir.isDirectory() && !mDropBoxDir.mkdirs()) {
5884410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                throw new IOException("Can't mkdir: " + mDropBoxDir);
5894410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
5904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            try {
5914410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                mStatFs = new StatFs(mDropBoxDir.getPath());
5924410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                mBlockSize = mStatFs.getBlockSize();
5934410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            } catch (IllegalArgumentException e) {  // StatFs throws this on error
5944410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                throw new IOException("Can't statfs: " + mDropBoxDir);
5954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
5964410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
5974410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
5984410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (mAllFiles == null) {
5994410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            File[] files = mDropBoxDir.listFiles();
6004410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (files == null) throw new IOException("Can't list files: " + mDropBoxDir);
6014410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6024410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            mAllFiles = new FileList();
6034410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            mFilesByTag = new HashMap<String, FileList>();
6044410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6054410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // Scan pre-existing files.
6064410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            for (File file : files) {
6074410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (file.getName().endsWith(".tmp")) {
6088a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.i(TAG, "Cleaning temp file: " + file);
6094410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    file.delete();
6104410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    continue;
6114410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
6124410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6134410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                EntryFile entry = new EntryFile(file, mBlockSize);
6144410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (entry.tag == null) {
6158a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.w(TAG, "Unrecognized file: " + file);
6164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    continue;
6174410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                } else if (entry.timestampMillis == 0) {
6188a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                    Slog.w(TAG, "Invalid filename: " + file);
6194410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    file.delete();
6204410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    continue;
6214410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
6224410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6234410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                enrollEntry(entry);
6244410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
6254410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
6264410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
6274410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6284410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** Adds a disk log file to in-memory tracking for accounting and enumeration. */
6294410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private synchronized void enrollEntry(EntryFile entry) {
6304410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        mAllFiles.contents.add(entry);
6314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        mAllFiles.blocks += entry.blocks;
6324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // mFilesByTag is used for trimming, so don't list empty files.
6344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // (Zero-length/lost files are trimmed by date from mAllFiles.)
6354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (entry.tag != null && entry.file != null && entry.blocks > 0) {
6374410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            FileList tagFiles = mFilesByTag.get(entry.tag);
6384410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (tagFiles == null) {
6394410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                tagFiles = new FileList();
6404410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                mFilesByTag.put(entry.tag, tagFiles);
6414410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
6424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            tagFiles.contents.add(entry);
6434410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            tagFiles.blocks += entry.blocks;
6444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
6454410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
6464410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6474410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /** Moves a temporary file to a final log filename and enrolls it. */
648b247536aa3d458750edbc6b45b2348a994d83426Hakan Still    private synchronized long createEntry(File temp, String tag, int flags) throws IOException {
6494410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        long t = System.currentTimeMillis();
6504410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6514410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // Require each entry to have a unique timestamp; if there are entries
6524410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // >10sec in the future (due to clock skew), drag them back to avoid
6534410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // keeping them around forever.
6544410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6554410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        SortedSet<EntryFile> tail = mAllFiles.contents.tailSet(new EntryFile(t + 10000));
6564410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        EntryFile[] future = null;
6574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (!tail.isEmpty()) {
6584410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            future = tail.toArray(new EntryFile[tail.size()]);
6594410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            tail.clear();  // Remove from mAllFiles
6604410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
6614410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6624410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (!mAllFiles.contents.isEmpty()) {
6634410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            t = Math.max(t, mAllFiles.contents.last().timestampMillis + 1);
6644410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
6654410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6664410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (future != null) {
6674410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            for (EntryFile late : future) {
6684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                mAllFiles.blocks -= late.blocks;
6694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                FileList tagFiles = mFilesByTag.get(late.tag);
670f283e3621026b0e3703b750d7b95d706e4bff050Dan Egnor                if (tagFiles != null && tagFiles.contents.remove(late)) {
671f283e3621026b0e3703b750d7b95d706e4bff050Dan Egnor                    tagFiles.blocks -= late.blocks;
672f283e3621026b0e3703b750d7b95d706e4bff050Dan Egnor                }
673f18a01c77e78209b74e34d05cfb352fa4a92db5fDan Egnor                if ((late.flags & DropBoxManager.IS_EMPTY) == 0) {
6744410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    enrollEntry(new EntryFile(
6754410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                            late.file, mDropBoxDir, late.tag, t++, late.flags, mBlockSize));
6764410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                } else {
6774410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    enrollEntry(new EntryFile(mDropBoxDir, late.tag, t++));
6784410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
6794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
6804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
6814410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6824410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (temp == null) {
6834410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            enrollEntry(new EntryFile(mDropBoxDir, tag, t));
6844410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        } else {
6854410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            enrollEntry(new EntryFile(temp, mDropBoxDir, tag, t, flags, mBlockSize));
6864410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
687b247536aa3d458750edbc6b45b2348a994d83426Hakan Still        return t;
6884410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
6894410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
6904410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    /**
6914410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     * Trims the files on disk to make sure they aren't using too much space.
6924410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     * @return the overall quota for storage (in bytes)
6934410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor     */
6944410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    private synchronized long trimToFit() {
6954410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // Expunge aged items (including tombstones marking deleted data).
6964410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
697625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey        int ageSeconds = Settings.Global.getInt(mContentResolver,
698625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.DROPBOX_AGE_SECONDS, DEFAULT_AGE_SECONDS);
699625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey        int maxFiles = Settings.Global.getInt(mContentResolver,
700625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                Settings.Global.DROPBOX_MAX_FILES, DEFAULT_MAX_FILES);
7014410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        long cutoffMillis = System.currentTimeMillis() - ageSeconds * 1000;
7024410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        while (!mAllFiles.contents.isEmpty()) {
7034410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            EntryFile entry = mAllFiles.contents.first();
7043a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor            if (entry.timestampMillis > cutoffMillis && mAllFiles.contents.size() < maxFiles) break;
7054410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7064410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            FileList tag = mFilesByTag.get(entry.tag);
7074410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (tag != null && tag.contents.remove(entry)) tag.blocks -= entry.blocks;
7084410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks;
7094410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            if (entry.file != null) entry.file.delete();
7104410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
7114410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7124410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // Compute overall quota (a fraction of available free space) in blocks.
7134410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // The quota changes dynamically based on the amount of free space;
7144410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // that way when lots of data is available we can use it, but we'll get
7154410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // out of the way if storage starts getting tight.
7164410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7174410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        long uptimeMillis = SystemClock.uptimeMillis();
7184410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (uptimeMillis > mCachedQuotaUptimeMillis + QUOTA_RESCAN_MILLIS) {
719625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey            int quotaPercent = Settings.Global.getInt(mContentResolver,
720625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                    Settings.Global.DROPBOX_QUOTA_PERCENT, DEFAULT_QUOTA_PERCENT);
721625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey            int reservePercent = Settings.Global.getInt(mContentResolver,
722625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                    Settings.Global.DROPBOX_RESERVE_PERCENT, DEFAULT_RESERVE_PERCENT);
723625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey            int quotaKb = Settings.Global.getInt(mContentResolver,
724625239a05401bbf18b04d9874cea3f82da7c29a1Jeff Sharkey                    Settings.Global.DROPBOX_QUOTA_KB, DEFAULT_QUOTA_KB);
7254410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7264410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            mStatFs.restat(mDropBoxDir.getPath());
7274410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int available = mStatFs.getAvailableBlocks();
7284410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int nonreserved = available - mStatFs.getBlockCount() * reservePercent / 100;
7294410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int maximum = quotaKb * 1024 / mBlockSize;
7304410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            mCachedQuotaBlocks = Math.min(maximum, Math.max(0, nonreserved * quotaPercent / 100));
7314410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            mCachedQuotaUptimeMillis = uptimeMillis;
7324410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
7334410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7344410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // If we're using too much space, delete old items to make room.
7354410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        //
7364410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // We trim each tag independently (this is why we keep per-tag lists).
7374410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // Space is "fairly" shared between tags -- they are all squeezed
7384410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // equally until enough space is reclaimed.
7394410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        //
7404410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // A single circular buffer (a la logcat) would be simpler, but this
7414410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // way we can handle fat/bursty data (like 1MB+ bugreports, 300KB+
7424410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // kernel crash dumps, and 100KB+ ANR reports) without swamping small,
7433a8b0c18a49c017ecfde8ebf4eb4bb5181ac6fefDan Egnor        // well-behaved data streams (event statistics, profile data, etc).
7444410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        //
7454410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // Deleted files are replaced with zero-length tombstones to mark what
7464410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        // was lost.  Tombstones are expunged by age (see above).
7474410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7484410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        if (mAllFiles.blocks > mCachedQuotaBlocks) {
7494410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // Find a fair share amount of space to limit each tag
7504410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int unsqueezed = mAllFiles.blocks, squeezed = 0;
7514410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            TreeSet<FileList> tags = new TreeSet<FileList>(mFilesByTag.values());
7524410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            for (FileList tag : tags) {
7534410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (squeezed > 0 && tag.blocks <= (mCachedQuotaBlocks - unsqueezed) / squeezed) {
7544410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    break;
7554410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
7564410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                unsqueezed -= tag.blocks;
7574410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                squeezed++;
7584410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
7594410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            int tagQuota = (mCachedQuotaBlocks - unsqueezed) / squeezed;
7604410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7614410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            // Remove old items from each tag until it meets the per-tag quota.
7624410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            for (FileList tag : tags) {
7634410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                if (mAllFiles.blocks < mCachedQuotaBlocks) break;
7644410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                while (tag.blocks > tagQuota && !tag.contents.isEmpty()) {
7654410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    EntryFile entry = tag.contents.first();
7664410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    if (tag.contents.remove(entry)) tag.blocks -= entry.blocks;
7674410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    if (mAllFiles.contents.remove(entry)) mAllFiles.blocks -= entry.blocks;
7684410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7694410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    try {
7704410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        if (entry.file != null) entry.file.delete();
7714410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                        enrollEntry(new EntryFile(mDropBoxDir, entry.tag, entry.timestampMillis));
7724410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    } catch (IOException e) {
7738a9b22056b13477f59df934928c00c58b5871c95Joe Onorato                        Slog.e(TAG, "Can't write tombstone file", e);
7744410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                    }
7754410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor                }
7764410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor            }
7774410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        }
7784410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor
7794410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor        return mCachedQuotaBlocks * mBlockSize;
7804410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor    }
7814410ec8f7cec6ab05a8c24c04fe2d5ed5d1e18dfDan Egnor}
782