aboutsummaryrefslogtreecommitdiffstats
path: root/src/com/cyanogenmod/filemanager/console
diff options
context:
space:
mode:
authorJorge Ruesga <jorge@ruesga.com>2014-10-28 03:26:42 +0100
committerJorge Ruesga <jorge@ruesga.com>2014-11-10 23:16:25 +0000
commit877d4660622ebcaa992f05396237169c289470c4 (patch)
treeb254cb04803096aa9695f42e1bcc914ba09e12fd /src/com/cyanogenmod/filemanager/console
parentfcb4908c2c949f55ec966e09a0a91210dff2ca3f (diff)
downloadandroid_packages_apps_CMFileManager-877d4660622ebcaa992f05396237169c289470c4.tar.gz
android_packages_apps_CMFileManager-877d4660622ebcaa992f05396237169c289470c4.tar.bz2
android_packages_apps_CMFileManager-877d4660622ebcaa992f05396237169c289470c4.zip
cmfm: secure storage and other improvements
This patch adds support for virtual filesystems and implements a SecureStorage filesystem (a password protected area) mounted in /storage or /sdcard/storage (in chrooted environments). Also includes a better print support and a cleanup of the code and design of the menu drawer. Bump version to 2.0.0 Required: https://github.com/jruesga/android_external_libtruezip located in external/libtruezip Patchset 4: Fix selection of unmounted virtual storages. Fix actions on virtual mount points folders. Fix strings and typos. Change drop for delete secure storage. Patchset 5: Move actionbar buttons to navigation drawer Remove history position Patchset 6: Update theme preview images Fix filesystem status image on theme change Patchset 7: Fix binary file detection in editor (including unicode files) Patchset 8: Fix unsafe operations in virtual mountpoint logic Patchset 9: Rebase Change-Id: I65511352ca649dcbf238c8b07cf8c22465296e8e Signed-off-by: Jorge Ruesga <jorge@ruesga.com>
Diffstat (limited to 'src/com/cyanogenmod/filemanager/console')
-rw-r--r--src/com/cyanogenmod/filemanager/console/AuthenticationFailedException.java36
-rw-r--r--src/com/cyanogenmod/filemanager/console/CancelledOperationException.java34
-rw-r--r--src/com/cyanogenmod/filemanager/console/Console.java13
-rw-r--r--src/com/cyanogenmod/filemanager/console/VirtualConsole.java144
-rw-r--r--src/com/cyanogenmod/filemanager/console/VirtualMountPointConsole.java292
-rw-r--r--src/com/cyanogenmod/filemanager/console/java/JavaConsole.java122
-rw-r--r--src/com/cyanogenmod/filemanager/console/remote/RemoteConsole.java145
-rw-r--r--src/com/cyanogenmod/filemanager/console/secure/SecureConsole.java640
-rw-r--r--src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriver.java37
-rw-r--r--src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriverProvider.java64
-rw-r--r--src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyManagerProvider.java101
-rw-r--r--src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyPromptDialog.java399
-rw-r--r--src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java15
13 files changed, 1920 insertions, 122 deletions
diff --git a/src/com/cyanogenmod/filemanager/console/AuthenticationFailedException.java b/src/com/cyanogenmod/filemanager/console/AuthenticationFailedException.java
new file mode 100644
index 00000000..795111e9
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/AuthenticationFailedException.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod 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.cyanogenmod.filemanager.console;
+
+import java.io.IOException;
+
+/**
+ * An exception that indicates that the operation failed because an authentication failure
+ */
+public class AuthenticationFailedException extends IOException {
+ private static final long serialVersionUID = -2199496556437722726L;
+
+ /**
+ * Constructor of <code>AuthenticationFailedException</code>.
+ *
+ * @param msg The associated message
+ */
+ public AuthenticationFailedException(String msg) {
+ super(msg);
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/console/CancelledOperationException.java b/src/com/cyanogenmod/filemanager/console/CancelledOperationException.java
new file mode 100644
index 00000000..e19d0dc3
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/CancelledOperationException.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2012 The CyanogenMod 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.cyanogenmod.filemanager.console;
+
+import java.io.IOException;
+
+/**
+ * An exception that indicates that the operation was cancelled
+ */
+public class CancelledOperationException extends IOException {
+ private static final long serialVersionUID = 2999554355110192173L;
+
+ /**
+ * Constructor of <code>CancelledOperationException</code>.
+ */
+ public CancelledOperationException() {
+ super();
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/console/Console.java b/src/com/cyanogenmod/filemanager/console/Console.java
index ba28db55..6404431f 100644
--- a/src/com/cyanogenmod/filemanager/console/Console.java
+++ b/src/com/cyanogenmod/filemanager/console/Console.java
@@ -15,6 +15,8 @@
*/
package com.cyanogenmod.filemanager.console;
+import android.content.Context;
+
import com.cyanogenmod.filemanager.commands.AsyncResultExecutable;
import com.cyanogenmod.filemanager.commands.Executable;
import com.cyanogenmod.filemanager.commands.ExecutableFactory;
@@ -46,7 +48,7 @@ public abstract class Console
*
* @return boolean If the console has to trace
*/
- public boolean isTrace() {
+ public final boolean isTrace() {
return this.mTrace;
}
@@ -111,6 +113,7 @@ public abstract class Console
* Method for execute a command in the operating system layer.
*
* @param executable The executable command to be executed
+ * @param ctx The current context
* @throws ConsoleAllocException If the console is not allocated
* @throws InsufficientPermissionsException If an operation requires elevated permissions
* @throws NoSuchFileOrDirectory If the file or directory was not found
@@ -118,10 +121,14 @@ public abstract class Console
* @throws CommandNotFoundException If the executable program was not found
* @throws ExecutionException If the operation returns a invalid exit code
* @throws ReadOnlyFilesystemException If the operation writes in a read-only filesystem
+ * @throws CancelledOperationException If the operation was cancelled
+ * @throws AuthenticationFailedException If the operation failed because an
+ * authentication failure
+ * @throws AuthenticationFailedException
*/
- public abstract void execute(final Executable executable)
+ public abstract void execute(final Executable executable, final Context ctx)
throws ConsoleAllocException, InsufficientPermissionsException, NoSuchFileOrDirectory,
OperationTimeoutException, ExecutionException, CommandNotFoundException,
- ReadOnlyFilesystemException;
+ ReadOnlyFilesystemException, CancelledOperationException, AuthenticationFailedException;
}
diff --git a/src/com/cyanogenmod/filemanager/console/VirtualConsole.java b/src/com/cyanogenmod/filemanager/console/VirtualConsole.java
new file mode 100644
index 00000000..8512bc77
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/VirtualConsole.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright (C) 20124 The CyanogenMod 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.cyanogenmod.filemanager.console;
+
+import android.content.Context;
+import android.util.Log;
+
+import com.cyanogenmod.filemanager.commands.SIGNAL;
+import com.cyanogenmod.filemanager.console.Console;
+import com.cyanogenmod.filemanager.console.ConsoleAllocException;
+import com.cyanogenmod.filemanager.model.Identity;
+import com.cyanogenmod.filemanager.util.AIDHelper;
+
+/**
+ * An abstract base class for all the virtual {@link Console}.
+ */
+public abstract class VirtualConsole extends Console {
+
+ public static final String TAG = "VirtualConsole";
+
+ private boolean mActive;
+ private final Context mCtx;
+ private final Identity mIdentity;
+
+ /**
+ * Constructor of <code>VirtualConsole</code>
+ *
+ * @param ctx The current context
+ */
+ public VirtualConsole(Context ctx) {
+ super();
+ mCtx = ctx;
+ mIdentity = AIDHelper.createVirtualIdentity();
+ }
+
+ public abstract String getName();
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void alloc() throws ConsoleAllocException {
+ try {
+ if (isTrace()) {
+ Log.v(TAG, "Allocating " + getName() + " console");
+ }
+ mActive = true;
+ } catch (Exception e) {
+ Log.e(TAG, "Failed to allocate " + getName() + " console", e);
+ throw new ConsoleAllocException("failed to build console", e);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void dealloc() {
+ if (isTrace()) {
+ Log.v(TAG, "Deallocating Java console");
+ }
+ mActive = true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void realloc() throws ConsoleAllocException {
+ dealloc();
+ alloc();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Identity getIdentity() {
+ return mIdentity;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isPrivileged() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isActive() {
+ return mActive;
+ }
+
+ /**
+ * Method that returns the current context
+ *
+ * @return Context The current context
+ */
+ public Context getCtx() {
+ return mCtx;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onCancel() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onSendSignal(SIGNAL signal) {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean onEnd() {
+ return false;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/console/VirtualMountPointConsole.java b/src/com/cyanogenmod/filemanager/console/VirtualMountPointConsole.java
new file mode 100644
index 00000000..ba730606
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/VirtualMountPointConsole.java
@@ -0,0 +1,292 @@
+/*
+ * Copyright (C) 20124 The CyanogenMod 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.cyanogenmod.filemanager.console;
+
+import android.content.Context;
+import android.os.Environment;
+import android.os.SystemClock;
+
+import com.cyanogenmod.filemanager.FileManagerApplication;
+import com.cyanogenmod.filemanager.R;
+import com.cyanogenmod.filemanager.console.secure.SecureConsole;
+import com.cyanogenmod.filemanager.model.Directory;
+import com.cyanogenmod.filemanager.model.DiskUsage;
+import com.cyanogenmod.filemanager.model.Identity;
+import com.cyanogenmod.filemanager.model.MountPoint;
+import com.cyanogenmod.filemanager.model.Permissions;
+import com.cyanogenmod.filemanager.preferences.AccessMode;
+import com.cyanogenmod.filemanager.util.AIDHelper;
+import com.cyanogenmod.filemanager.util.FileHelper;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * An abstract base class for of a {@link VirtualConsole} that has a virtual mount point
+ * in the filesystem.
+ */
+public abstract class VirtualMountPointConsole extends VirtualConsole {
+
+ private static final String DEFAULT_STORAGE_NAME = "storage";
+
+// private static File sVirtualStorageDir;
+
+ private static List<VirtualMountPointConsole> sVirtualConsoles;
+ private static Identity sVirtualIdentity;
+ private static Permissions sVirtualFolderPermissions;
+
+ public VirtualMountPointConsole(Context ctx) {
+ super(ctx);
+ }
+
+ /**
+ * Should return the name of the mount point name
+ *
+ * @return String The name of the mount point name of this console.
+ */
+ public abstract String getMountPointName();
+
+ /**
+ * Method that returns if the console is secure
+ *
+ * @return boolean If the console is secure
+ */
+ public abstract boolean isSecure();
+
+ /**
+ * Method that returns if the console is remote
+ *
+ * @return boolean If the console is remote
+ */
+ public abstract boolean isRemote();
+
+ /**
+ * Method that returns if the console is mounted
+ *
+ * @return boolean If the console is mounted
+ */
+ public abstract boolean isMounted();
+
+ /**
+ * Method that unmounts the filesystem
+ *
+ * @return boolean If the filesystem was unmounted
+ */
+ public abstract boolean unmount();
+
+ /**
+ * Returns the mountpoints for the console
+ *
+ * @return List<MountPoint> The list of mountpoints handled by the console
+ */
+ public abstract List<MountPoint> getMountPoints();
+
+ /**
+ * Returns the disk usage of every mountpoint for the console
+ *
+ * @return List<DiskUsage> The list of disk usage of the mountpoints handled by the console
+ */
+ public abstract List<DiskUsage> getDiskUsage();
+
+ /**
+ * Returns the disk usage of the path
+ *
+ * @param path The path to check
+ * @return DiskUsage The disk usage for the passed path
+ */
+ public abstract DiskUsage getDiskUsage(String path);
+
+ /**
+ * Method that register all the implemented virtual consoles. This method should
+ * be called only once on the application instantiation.
+ *
+ * @param context The current context
+ */
+ public static void registerVirtualConsoles(Context context) {
+ if (sVirtualConsoles != null) return;
+ sVirtualConsoles = new ArrayList<VirtualMountPointConsole>();
+ sVirtualIdentity = AIDHelper.createVirtualIdentity();
+ sVirtualFolderPermissions = Permissions.createDefaultFolderPermissions();
+
+ int bufferSize = context.getResources().getInteger(R.integer.buffer_size);
+
+ // Register every known virtual mountable console
+ sVirtualConsoles.add(SecureConsole.getInstance(context, bufferSize));
+ // TODO Add remote consoles. Not ready for now.
+ // sVirtualConsoles.add(new RemoteConsole(context));
+ }
+
+ /**
+ * Method that returns the virtual storage directory
+ * @return
+ */
+ private static File getVirtualStorageDir() {
+ final Context context = FileManagerApplication.getInstance().getApplicationContext();
+ File dir = new File(context.getString(R.string.virtual_storage_dir));
+ AccessMode mode = FileManagerApplication.getAccessMode();
+ if (mode.equals(AccessMode.SAFE) || !dir.isDirectory()) {
+ // Chroot environment (create a folder inside the external storage)
+ return getChrootedVirtualStorageDir();
+ }
+ return dir;
+ }
+
+ /**
+ * Method that returns the chrooted virtual storage directory
+ *
+ * @return File The Virtual storage directory
+ */
+ private static File getChrootedVirtualStorageDir() {
+ File root = new File(Environment.getExternalStorageDirectory(), DEFAULT_STORAGE_NAME);
+ root.mkdir();
+ return root;
+ }
+
+ /**
+ * Method that list all the virtual directories
+ *
+ * @return List<Directory> The list of virtual directories
+ */
+ public static List<Directory> getVirtualMountableDirectories() {
+ final Date date = new Date(System.currentTimeMillis() - SystemClock.elapsedRealtime());
+ List<Directory> directories = new ArrayList<Directory>();
+ for (VirtualMountPointConsole console : sVirtualConsoles) {
+ File dir = null;
+ do {
+ dir = console.getVirtualMountPoint();
+ } while (dir.getParentFile() != null && !isVirtualStorageDir(dir.getParent()));
+
+ if (dir != null) {
+ Directory directory = new Directory(
+ dir.getName(),
+ getVirtualStorageDir().getAbsolutePath(),
+ sVirtualIdentity.getUser(),
+ sVirtualIdentity.getGroup(),
+ sVirtualFolderPermissions,
+ date, date, date);
+ directory.setSecure(console.isSecure());
+ directory.setRemote(console.isRemote());
+
+ if (!directories.contains(directory)) {
+ directories.add(directory);
+ }
+ }
+ }
+ return directories;
+ }
+
+ /**
+ * Method that returns the virtual mountpoints of every register console
+ * @return
+ */
+ public static List<MountPoint> getVirtualMountPoints() {
+ List<MountPoint> mountPoints = new ArrayList<MountPoint>();
+ for (VirtualMountPointConsole console : sVirtualConsoles) {
+ mountPoints.addAll(console.getMountPoints());
+ }
+ return mountPoints;
+ }
+
+ /**
+ * Method that returns the virtual disk usage of the mountpoints of every register console
+ * @return
+ */
+ public static List<DiskUsage> getVirtualDiskUsage() {
+ List<DiskUsage> diskUsage = new ArrayList<DiskUsage>();
+ for (VirtualMountPointConsole console : sVirtualConsoles) {
+ diskUsage.addAll(console.getDiskUsage());
+ }
+ return diskUsage;
+ }
+
+ /**
+ * Returns if the passed directory is the current virtual storage directory
+ *
+ * @param directory The directory to check
+ * @return boolean If is the current virtual storage directory
+ */
+ public static boolean isVirtualStorageDir(String directory) {
+ return getVirtualStorageDir().equals(new File(directory));
+ }
+
+ /**
+ * Returns if the passed resource belongs to a virtual filesystem
+ *
+ * @param path The path to check
+ * @return boolean If is the resource belongs to a virtual filesystem
+ */
+ public static boolean isVirtualStorageResource(String path) {
+ for (VirtualMountPointConsole console : sVirtualConsoles) {
+ if (FileHelper.belongsToDirectory(new File(path), console.getVirtualMountPoint())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Method that returns the virtual console for the path or null if the path
+ * is not a virtual filesystem
+ *
+ * @param path the path to check
+ * @return VirtualMountPointConsole The found console
+ */
+ public static VirtualMountPointConsole getVirtualConsoleForPath(String path) {
+ File file = new File(path);
+ for (VirtualMountPointConsole console : sVirtualConsoles) {
+ if (FileHelper.belongsToDirectory(file, console.getVirtualMountPoint())) {
+ return console;
+ }
+ }
+ return null;
+ }
+
+ public static List<Console> getVirtualConsoleForSearchPath(String path) {
+ List<Console> consoles = new ArrayList<Console>();
+ File dir = new File(path);
+ for (VirtualMountPointConsole console : sVirtualConsoles) {
+ if (FileHelper.belongsToDirectory(console.getVirtualMountPoint(), dir)) {
+ // Only mount consoles can participate in the search
+ if (console.isMounted()) {
+ consoles.add(console);
+ }
+ }
+ }
+ return consoles;
+ }
+
+ /**
+ * Returns if the passed directory is the virtual mountpoint directory of the virtual console
+ *
+ * @param directory The directory to check
+ * @return boolean If is the virtual mountpoint directory of the virtual console
+ */
+ public boolean isVirtualMountPointDir(String directory) {
+ return getVirtualMountPoint().equals(new File(directory));
+ }
+
+ /**
+ * Method that returns the virtual mount point for this console
+ *
+ * @return String The virtual mount point
+ */
+ public final File getVirtualMountPoint() {
+ return new File(getVirtualStorageDir(), getMountPointName());
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/console/java/JavaConsole.java b/src/com/cyanogenmod/filemanager/console/java/JavaConsole.java
index daf30525..f59f7461 100644
--- a/src/com/cyanogenmod/filemanager/console/java/JavaConsole.java
+++ b/src/com/cyanogenmod/filemanager/console/java/JavaConsole.java
@@ -17,43 +17,31 @@
package com.cyanogenmod.filemanager.console.java;
import android.content.Context;
-import android.os.Process;
import android.util.Log;
import com.cyanogenmod.filemanager.commands.Executable;
import com.cyanogenmod.filemanager.commands.ExecutableFactory;
-import com.cyanogenmod.filemanager.commands.SIGNAL;
import com.cyanogenmod.filemanager.commands.java.JavaExecutableFactory;
import com.cyanogenmod.filemanager.commands.java.Program;
import com.cyanogenmod.filemanager.console.CommandNotFoundException;
-import com.cyanogenmod.filemanager.console.Console;
import com.cyanogenmod.filemanager.console.ConsoleAllocException;
import com.cyanogenmod.filemanager.console.ExecutionException;
import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
import com.cyanogenmod.filemanager.console.OperationTimeoutException;
import com.cyanogenmod.filemanager.console.ReadOnlyFilesystemException;
-import com.cyanogenmod.filemanager.model.AID;
-import com.cyanogenmod.filemanager.model.Group;
-import com.cyanogenmod.filemanager.model.Identity;
-import com.cyanogenmod.filemanager.model.User;
-import com.cyanogenmod.filemanager.util.AIDHelper;
-
-import java.util.ArrayList;
+import com.cyanogenmod.filemanager.console.VirtualConsole;
/**
- * An implementation of a {@link Console} based on a java implementation.<br/>
+ * An implementation of a {@link VirtualConsole} based on a java implementation.<br/>
* <br/>
* This console is a non-privileged console an many of the functionality is not implemented
* because can't be obtain from java api.
*/
-public final class JavaConsole extends Console {
+public final class JavaConsole extends VirtualConsole {
private static final String TAG = "JavaConsole"; //$NON-NLS-1$
- private boolean mActive;
-
- private final Context mCtx;
private final int mBufferSize;
/**
@@ -63,45 +51,17 @@ public final class JavaConsole extends Console {
* @param bufferSize The buffer size
*/
public JavaConsole(Context ctx, int bufferSize) {
- super();
- this.mCtx = ctx;
+ super(ctx);
this.mBufferSize = bufferSize;
}
- /**
- * {@inheritDoc}
- */
- @Override
- public void alloc() throws ConsoleAllocException {
- try {
- if (isTrace()) {
- Log.v(TAG, "Allocating Java console"); //$NON-NLS-1$
- }
- this.mActive = true;
- } catch (Exception e) {
- Log.e(TAG, "Failed to allocate Java console", e); //$NON-NLS-1$
- throw new ConsoleAllocException("failed to build console", e); //$NON-NLS-1$
- }
- }
/**
* {@inheritDoc}
*/
@Override
- public void dealloc() {
- if (isTrace()) {
- Log.v(TAG, "Deallocating Java console"); //$NON-NLS-1$
- }
- this.mActive = true;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void realloc() throws ConsoleAllocException {
- dealloc();
- alloc();
+ public String getName() {
+ return "Java";
}
/**
@@ -116,48 +76,10 @@ public final class JavaConsole extends Console {
* {@inheritDoc}
*/
@Override
- public Identity getIdentity() {
- AID aid = AIDHelper.getAID(Process.myUid());
- if (aid == null) return null;
- return new Identity(
- new User(aid.getId(), aid.getName()),
- new Group(aid.getId(), aid.getName()),
- new ArrayList<Group>());
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isPrivileged() {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean isActive() {
- return this.mActive;
- }
-
- /**
- * Method that returns the current context
- *
- * @return Context The current context
- */
- public Context getCtx() {
- return this.mCtx;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public synchronized void execute(Executable executable) throws ConsoleAllocException,
- InsufficientPermissionsException, NoSuchFileOrDirectory,
- OperationTimeoutException, ExecutionException,
- CommandNotFoundException, ReadOnlyFilesystemException {
+ public synchronized void execute(Executable executable, Context ctx)
+ throws ConsoleAllocException, InsufficientPermissionsException, NoSuchFileOrDirectory,
+ OperationTimeoutException, ExecutionException, CommandNotFoundException,
+ ReadOnlyFilesystemException {
// Check that the program is a java program
try {
Program p = (Program)executable;
@@ -201,28 +123,4 @@ public final class JavaConsole extends Console {
}
}
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onCancel() {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onSendSignal(SIGNAL signal) {
- return false;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean onEnd() {
- return false;
- }
-
} \ No newline at end of file
diff --git a/src/com/cyanogenmod/filemanager/console/remote/RemoteConsole.java b/src/com/cyanogenmod/filemanager/console/remote/RemoteConsole.java
new file mode 100644
index 00000000..a57b5d51
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/remote/RemoteConsole.java
@@ -0,0 +1,145 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.filemanager.console.remote;
+
+import android.content.Context;
+
+import com.cyanogenmod.filemanager.commands.Executable;
+import com.cyanogenmod.filemanager.commands.ExecutableFactory;
+import com.cyanogenmod.filemanager.console.CommandNotFoundException;
+import com.cyanogenmod.filemanager.console.ConsoleAllocException;
+import com.cyanogenmod.filemanager.console.ExecutionException;
+import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
+import com.cyanogenmod.filemanager.console.OperationTimeoutException;
+import com.cyanogenmod.filemanager.console.ReadOnlyFilesystemException;
+import com.cyanogenmod.filemanager.console.VirtualMountPointConsole;
+import com.cyanogenmod.filemanager.model.DiskUsage;
+import com.cyanogenmod.filemanager.model.MountPoint;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An implementation of a {@link VirtualMountPointConsole} for remote filesystems
+ */
+public class RemoteConsole extends VirtualMountPointConsole {
+
+ /**
+ * Constructor of <code>RemoteConsole</code>
+ *
+ * @param ctx The current context
+ */
+ public RemoteConsole(Context ctx) {
+ super(ctx);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return "Remote";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSecure() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRemote() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isMounted() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean unmount() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<MountPoint> getMountPoints() {
+ List<MountPoint> mountPoints = new ArrayList<MountPoint>();
+ return mountPoints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<DiskUsage> getDiskUsage() {
+ List<DiskUsage> diskUsage = new ArrayList<DiskUsage>();
+ return diskUsage;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DiskUsage getDiskUsage(String path) {
+ // TODO Fix when remote console will be implemented
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getMountPointName() {
+ return "remote";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ExecutableFactory getExecutableFactory() {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void execute(Executable executable, Context ctx)
+ throws ConsoleAllocException, InsufficientPermissionsException, NoSuchFileOrDirectory,
+ OperationTimeoutException, ExecutionException, CommandNotFoundException,
+ ReadOnlyFilesystemException {
+
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/console/secure/SecureConsole.java b/src/com/cyanogenmod/filemanager/console/secure/SecureConsole.java
new file mode 100644
index 00000000..be019a66
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/secure/SecureConsole.java
@@ -0,0 +1,640 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.filemanager.console.secure;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Handler;
+import android.os.Message;
+import android.os.UserHandle;
+import android.os.Handler.Callback;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.cyanogenmod.filemanager.FileManagerApplication;
+import com.cyanogenmod.filemanager.R;
+import com.cyanogenmod.filemanager.commands.Executable;
+import com.cyanogenmod.filemanager.commands.ExecutableFactory;
+import com.cyanogenmod.filemanager.commands.MountExecutable;
+import com.cyanogenmod.filemanager.commands.secure.Program;
+import com.cyanogenmod.filemanager.commands.secure.SecureExecutableFactory;
+import com.cyanogenmod.filemanager.console.AuthenticationFailedException;
+import com.cyanogenmod.filemanager.console.CancelledOperationException;
+import com.cyanogenmod.filemanager.console.CommandNotFoundException;
+import com.cyanogenmod.filemanager.console.Console;
+import com.cyanogenmod.filemanager.console.ConsoleAllocException;
+import com.cyanogenmod.filemanager.console.ExecutionException;
+import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
+import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
+import com.cyanogenmod.filemanager.console.OperationTimeoutException;
+import com.cyanogenmod.filemanager.console.ReadOnlyFilesystemException;
+import com.cyanogenmod.filemanager.console.VirtualMountPointConsole;
+import com.cyanogenmod.filemanager.model.DiskUsage;
+import com.cyanogenmod.filemanager.model.MountPoint;
+import com.cyanogenmod.filemanager.preferences.FileManagerSettings;
+import com.cyanogenmod.filemanager.preferences.Preferences;
+import com.cyanogenmod.filemanager.util.DialogHelper;
+import com.cyanogenmod.filemanager.util.ExceptionUtil;
+import com.cyanogenmod.filemanager.util.FileHelper;
+
+import org.apache.http.auth.AuthenticationException;
+
+import de.schlichtherle.truezip.crypto.raes.RaesAuthenticationException;
+import de.schlichtherle.truezip.file.TArchiveDetector;
+import de.schlichtherle.truezip.file.TFile;
+import de.schlichtherle.truezip.file.TVFS;
+import de.schlichtherle.truezip.key.CancelledOperation;
+import static de.schlichtherle.truezip.fs.FsSyncOption.CLEAR_CACHE;
+import static de.schlichtherle.truezip.fs.FsSyncOption.FORCE_CLOSE_INPUT;
+import static de.schlichtherle.truezip.fs.FsSyncOption.FORCE_CLOSE_OUTPUT;
+import de.schlichtherle.truezip.util.BitField;
+
+import java.io.File;
+import java.io.FilenameFilter;
+import java.io.IOException;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.UUID;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * A secure implementation of a {@link VirtualMountPointConsole} that uses a
+ * secure filesystem backend
+ */
+public class SecureConsole extends VirtualMountPointConsole {
+
+ public static final String TAG = "SecureConsole";
+
+ /** The singleton TArchiveDetector which enclosure this driver **/
+ public static final TArchiveDetector DETECTOR = new TArchiveDetector(
+ SecureStorageDriverProvider.SINGLETON, SecureStorageDriverProvider.SINGLETON.get());
+
+ public static String getSecureStorageName() {
+ return String.format("storage.%s.%s",
+ String.valueOf(UserHandle.myUserId()),
+ SecureStorageDriverProvider.SECURE_STORAGE_SCHEME);
+ }
+
+ public static TFile getSecureStorageRoot() {
+ return new TFile(FileManagerApplication.getInstance().getExternalFilesDir(null),
+ getSecureStorageName(), DETECTOR);
+ }
+
+ public static URI getSecureStorageRootUri() {
+ return new File(FileManagerApplication.getInstance().getExternalFilesDir(null),
+ getSecureStorageName()).toURI();
+ }
+
+ private static SecureConsole sConsole = null;
+
+ public final Handler mSyncHandler;
+
+ private boolean mIsMounted;
+ private boolean mRequiresSync;
+
+ private final int mBufferSize;
+
+ private static final long SYNC_WAIT = 10000L;
+
+ private static final int MSG_SYNC_FS = 0;
+
+ private final ExecutorService mExecutorService = Executors.newFixedThreadPool(1);
+
+ private final Callback mSyncCallback = new Callback() {
+ @Override
+ public boolean handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_SYNC_FS:
+ mExecutorService.execute(new Runnable() {
+ @Override
+ public void run() {
+ sync();
+ }
+ });
+ break;
+
+ default:
+ break;
+ }
+ return true;
+ }
+ };
+
+ /**
+ * Return an instance of the current console
+ * @return
+ */
+ public static synchronized SecureConsole getInstance(Context ctx, int bufferSize) {
+ if (sConsole == null) {
+ sConsole = new SecureConsole(ctx, bufferSize);
+ }
+ return sConsole;
+ }
+
+ private final TFile mStorageRoot;
+ private final String mStorageName;
+
+ /**
+ * Constructor of <code>SecureConsole</code>
+ *
+ * @param ctx The current context
+ */
+ private SecureConsole(Context ctx, int bufferSize) {
+ super(ctx);
+ mIsMounted = false;
+ mBufferSize = bufferSize;
+ mSyncHandler = new Handler(mSyncCallback);
+ mStorageRoot = getSecureStorageRoot();
+ mStorageName = getSecureStorageName();
+
+ // Save a copy of the console. This has a unique instance for all the app
+ if (sConsole != null) {
+ sConsole = this;
+ }
+ }
+
+ @Override
+ public void dealloc() {
+ super.dealloc();
+
+ // Synchronize the underlaying storage
+ mSyncHandler.removeMessages(MSG_SYNC_FS);
+ sync();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getName() {
+ return "Secure";
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isSecure() {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isMounted() {
+ return mIsMounted;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public List<MountPoint> getMountPoints() {
+ // This console only has one mountpoint
+ List<MountPoint> mountPoints = new ArrayList<MountPoint>();
+ String status = mIsMounted ? MountExecutable.READWRITE : MountExecutable.READONLY;
+ mountPoints.add(new MountPoint(getVirtualMountPoint().getAbsolutePath(),
+ "securestorage", "securestoragefs", status, 0, 0, true, false));
+ return mountPoints;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ @SuppressWarnings("deprecation")
+ public List<DiskUsage> getDiskUsage() {
+ // This console only has one mountpoint, and is fully usage
+ List<DiskUsage> diskUsage = new ArrayList<DiskUsage>();
+ File mp = mStorageRoot.getFile();
+ diskUsage.add(new DiskUsage(mp.getAbsolutePath(),
+ mp.getTotalSpace(),
+ mp.length(),
+ mp.getTotalSpace() - mp.length()));
+ return diskUsage;
+ }
+
+ /**
+ * Method that returns if the path belongs to the secure storage
+ *
+ * @param path The path to check
+ * @return
+ */
+ public boolean isSecureStorageResource(String path) {
+ return FileHelper.belongsToDirectory(new File(path), getVirtualMountPoint());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public DiskUsage getDiskUsage(String path) {
+ if (isSecureStorageResource(path)) {
+ return getDiskUsage().get(0);
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public String getMountPointName() {
+ return "secure";
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public boolean isRemote() {
+ return false;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public ExecutableFactory getExecutableFactory() {
+ return new SecureExecutableFactory(this);
+ }
+
+ /**
+ * Method that request a reset of the current password
+ */
+ public void requestReset(final Context ctx) {
+ AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ boolean result = false;
+
+ // Unmount the filesystem
+ if (mIsMounted) {
+ unmount();
+ }
+ try {
+ SecureStorageKeyManagerProvider.SINGLETON.reset();
+
+ // Mount with the new key
+ mount(ctx);
+
+ // In order to claim a write, we need to be sure that an operation is
+ // done to disk before unmount the device.
+ try {
+ String testName = UUID.randomUUID().toString();
+ TFile test = new TFile(getSecureStorageRoot(), testName);
+ test.createNewFile();
+ test.rm();
+ result = true;
+ } catch (IOException ex) {
+ ExceptionUtil.translateException(ctx, ex);
+ }
+
+ } catch (Exception ex) {
+ ExceptionUtil.translateException(ctx, ex);
+ } finally {
+ unmount();
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result) {
+ // Success
+ DialogHelper.showToast(ctx, R.string.msgs_success, Toast.LENGTH_SHORT);
+ }
+ }
+
+ };
+ task.execute();
+ }
+
+ /**
+ * Method that request a delete of the current password
+ */
+ @SuppressWarnings("deprecation")
+ public void requestDelete(final Context ctx) {
+ AsyncTask<Void, Void, Boolean> task = new AsyncTask<Void, Void, Boolean>() {
+ @Override
+ protected Boolean doInBackground(Void... params) {
+ boolean result = false;
+
+ // Unmount the filesystem
+ if (mIsMounted) {
+ unmount();
+ }
+ try {
+ SecureStorageKeyManagerProvider.SINGLETON.delete();
+
+ // Test mount/unmount
+ mount(ctx);
+ unmount();
+
+ // Password is valid. Delete the storage
+ mStorageRoot.getFile().delete();
+
+ // Send an broadcast to notify that the mount state of this filesystem changed
+ Intent intent = new Intent(FileManagerSettings.INTENT_MOUNT_STATUS_CHANGED);
+ intent.putExtra(FileManagerSettings.EXTRA_MOUNTPOINT,
+ getVirtualMountPoint().toString());
+ intent.putExtra(FileManagerSettings.EXTRA_STATUS, MountExecutable.READONLY);
+ getCtx().sendBroadcast(intent);
+
+ result = true;
+
+ } catch (Exception ex) {
+ ExceptionUtil.translateException(ctx, ex);
+ }
+
+ return result;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean result) {
+ if (result) {
+ // Success
+ DialogHelper.showToast(ctx, R.string.msgs_success, Toast.LENGTH_SHORT);
+ }
+ }
+
+ };
+ task.execute();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean unmount() {
+ // Unmount the filesystem and cancel the cached key
+ mRequiresSync = true;
+ boolean ret = sync();
+ if (ret) {
+ SecureStorageKeyManagerProvider.SINGLETON.unmount();
+ }
+ mIsMounted = false;
+
+ // Send an broadcast to notify that the mount state of this filesystem changed
+ Intent intent = new Intent(FileManagerSettings.INTENT_MOUNT_STATUS_CHANGED);
+ intent.putExtra(FileManagerSettings.EXTRA_MOUNTPOINT,
+ getVirtualMountPoint().toString());
+ intent.putExtra(FileManagerSettings.EXTRA_STATUS, MountExecutable.READONLY);
+ getCtx().sendBroadcast(intent);
+
+ return mIsMounted;
+ }
+
+ /**
+ * Method that verifies if the current storage is open and mount it
+ *
+ * @param ctx The current context
+ * @throws CancelledOperationException If the operation was cancelled (by the user)
+ * @throws AuthenticationException If the secure storage isn't unlocked
+ * @throws NoSuchFileOrDirectory If the secure storage isn't accessible
+ */
+ @SuppressWarnings("deprecation")
+ public synchronized void mount(Context ctx)
+ throws CancelledOperationException, AuthenticationFailedException,
+ NoSuchFileOrDirectory {
+ if (!mIsMounted) {
+ File root = mStorageRoot.getFile();
+ try {
+ boolean newStorage = !root.exists();
+ mStorageRoot.mount();
+ if (newStorage) {
+ // Force a synchronization
+ mRequiresSync = true;
+ sync();
+ } else {
+ // Remove any previous cache files (if not sync invoked)
+ clearCache(ctx);
+ }
+
+ // The device is mounted
+ mIsMounted = true;
+
+ // Send an broadcast to notify that the mount state of this filesystem changed
+ Intent intent = new Intent(FileManagerSettings.INTENT_MOUNT_STATUS_CHANGED);
+ intent.putExtra(FileManagerSettings.EXTRA_MOUNTPOINT,
+ getVirtualMountPoint().toString());
+ intent.putExtra(FileManagerSettings.EXTRA_STATUS, MountExecutable.READWRITE);
+ getCtx().sendBroadcast(intent);
+
+ } catch (IOException ex) {
+ if (ex.getCause() != null && ex.getCause() instanceof CancelledOperation) {
+ throw new CancelledOperationException();
+ }
+ if (ex.getCause() != null && ex.getCause() instanceof RaesAuthenticationException) {
+ throw new AuthenticationFailedException(ctx.getString(
+ R.string.secure_storage_unlock_failed));
+ }
+ Log.e(TAG, String.format("Failed to open secure storage: %s", root, ex));
+ throw new NoSuchFileOrDirectory();
+ }
+ }
+ }
+
+ /**
+ * Method that returns if the path is the real secure storage file
+ *
+ * @param path The path to check
+ * @return boolean If the path is the secure storage
+ */
+ public static boolean isSecureStorageDir(String path) {
+ Console vc = getVirtualConsoleForPath(path);
+ if (vc != null && vc instanceof SecureConsole) {
+ return isSecureStorageDir(((SecureConsole) vc).buildRealFile(path));
+ }
+ return false;
+ }
+
+ /**
+ * Method that returns if the path is the real secure storage file
+ *
+ * @param path The path to check
+ * @return boolean If the path is the secure storage
+ */
+ public static boolean isSecureStorageDir(TFile path) {
+ return getSecureStorageRoot().equals(path);
+ }
+
+ /**
+ * Method that build a real file from a virtual path
+ *
+ * @param path The path from build the real file
+ * @return TFile The real file
+ */
+ public TFile buildRealFile(String path) {
+ String real = mStorageRoot.toString();
+ String virtual = getVirtualMountPoint().toString();
+ String src = path.replace(virtual, real);
+ return new TFile(src, DETECTOR);
+ }
+
+ /**
+ * Method that build a virtual file from a real path
+ *
+ * @param path The path from build the virtual file
+ * @return TFile The virtual file
+ */
+ public String buildVirtualPath(TFile path) {
+ String real = mStorageRoot.toString();
+ String virtual = getVirtualMountPoint().toString();
+ String dst = path.toString().replace(real, virtual);
+ return dst;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public synchronized void execute(Executable executable, Context ctx)
+ throws ConsoleAllocException, InsufficientPermissionsException, NoSuchFileOrDirectory,
+ OperationTimeoutException, ExecutionException, CommandNotFoundException,
+ ReadOnlyFilesystemException, CancelledOperationException,
+ AuthenticationFailedException {
+ // Check that the program is a secure program
+ try {
+ Program p = (Program) executable;
+ p.setBufferSize(mBufferSize);
+ } catch (Throwable e) {
+ Log.e(TAG, String.format("Failed to resolve program: %s", //$NON-NLS-1$
+ executable.getClass().toString()), e);
+ throw new CommandNotFoundException("executable is not a program", e); //$NON-NLS-1$
+ }
+
+ //Auditing program execution
+ if (isTrace()) {
+ Log.v(TAG, String.format("Executing program: %s", //$NON-NLS-1$
+ executable.getClass().toString()));
+ }
+
+
+ final Program program = (Program) executable;
+
+ // Open storage encryption (if required)
+ if (program.requiresOpen()) {
+ mount(ctx);
+ }
+
+ // Execute the program
+ program.setTrace(isTrace());
+ if (program.isAsynchronous()) {
+ // Execute in a thread
+ Thread t = new Thread() {
+ @Override
+ public void run() {
+ try {
+ program.execute();
+ requestSync(program);
+ } catch (Exception e) {
+ // Program must use onException to communicate exceptions
+ Log.v(TAG,
+ String.format("Async execute failed program: %s", //$NON-NLS-1$
+ program.getClass().toString()));
+ }
+ }
+ };
+ t.start();
+
+ } else {
+ // Synchronous execution
+ program.execute();
+ requestSync(program);
+ }
+ }
+
+ /**
+ * Request a synchronization of the underlying filesystem
+ *
+ * @param program The last called program
+ */
+ private void requestSync(Program program) {
+ if (program.requiresSync()) {
+ mRequiresSync = true;
+ }
+
+ // There is some changes to synchronize?
+ if (mRequiresSync) {
+ Boolean defaultValue = ((Boolean)FileManagerSettings.
+ SETTINGS_SECURE_STORAGE_DELAYED_SYNC.getDefaultValue());
+ Boolean delayedSync =
+ Boolean.valueOf(
+ Preferences.getSharedPreferences().getBoolean(
+ FileManagerSettings.SETTINGS_SECURE_STORAGE_DELAYED_SYNC.getId(),
+ defaultValue.booleanValue()));
+ mSyncHandler.removeMessages(MSG_SYNC_FS);
+ if (delayedSync) {
+ // Request a sync in 30 seconds, if users is not doing any operation
+ mSyncHandler.sendEmptyMessageDelayed(MSG_SYNC_FS, SYNC_WAIT);
+ } else {
+ // Do the synchronization now
+ mSyncHandler.sendEmptyMessage(MSG_SYNC_FS);
+ }
+ }
+ }
+
+ /**
+ * Synchronize the underlying filesystem
+ *
+ * @retun boolean If the unmount success
+ */
+ public synchronized boolean sync() {
+ if (mRequiresSync) {
+ Log.i(TAG, "Syncing underlaying storage");
+ mRequiresSync = false;
+ // Sync the underlying storage
+ try {
+ TVFS.sync(mStorageRoot,
+ BitField.of(CLEAR_CACHE)
+ .set(FORCE_CLOSE_INPUT, true)
+ .set(FORCE_CLOSE_OUTPUT, true));
+ return true;
+ } catch (IOException e) {
+ Log.e(TAG, String.format("Failed to sync secure storage: %s", mStorageRoot, e));
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Method that clear the cache
+ *
+ * @param ctx The current context
+ */
+ private void clearCache(Context ctx) {
+ File filesDir = ctx.getExternalFilesDir(null);
+ File[] cacheFiles = filesDir.listFiles(new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ return filename.startsWith(mStorageName)
+ && filename.endsWith(".tmp");
+ }
+ });
+ for (File cacheFile : cacheFiles) {
+ cacheFile.delete();
+ }
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriver.java b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriver.java
new file mode 100644
index 00000000..df2e4822
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriver.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.filemanager.console.secure;
+
+import de.schlichtherle.truezip.fs.archive.zip.raes.SafeZipRaesDriver;
+import de.schlichtherle.truezip.socket.sl.IOPoolLocator;
+
+/**
+ * Custom implementation of {@code SafeZipRaesDriver}
+ */
+public class SecureStorageDriver extends SafeZipRaesDriver {
+
+ // The singleton FsDriver reference
+ static final SecureStorageDriver SINGLETON = new SecureStorageDriver();
+
+ /**
+ * Constructor of {@code SecureStorageDriver}
+ */
+ private SecureStorageDriver() {
+ super(IOPoolLocator.SINGLETON, SecureStorageKeyManagerProvider.SINGLETON);
+ }
+
+}
diff --git a/src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriverProvider.java b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriverProvider.java
new file mode 100644
index 00000000..17555286
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageDriverProvider.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.filemanager.console.secure;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import de.schlichtherle.truezip.fs.FsDriver;
+import de.schlichtherle.truezip.fs.FsDriverProvider;
+import de.schlichtherle.truezip.fs.FsScheme;
+import de.schlichtherle.truezip.fs.file.FileDriver;
+
+/**
+ * The SecureStorage driver provider which handles {@code "secure"} data schemes
+ */
+public class SecureStorageDriverProvider implements FsDriverProvider {
+
+ /** File scheme **/
+ public static final String FILE_SCHEME = "file";
+
+ /** SecureStorage scheme **/
+ public static final String SECURE_STORAGE_SCHEME = "secure";
+
+ /** The singleton instance of this class. */
+ static final SecureStorageDriverProvider SINGLETON = new SecureStorageDriverProvider();
+
+ /** You cannot instantiate this class. */
+ private SecureStorageDriverProvider() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map<FsScheme, FsDriver> get() {
+ return Boot.DRIVERS;
+ }
+
+ /** A static data utility class used for lazy initialization. */
+ private static final class Boot {
+ static final Map<FsScheme, FsDriver> DRIVERS;
+ static {
+ final Map<FsScheme, FsDriver> fast = new LinkedHashMap<FsScheme, FsDriver>();
+ fast.put(FsScheme.create(FILE_SCHEME), new FileDriver());
+ fast.put(FsScheme.create(SECURE_STORAGE_SCHEME), SecureStorageDriver.SINGLETON);
+ DRIVERS = Collections.unmodifiableMap(fast);
+ }
+ } // Boot
+}
diff --git a/src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyManagerProvider.java b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyManagerProvider.java
new file mode 100644
index 00000000..810b94dd
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyManagerProvider.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.filemanager.console.secure;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+import de.schlichtherle.truezip.crypto.raes.param.AesCipherParameters;
+import de.schlichtherle.truezip.key.AbstractKeyManagerProvider;
+import de.schlichtherle.truezip.key.KeyManager;
+import de.schlichtherle.truezip.key.PromptingKeyManager;
+import de.schlichtherle.truezip.key.PromptingKeyProvider;
+
+/**
+ * The SecureStorage KeyManager provider
+ */
+public class SecureStorageKeyManagerProvider extends AbstractKeyManagerProvider {
+
+ /** The singleton instance of this class. */
+ static final SecureStorageKeyManagerProvider SINGLETON =
+ new SecureStorageKeyManagerProvider();
+
+ private final static SecureStorageKeyPromptDialog PROMPT_DIALOG =
+ new SecureStorageKeyPromptDialog();
+
+ /** You cannot instantiate this class. */
+ private SecureStorageKeyManagerProvider() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public Map<Class<?>, KeyManager<?>> get() {
+ return Boot.MANAGERS;
+ }
+
+ /**
+ * @hide
+ */
+ void unmount() {
+ PROMPT_DIALOG.umount();
+ getKeyProvider().setKey(null);
+ }
+
+ /**
+ * @hide
+ */
+ void reset() {
+ PROMPT_DIALOG.reset();
+ getKeyProvider().setKey(null);
+ }
+
+ /**
+ * @hide
+ */
+ void delete() {
+ PROMPT_DIALOG.delete();
+ getKeyProvider().setKey(null);
+ }
+
+ @SuppressWarnings("unchecked")
+ private static PromptingKeyProvider<AesCipherParameters> getKeyProvider() {
+ PromptingKeyManager<AesCipherParameters> keyManager =
+ (PromptingKeyManager<AesCipherParameters>) Boot.MANAGERS.get(
+ AesCipherParameters.class);
+ return (PromptingKeyProvider<AesCipherParameters>) keyManager.getKeyProvider(
+ SecureConsole.getSecureStorageRootUri());
+ }
+
+ /** A static data utility class used for lazy initialization. */
+ private static final class Boot {
+ static final Map<Class<?>, KeyManager<?>> MANAGERS;
+ static {
+ final PromptingKeyManager<AesCipherParameters> promptKeyManager =
+ new PromptingKeyManager<AesCipherParameters>(PROMPT_DIALOG);
+ final Map<Class<?>, KeyManager<?>> fast = new LinkedHashMap<Class<?>, KeyManager<?>>();
+ fast.put(AesCipherParameters.class, promptKeyManager);
+ MANAGERS = Collections.unmodifiableMap(fast);
+
+ // We need that the provider ask always for a password
+ getKeyProvider().setAskAlwaysForWriteKey(true);
+ }
+ } // class Boot
+
+}
diff --git a/src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyPromptDialog.java b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyPromptDialog.java
new file mode 100644
index 00000000..2db530c4
--- /dev/null
+++ b/src/com/cyanogenmod/filemanager/console/secure/SecureStorageKeyPromptDialog.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2014 The CyanogenMod 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.cyanogenmod.filemanager.console.secure;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.text.Editable;
+import android.text.TextWatcher;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.TextView;
+
+import com.cyanogenmod.filemanager.FileManagerApplication;
+import com.cyanogenmod.filemanager.R;
+import com.cyanogenmod.filemanager.ui.ThemeManager;
+import com.cyanogenmod.filemanager.ui.ThemeManager.Theme;
+import com.cyanogenmod.filemanager.util.DialogHelper;
+
+import de.schlichtherle.truezip.crypto.raes.param.AesCipherParameters;
+import de.schlichtherle.truezip.crypto.raes.Type0RaesParameters.KeyStrength;
+import de.schlichtherle.truezip.key.KeyPromptingCancelledException;
+import de.schlichtherle.truezip.key.KeyPromptingInterruptedException;
+import de.schlichtherle.truezip.key.PromptingKeyProvider.Controller;
+import de.schlichtherle.truezip.key.UnknownKeyException;
+
+/**
+ * A class that remembers all the secure storage
+ */
+public class SecureStorageKeyPromptDialog
+ implements de.schlichtherle.truezip.key.PromptingKeyProvider.View<AesCipherParameters> {
+
+ private static final int MIN_PASSWORD_LENGTH = 8;
+
+ private static final int MSG_REQUEST_UNLOCK_DIALOG = 1;
+
+ private static boolean sResetInProgress;
+ private static boolean sDeleteInProgress;
+ private static transient AesCipherParameters sOldUnlockKey = null;
+ private static transient AesCipherParameters sUnlockKey = null;
+ private static transient AesCipherParameters sOldUnlockKeyTemp = null;
+ private static transient AesCipherParameters sUnlockKeyTemp = null;
+ private static final Object WAIT_SYNC = new Object();
+
+ /**
+ * An activity that simulates a dialog over the activity that requested the key prompt.
+ */
+ public static class SecureStorageKeyPromptActivity extends Activity
+ implements OnClickListener, TextWatcher {
+
+ private AlertDialog mDialog;
+
+ private TextView mMessage;
+ private EditText mOldKey;
+ private EditText mKey;
+ private EditText mRepeatKey;
+ private TextView mValidationMsg;
+ private Button mUnlock;
+
+ private boolean mNewStorage;
+ private boolean mResetPassword;
+ private boolean mDeleteStorage;
+
+ AesCipherParameters mOldKeyParams;
+ AesCipherParameters mKeyParams;
+
+ @Override
+ @SuppressWarnings("deprecation")
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Check with java.io.File instead of TFile because TFile#exists() will
+ // check for password key, which is currently locked
+ mNewStorage = !SecureConsole.getSecureStorageRoot().getFile().exists();
+ mResetPassword = sResetInProgress;
+ mDeleteStorage = sDeleteInProgress;
+
+ // Set the theme before setContentView
+ Theme theme = ThemeManager.getCurrentTheme(this);
+ theme.setBaseTheme(this, true);
+
+ // Load the dialog's custom layout
+ ViewGroup v = (ViewGroup) LayoutInflater.from(this).inflate(
+ R.layout.unlock_dialog_message, null);
+ mMessage = (TextView) v.findViewById(R.id.unlock_dialog_message);
+ mOldKey = (EditText) v.findViewById(R.id.unlock_old_password);
+ mOldKey.addTextChangedListener(this);
+ mKey = (EditText) v.findViewById(R.id.unlock_password);
+ mKey.addTextChangedListener(this);
+ mRepeatKey = (EditText) v.findViewById(R.id.unlock_repeat);
+ mRepeatKey.addTextChangedListener(this);
+ View oldPasswordLayout = v.findViewById(R.id.unlock_old_password_layout);
+ View repeatLayout = v.findViewById(R.id.unlock_repeat_layout);
+ mValidationMsg = (TextView) v.findViewById(R.id.unlock_validation_msg);
+
+ // Load resources
+ int messageResourceId = R.string.secure_storage_unlock_key_prompt_msg;
+ int positiveButtonLabelResourceId = R.string.secure_storage_unlock_button;
+ String title = getString(R.string.secure_storage_unlock_title);
+ if (mNewStorage) {
+ positiveButtonLabelResourceId = R.string.secure_storage_create_button;
+ title = getString(R.string.secure_storage_create_title);
+ messageResourceId = R.string.secure_storage_unlock_key_new_msg;
+ } else if (mResetPassword) {
+ positiveButtonLabelResourceId = R.string.secure_storage_reset_button;
+ title = getString(R.string.secure_storage_reset_title);
+ messageResourceId = R.string.secure_storage_unlock_key_reset_msg;
+ TextView passwordLabel = (TextView) v.findViewById(R.id.unlock_password_title);
+ passwordLabel.setText(R.string.secure_storage_unlock_new_key_title);
+ } else if (mDeleteStorage) {
+ positiveButtonLabelResourceId = R.string.secure_storage_delete_button;
+ title = getString(R.string.secure_storage_delete_title);
+ messageResourceId = R.string.secure_storage_unlock_key_delete_msg;
+ }
+
+ // Set the message according to the storage creation status
+ mMessage.setText(messageResourceId);
+ repeatLayout.setVisibility(mNewStorage || mResetPassword ? View.VISIBLE : View.GONE);
+ oldPasswordLayout.setVisibility(mResetPassword ? View.VISIBLE : View.GONE);
+
+ // Set validation msg
+ mValidationMsg.setText(getString(R.string.secure_storage_unlock_validation_length,
+ MIN_PASSWORD_LENGTH));
+ mValidationMsg.setVisibility(View.VISIBLE);
+
+ // Create the dialog
+ mDialog = DialogHelper.createTwoButtonsDialog(this,
+ positiveButtonLabelResourceId, R.string.cancel,
+ theme.getResourceId(this,"ic_secure_drawable"), title, v, this);
+ mDialog.setOnDismissListener(new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ mDialog.dismiss();
+ finish();
+ }
+ });
+ mDialog.setOnCancelListener(new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ sUnlockKeyTemp = null;
+ mDialog.cancel();
+ finish();
+ }
+ });
+ mDialog.setCanceledOnTouchOutside(false);
+
+ // Apply the theme to the custom view of the dialog
+ applyTheme(this, v);
+ }
+
+ @Override
+ public void onAttachedToWindow() {
+ super.onAttachedToWindow();
+
+ DialogHelper.delegateDialogShow(this, mDialog);
+ mUnlock = mDialog.getButton(DialogInterface.BUTTON_POSITIVE);
+ mUnlock.setEnabled(false);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+
+ // Unlock the wait
+ synchronized (WAIT_SYNC) {
+ WAIT_SYNC.notify();
+ }
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ // Create the AES parameter and set to the prompting view
+ if (mResetPassword) {
+ AesCipherParameters params = new AesCipherParameters();
+ params.setPassword(mOldKey.getText().toString().toCharArray());
+ params.setKeyStrength(KeyStrength.BITS_128);
+ sOldUnlockKeyTemp = params;
+ }
+ AesCipherParameters params = new AesCipherParameters();
+ params.setPassword(mKey.getText().toString().toCharArray());
+ params.setKeyStrength(KeyStrength.BITS_128);
+ sUnlockKeyTemp = params;
+
+ // We ended with this dialog
+ dialog.dismiss();
+ break;
+
+ case DialogInterface.BUTTON_NEGATIVE:
+ // User had cancelled the dialog
+ dialog.cancel();
+
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ // Ignore
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ // Ignore
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ // Validations:
+ // * Key must be MIN_PASSWORD_LENGTH characters or more
+ // * Repeat == Key
+ String oldkey = mOldKey.getText().toString();
+ String key = mKey.getText().toString();
+ String repeatKey = mRepeatKey.getText().toString();
+ boolean validLength = key.length() >= MIN_PASSWORD_LENGTH &&
+ (!mResetPassword || (mResetPassword && oldkey.length() >= MIN_PASSWORD_LENGTH));
+ boolean validEquals = key.equals(repeatKey);
+ boolean valid = validLength && ((mNewStorage && validEquals) || !mNewStorage);
+ mUnlock.setEnabled(valid);
+
+ if (!validLength) {
+ mValidationMsg.setText(getString(R.string.secure_storage_unlock_validation_length,
+ MIN_PASSWORD_LENGTH));
+ mValidationMsg.setVisibility(View.VISIBLE);
+ } else if (mNewStorage && !validEquals) {
+ mValidationMsg.setText(R.string.secure_storage_unlock_validation_equals);
+ mValidationMsg.setVisibility(View.VISIBLE);
+ } else {
+ mValidationMsg.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private void applyTheme(Context ctx, ViewGroup root) {
+ // Apply the current theme
+ Theme theme = ThemeManager.getCurrentTheme(ctx);
+ theme.setBackgroundDrawable(ctx, root, "background_drawable");
+ theme.setTextColor(ctx, mMessage, "text_color");
+ theme.setTextColor(ctx, mOldKey, "text_color");
+ theme.setTextColor(ctx, (TextView) root.findViewById(R.id.unlock_old_password_title),
+ "text_color");
+ theme.setTextColor(ctx, mKey, "text_color");
+ theme.setTextColor(ctx, (TextView) root.findViewById(R.id.unlock_password_title),
+ "text_color");
+ theme.setTextColor(ctx, mRepeatKey, "text_color");
+ theme.setTextColor(ctx, (TextView) root.findViewById(R.id.unlock_repeat_title),
+ "text_color");
+ theme.setTextColor(ctx, mValidationMsg, "text_color");
+ mValidationMsg.setCompoundDrawablesWithIntrinsicBounds(
+ theme.getDrawable(ctx, "filesystem_warning_drawable"), //$NON-NLS-1$
+ null, null, null);
+ }
+ }
+
+ SecureStorageKeyPromptDialog() {
+ super();
+ sResetInProgress = false;
+ sDeleteInProgress = false;
+ sOldUnlockKey = null;
+ sUnlockKey = null;
+ }
+
+ @Override
+ public void promptWriteKey(Controller<AesCipherParameters> controller)
+ throws UnknownKeyException {
+ controller.setKey(getOrPromptForKey(false));
+ if (sResetInProgress) {
+ // Not needed any more. Reads are now done with new key
+ sOldUnlockKey = null;
+ sResetInProgress = false;
+ }
+ }
+
+ @Override
+ public void promptReadKey(Controller<AesCipherParameters> controller, boolean invalid)
+ throws UnknownKeyException {
+ if (!sResetInProgress && invalid) {
+ sUnlockKey = null;
+ }
+ controller.setKey(getOrPromptForKey(true));
+ }
+
+ /**
+ * {@hide}
+ */
+ void umount() {
+ // Discard current keys
+ sResetInProgress = false;
+ sDeleteInProgress = false;
+ sOldUnlockKey = null;
+ sUnlockKey = null;
+ }
+
+ /**
+ * {@hide}
+ */
+ void reset() {
+ // Discard current keys
+ sResetInProgress = true;
+ sDeleteInProgress = false;
+ sOldUnlockKey = null;
+ sUnlockKey = null;
+ }
+
+ /**
+ * {@hide}
+ */
+ void delete() {
+ sDeleteInProgress = true;
+ sResetInProgress = false;
+ sOldUnlockKey = null;
+ }
+
+ /**
+ * Method that return or prompt the user for the secure storage key
+ *
+ * @param read If should return the read or write key
+ * @return AesCipherParameters The AES cipher parameters
+ */
+ private static synchronized AesCipherParameters getOrPromptForKey(boolean read)
+ throws UnknownKeyException {
+ // Check if we have a cached key
+ if (read && sResetInProgress && sOldUnlockKey != null) {
+ return sOldUnlockKey;
+ }
+ if (sUnlockKey != null) {
+ return sUnlockKey;
+ }
+
+ // Need to prompt the user for the secure storage key, so we open a overlay activity
+ // to show the prompt dialog
+ Handler handler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message inputMessage) {
+ Context ctx = FileManagerApplication.getInstance();
+ Intent intent = new Intent(ctx, SecureStorageKeyPromptActivity.class);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ ctx.startActivity(intent);
+ }
+ };
+ handler.sendEmptyMessage(MSG_REQUEST_UNLOCK_DIALOG);
+
+ // Wait for the response
+ synchronized (WAIT_SYNC) {
+ try {
+ WAIT_SYNC.wait();
+ } catch (InterruptedException ex) {
+ throw new KeyPromptingInterruptedException(ex);
+ }
+ }
+
+ // Request for authentication is done. We need to exit from delete status
+ sDeleteInProgress = false;
+
+ // Check if the user cancelled the dialog
+ if (sUnlockKeyTemp == null) {
+ throw new KeyPromptingCancelledException();
+ }
+
+ // Move temporary params to real params
+ sUnlockKey = sUnlockKeyTemp;
+ sOldUnlockKey = sOldUnlockKeyTemp;
+
+ AesCipherParameters key = sUnlockKey;
+ if (sResetInProgress && read) {
+ key = sOldUnlockKey;
+ }
+ return key;
+ }
+}
diff --git a/src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java b/src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java
index bc97b25a..7a1f5a9e 100644
--- a/src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java
+++ b/src/com/cyanogenmod/filemanager/console/shell/ShellConsole.java
@@ -16,6 +16,7 @@
package com.cyanogenmod.filemanager.console.shell;
+import android.content.Context;
import android.util.Log;
import com.cyanogenmod.filemanager.FileManagerApplication;
@@ -286,7 +287,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
//Retrieve identity
IdentityExecutable identityCmd =
getExecutableFactory().newCreator().createIdentityExecutable();
- execute(identityCmd);
+ execute(identityCmd, null);
this.mIdentity = identityCmd.getResult();
// Identity command is required for root console detection,
// but Groups command is not used for now. Also, this command is causing
@@ -297,7 +298,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
//Try with groups
GroupsExecutable groupsCmd =
getExecutableFactory().newCreator().createGroupsExecutable();
- execute(groupsCmd);
+ execute(groupsCmd, null);
this.mIdentity.setGroups(groupsCmd.getResult());
}
} catch (Exception ex) {
@@ -372,10 +373,10 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
* {@inheritDoc}
*/
@Override
- public final synchronized void execute(final Executable executable)
- throws ConsoleAllocException, InsufficientPermissionsException,
- CommandNotFoundException, NoSuchFileOrDirectory,
- OperationTimeoutException, ExecutionException, ReadOnlyFilesystemException {
+ public synchronized void execute(Executable executable, Context ctx)
+ throws ConsoleAllocException, InsufficientPermissionsException, NoSuchFileOrDirectory,
+ OperationTimeoutException, ExecutionException, CommandNotFoundException,
+ ReadOnlyFilesystemException {
execute(executable, false);
}
@@ -1192,7 +1193,7 @@ public abstract class ShellConsole extends Console implements Program.ProgramLis
return false;
}
- if (this.mActiveCommand.getCommand() != null) {
+ if (this.mActiveCommand != null && this.mActiveCommand.getCommand() != null) {
try {
boolean isCancellable = true;
if (this.mActiveCommand instanceof AsyncResultProgram) {