Loading src/com/android/documentsui/DocumentsApplication.java +3 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ import android.net.Uri; import android.os.RemoteException; import android.text.format.DateUtils; import com.android.documentsui.clipping.ClipStorage; import com.android.documentsui.clipping.DocumentClipper; public class DocumentsApplication extends Application { private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS; Loading src/com/android/documentsui/FilesActivity.java +1 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.view.MenuItem; import com.android.documentsui.MenuManager.DirectoryDetails; import com.android.documentsui.OperationDialogFragment.DialogType; import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.clipping.DocumentClipper; import com.android.documentsui.dirlist.AnimationView; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.dirlist.FragmentTuner; Loading src/com/android/documentsui/ClipStorage.java→src/com/android/documentsui/clipping/ClipStorage.java +8 −100 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.documentsui; package com.android.documentsui.clipping; import android.content.SharedPreferences; import android.net.Uri; Loading @@ -24,15 +24,13 @@ import android.system.ErrnoException; import android.system.Os; import android.util.Log; import com.android.documentsui.Files; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileLock; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.concurrent.TimeUnit; /** Loading @@ -45,7 +43,7 @@ public final class ClipStorage { public static final int NO_SELECTION_TAG = -1; static final String PREF_NAME = "ClipStoragePref"; public static final String PREF_NAME = "ClipStoragePref"; @VisibleForTesting static final int NUM_OF_SLOTS = 20; Loading Loading @@ -90,7 +88,7 @@ public final class ClipStorage { * file may be overwritten.</li> * </ul> */ public synchronized int claimStorageSlot() { synchronized int claimStorageSlot() { int curPos = mNextPos; for (int i = 0; i < NUM_OF_SLOTS; ++i, curPos = (curPos + 1) % NUM_OF_SLOTS) { createSlotFile(curPos); Loading Loading @@ -167,9 +165,9 @@ public final class ClipStorage { /** * Returns a Reader. Callers must close the reader when finished. */ public Reader createReader(File file) throws IOException { ClipStorageReader createReader(File file) throws IOException { assert(file.getParentFile().getParentFile().equals(mOutDir)); return new Reader(file); return new ClipStorageReader(file); } private File toSlotDataFile(int pos) { Loading @@ -186,7 +184,7 @@ public final class ClipStorage { /** * Provides initialization of the clip data storage directory. */ static File prepareStorage(File cacheDir) { public static File prepareStorage(File cacheDir) { File clipDir = getClipDir(cacheDir); clipDir.mkdir(); Loading @@ -198,96 +196,6 @@ public final class ClipStorage { return new File(cacheDir, "clippings"); } static final class Reader implements Iterable<Uri>, Closeable { /** * FileLock can't be held multiple times in a single JVM, but it's possible to have multiple * readers reading the same clip file. Share the FileLock here so that it can be released * when it's not needed. */ private static final Map<String, FileLockEntry> sLocks = new HashMap<>(); private final String mCanonicalPath; private final Scanner mScanner; private Reader(File file) throws IOException { FileInputStream inStream = new FileInputStream(file); mScanner = new Scanner(inStream); mCanonicalPath = file.getCanonicalPath(); // Resolve symlink synchronized (sLocks) { if (sLocks.containsKey(mCanonicalPath)) { // Read lock is already held by someone in this JVM, just increment the ref // count. sLocks.get(mCanonicalPath).mCount++; } else { // No map entry, need to lock the file so it won't pass this line until the // corresponding writer is done writing. FileLock lock = inStream.getChannel().lock(0L, Long.MAX_VALUE, true); sLocks.put(mCanonicalPath, new FileLockEntry(1, lock, mScanner)); } } } @Override public Iterator iterator() { return new Iterator(mScanner); } @Override public void close() throws IOException { synchronized (sLocks) { FileLockEntry ref = sLocks.get(mCanonicalPath); assert(ref.mCount > 0); if (--ref.mCount == 0) { // If ref count is 0 now, then there is no one who needs to hold the read lock. // Release the lock, and remove the entry. ref.mLock.release(); ref.mScanner.close(); sLocks.remove(mCanonicalPath); } if (mScanner != ref.mScanner) { mScanner.close(); } } } } private static final class Iterator implements java.util.Iterator { private final Scanner mScanner; private Iterator(Scanner scanner) { mScanner = scanner; } @Override public boolean hasNext() { return mScanner.hasNextLine(); } @Override public Uri next() { String line = mScanner.nextLine(); return Uri.parse(line); } } private static final class FileLockEntry { private int mCount; private FileLock mLock; // We need to keep this scanner here because if the scanner is closed, the file lock is // closed too. private Scanner mScanner; private FileLockEntry(int count, FileLock lock, Scanner scanner) { mCount = count; mLock = lock; mScanner = scanner; } } private static final class Writer implements Closeable { private final FileOutputStream mOut; Loading src/com/android/documentsui/clipping/ClipStorageReader.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.documentsui.clipping; import android.net.Uri; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.channels.FileLock; import java.util.HashMap; import java.util.Map; import java.util.Scanner; /** * Reader class used to read uris from clip files stored in {@link ClipStorage}. It provides * synchronization within a single process as an addition to {@link FileLock} which is for * cross-process synchronization. */ class ClipStorageReader implements Iterable<Uri>, Closeable { /** * FileLock can't be held multiple times in a single JVM, but it's possible to have multiple * readers reading the same clip file. Share the FileLock here so that it can be released * when it's not needed. */ private static final Map<String, FileLockEntry> sLocks = new HashMap<>(); private final String mCanonicalPath; private final Scanner mScanner; ClipStorageReader(File file) throws IOException { FileInputStream inStream = new FileInputStream(file); mScanner = new Scanner(inStream); mCanonicalPath = file.getCanonicalPath(); // Resolve symlink synchronized (sLocks) { if (sLocks.containsKey(mCanonicalPath)) { // Read lock is already held by someone in this JVM, just increment the ref // count. sLocks.get(mCanonicalPath).mCount++; } else { // No map entry, need to lock the file so it won't pass this line until the // corresponding writer is done writing. FileLock lock = inStream.getChannel().lock(0L, Long.MAX_VALUE, true); sLocks.put(mCanonicalPath, new FileLockEntry(1, lock, mScanner)); } } } @Override public Iterator iterator() { return new Iterator(mScanner); } @Override public void close() throws IOException { FileLockEntry ref; synchronized (sLocks) { ref = sLocks.get(mCanonicalPath); assert(ref.mCount > 0); if (--ref.mCount == 0) { // If ref count is 0 now, then there is no one who needs to hold the read lock. // Release the lock, and remove the entry. ref.mLock.release(); ref.mScanner.close(); sLocks.remove(mCanonicalPath); } } if (mScanner != ref.mScanner) { mScanner.close(); } } private static final class Iterator implements java.util.Iterator { private final Scanner mScanner; private Iterator(Scanner scanner) { mScanner = scanner; } @Override public boolean hasNext() { return mScanner.hasNextLine(); } @Override public Uri next() { String line = mScanner.nextLine(); return Uri.parse(line); } } private static final class FileLockEntry { private final FileLock mLock; // We need to keep this scanner here because if the scanner is closed, the file lock is // closed too. private final Scanner mScanner; private int mCount; private FileLockEntry(int count, FileLock lock, Scanner scanner) { mCount = count; mLock = lock; mScanner = scanner; } } } src/com/android/documentsui/DocumentClipper.java→src/com/android/documentsui/clipping/DocumentClipper.java +4 −3 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2016 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. Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.documentsui; package com.android.documentsui.clipping; import android.content.ClipData; import android.content.ClipDescription; Loading @@ -27,6 +27,7 @@ import android.provider.DocumentsContract; import android.support.annotation.Nullable; import android.util.Log; import com.android.documentsui.Shared; import com.android.documentsui.dirlist.MultiSelectManager.Selection; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; Loading Loading @@ -59,7 +60,7 @@ public final class DocumentClipper { private final ClipStorage mClipStorage; private final ClipboardManager mClipboard; DocumentClipper(Context context, ClipStorage storage) { public DocumentClipper(Context context, ClipStorage storage) { mContext = context; mClipStorage = storage; mClipboard = context.getSystemService(ClipboardManager.class); Loading Loading
src/com/android/documentsui/DocumentsApplication.java +3 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,9 @@ import android.net.Uri; import android.os.RemoteException; import android.text.format.DateUtils; import com.android.documentsui.clipping.ClipStorage; import com.android.documentsui.clipping.DocumentClipper; public class DocumentsApplication extends Application { private static final long PROVIDER_ANR_TIMEOUT = 20 * DateUtils.SECOND_IN_MILLIS; Loading
src/com/android/documentsui/FilesActivity.java +1 −0 Original line number Diff line number Diff line Loading @@ -39,6 +39,7 @@ import android.view.MenuItem; import com.android.documentsui.MenuManager.DirectoryDetails; import com.android.documentsui.OperationDialogFragment.DialogType; import com.android.documentsui.RecentsProvider.ResumeColumns; import com.android.documentsui.clipping.DocumentClipper; import com.android.documentsui.dirlist.AnimationView; import com.android.documentsui.dirlist.DirectoryFragment; import com.android.documentsui.dirlist.FragmentTuner; Loading
src/com/android/documentsui/ClipStorage.java→src/com/android/documentsui/clipping/ClipStorage.java +8 −100 Original line number Diff line number Diff line Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.documentsui; package com.android.documentsui.clipping; import android.content.SharedPreferences; import android.net.Uri; Loading @@ -24,15 +24,13 @@ import android.system.ErrnoException; import android.system.Os; import android.util.Log; import com.android.documentsui.Files; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.nio.channels.FileLock; import java.util.HashMap; import java.util.Map; import java.util.Scanner; import java.util.concurrent.TimeUnit; /** Loading @@ -45,7 +43,7 @@ public final class ClipStorage { public static final int NO_SELECTION_TAG = -1; static final String PREF_NAME = "ClipStoragePref"; public static final String PREF_NAME = "ClipStoragePref"; @VisibleForTesting static final int NUM_OF_SLOTS = 20; Loading Loading @@ -90,7 +88,7 @@ public final class ClipStorage { * file may be overwritten.</li> * </ul> */ public synchronized int claimStorageSlot() { synchronized int claimStorageSlot() { int curPos = mNextPos; for (int i = 0; i < NUM_OF_SLOTS; ++i, curPos = (curPos + 1) % NUM_OF_SLOTS) { createSlotFile(curPos); Loading Loading @@ -167,9 +165,9 @@ public final class ClipStorage { /** * Returns a Reader. Callers must close the reader when finished. */ public Reader createReader(File file) throws IOException { ClipStorageReader createReader(File file) throws IOException { assert(file.getParentFile().getParentFile().equals(mOutDir)); return new Reader(file); return new ClipStorageReader(file); } private File toSlotDataFile(int pos) { Loading @@ -186,7 +184,7 @@ public final class ClipStorage { /** * Provides initialization of the clip data storage directory. */ static File prepareStorage(File cacheDir) { public static File prepareStorage(File cacheDir) { File clipDir = getClipDir(cacheDir); clipDir.mkdir(); Loading @@ -198,96 +196,6 @@ public final class ClipStorage { return new File(cacheDir, "clippings"); } static final class Reader implements Iterable<Uri>, Closeable { /** * FileLock can't be held multiple times in a single JVM, but it's possible to have multiple * readers reading the same clip file. Share the FileLock here so that it can be released * when it's not needed. */ private static final Map<String, FileLockEntry> sLocks = new HashMap<>(); private final String mCanonicalPath; private final Scanner mScanner; private Reader(File file) throws IOException { FileInputStream inStream = new FileInputStream(file); mScanner = new Scanner(inStream); mCanonicalPath = file.getCanonicalPath(); // Resolve symlink synchronized (sLocks) { if (sLocks.containsKey(mCanonicalPath)) { // Read lock is already held by someone in this JVM, just increment the ref // count. sLocks.get(mCanonicalPath).mCount++; } else { // No map entry, need to lock the file so it won't pass this line until the // corresponding writer is done writing. FileLock lock = inStream.getChannel().lock(0L, Long.MAX_VALUE, true); sLocks.put(mCanonicalPath, new FileLockEntry(1, lock, mScanner)); } } } @Override public Iterator iterator() { return new Iterator(mScanner); } @Override public void close() throws IOException { synchronized (sLocks) { FileLockEntry ref = sLocks.get(mCanonicalPath); assert(ref.mCount > 0); if (--ref.mCount == 0) { // If ref count is 0 now, then there is no one who needs to hold the read lock. // Release the lock, and remove the entry. ref.mLock.release(); ref.mScanner.close(); sLocks.remove(mCanonicalPath); } if (mScanner != ref.mScanner) { mScanner.close(); } } } } private static final class Iterator implements java.util.Iterator { private final Scanner mScanner; private Iterator(Scanner scanner) { mScanner = scanner; } @Override public boolean hasNext() { return mScanner.hasNextLine(); } @Override public Uri next() { String line = mScanner.nextLine(); return Uri.parse(line); } } private static final class FileLockEntry { private int mCount; private FileLock mLock; // We need to keep this scanner here because if the scanner is closed, the file lock is // closed too. private Scanner mScanner; private FileLockEntry(int count, FileLock lock, Scanner scanner) { mCount = count; mLock = lock; mScanner = scanner; } } private static final class Writer implements Closeable { private final FileOutputStream mOut; Loading
src/com/android/documentsui/clipping/ClipStorageReader.java 0 → 100644 +125 −0 Original line number Diff line number Diff line /* * Copyright (C) 2016 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.documentsui.clipping; import android.net.Uri; import java.io.Closeable; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.nio.channels.FileLock; import java.util.HashMap; import java.util.Map; import java.util.Scanner; /** * Reader class used to read uris from clip files stored in {@link ClipStorage}. It provides * synchronization within a single process as an addition to {@link FileLock} which is for * cross-process synchronization. */ class ClipStorageReader implements Iterable<Uri>, Closeable { /** * FileLock can't be held multiple times in a single JVM, but it's possible to have multiple * readers reading the same clip file. Share the FileLock here so that it can be released * when it's not needed. */ private static final Map<String, FileLockEntry> sLocks = new HashMap<>(); private final String mCanonicalPath; private final Scanner mScanner; ClipStorageReader(File file) throws IOException { FileInputStream inStream = new FileInputStream(file); mScanner = new Scanner(inStream); mCanonicalPath = file.getCanonicalPath(); // Resolve symlink synchronized (sLocks) { if (sLocks.containsKey(mCanonicalPath)) { // Read lock is already held by someone in this JVM, just increment the ref // count. sLocks.get(mCanonicalPath).mCount++; } else { // No map entry, need to lock the file so it won't pass this line until the // corresponding writer is done writing. FileLock lock = inStream.getChannel().lock(0L, Long.MAX_VALUE, true); sLocks.put(mCanonicalPath, new FileLockEntry(1, lock, mScanner)); } } } @Override public Iterator iterator() { return new Iterator(mScanner); } @Override public void close() throws IOException { FileLockEntry ref; synchronized (sLocks) { ref = sLocks.get(mCanonicalPath); assert(ref.mCount > 0); if (--ref.mCount == 0) { // If ref count is 0 now, then there is no one who needs to hold the read lock. // Release the lock, and remove the entry. ref.mLock.release(); ref.mScanner.close(); sLocks.remove(mCanonicalPath); } } if (mScanner != ref.mScanner) { mScanner.close(); } } private static final class Iterator implements java.util.Iterator { private final Scanner mScanner; private Iterator(Scanner scanner) { mScanner = scanner; } @Override public boolean hasNext() { return mScanner.hasNextLine(); } @Override public Uri next() { String line = mScanner.nextLine(); return Uri.parse(line); } } private static final class FileLockEntry { private final FileLock mLock; // We need to keep this scanner here because if the scanner is closed, the file lock is // closed too. private final Scanner mScanner; private int mCount; private FileLockEntry(int count, FileLock lock, Scanner scanner) { mCount = count; mLock = lock; mScanner = scanner; } } }
src/com/android/documentsui/DocumentClipper.java→src/com/android/documentsui/clipping/DocumentClipper.java +4 −3 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * Copyright (C) 2016 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. Loading @@ -14,7 +14,7 @@ * limitations under the License. */ package com.android.documentsui; package com.android.documentsui.clipping; import android.content.ClipData; import android.content.ClipDescription; Loading @@ -27,6 +27,7 @@ import android.provider.DocumentsContract; import android.support.annotation.Nullable; import android.util.Log; import com.android.documentsui.Shared; import com.android.documentsui.dirlist.MultiSelectManager.Selection; import com.android.documentsui.model.DocumentInfo; import com.android.documentsui.model.DocumentStack; Loading Loading @@ -59,7 +60,7 @@ public final class DocumentClipper { private final ClipStorage mClipStorage; private final ClipboardManager mClipboard; DocumentClipper(Context context, ClipStorage storage) { public DocumentClipper(Context context, ClipStorage storage) { mContext = context; mClipStorage = storage; mClipboard = context.getSystemService(ClipboardManager.class); Loading