2 * Copyright (C) 2012 The CyanogenMod Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.cyanogenmod.filemanager.util;
19 import android.app.Activity;
20 import android.app.AlertDialog;
21 import android.content.ActivityNotFoundException;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.os.AsyncTask;
25 import android.util.Log;
26 import android.widget.Toast;
28 import com.cyanogenmod.filemanager.FileManagerApplication;
29 import com.cyanogenmod.filemanager.R;
30 import com.cyanogenmod.filemanager.commands.SyncResultExecutable;
31 import com.cyanogenmod.filemanager.commands.shell.InvalidCommandDefinitionException;
32 import com.cyanogenmod.filemanager.console.CommandNotFoundException;
33 import com.cyanogenmod.filemanager.console.ConsoleAllocException;
34 import com.cyanogenmod.filemanager.console.ConsoleBuilder;
35 import com.cyanogenmod.filemanager.console.ExecutionException;
36 import com.cyanogenmod.filemanager.console.InsufficientPermissionsException;
37 import com.cyanogenmod.filemanager.console.NoSuchFileOrDirectory;
38 import com.cyanogenmod.filemanager.console.OperationTimeoutException;
39 import com.cyanogenmod.filemanager.console.ReadOnlyFilesystemException;
40 import com.cyanogenmod.filemanager.console.RelaunchableException;
41 import com.cyanogenmod.filemanager.preferences.AccessMode;
43 import java.io.FileNotFoundException;
44 import java.io.IOException;
45 import java.io.PrintWriter;
46 import java.io.StringWriter;
47 import java.text.ParseException;
48 import java.util.List;
51 * A helper class with useful methods for deal with exceptions.
53 public final class ExceptionUtil {
56 * An interface to communicate events related with the result of a command relaunch.
58 public interface OnRelaunchCommandResult {
60 * Method invoked when the relaunch operation was success
65 * Method invoked when the relaunch operation was cancelled by the user
70 * Method invoked when the relaunch operation was failed
72 * @param cause The cause of the failed operation
74 void onFailed(Throwable cause);
78 * Constructor of <code>ExceptionUtil</code>.
80 private ExceptionUtil() {
84 //Definition of known exceptions and his representation mode and resource identifiers
85 private static final Class<?>[] KNOWN_EXCEPTIONS = {
86 FileNotFoundException.class,
88 InvalidCommandDefinitionException.class,
89 ConsoleAllocException.class,
90 NoSuchFileOrDirectory.class,
91 ReadOnlyFilesystemException.class,
92 InsufficientPermissionsException.class,
93 CommandNotFoundException.class,
94 OperationTimeoutException.class,
95 ExecutionException.class,
97 ActivityNotFoundException.class
99 private static final int[] KNOWN_EXCEPTIONS_IDS = {
100 R.string.msgs_file_not_found,
101 R.string.msgs_io_failed,
102 R.string.msgs_command_not_found,
103 R.string.msgs_console_alloc_failure,
104 R.string.msgs_file_not_found,
105 R.string.msgs_read_only_filesystem,
106 R.string.msgs_insufficient_permissions,
107 R.string.msgs_command_not_found,
108 R.string.msgs_operation_timeout,
109 R.string.msgs_operation_failure,
110 R.string.msgs_operation_failure,
111 R.string.msgs_not_registered_app
113 private static final boolean[] KNOWN_EXCEPTIONS_TOAST = {
129 * Method that attach a asynchronous task for executing when exception need
132 * @param ex The exception
133 * @param task The task
134 * @see RelaunchableException
136 public static void attachAsyncTask(Throwable ex, AsyncTask<Object, Integer, Boolean> task) {
137 if (ex instanceof RelaunchableException) {
138 ((RelaunchableException)ex).setTask(task);
143 * Method that captures and translate an exception, showing a
144 * toast or a alert, according to the importance.
146 * @param context The current context
147 * @param ex The exception
149 public static synchronized void translateException(
150 final Context context, Throwable ex) {
151 translateException(context, ex, false, true);
155 * Method that captures and translate an exception, showing a
156 * toast or a alert, according to the importance.
158 * @param context The current context.
159 * @param ex The exception
160 * @param quiet Don't show UI messages
161 * @param askUser Ask the user when if the exception could be relaunched with other privileged
163 public static synchronized void translateException(
164 final Context context, final Throwable ex,
165 final boolean quiet, final boolean askUser) {
166 translateException(context, ex, quiet, askUser, null);
170 * Method that captures and translate an exception, showing a
171 * toast or a alert, according to the importance.
173 * @param context The current context.
174 * @param ex The exception
175 * @param quiet Don't show UI messages
176 * @param askUser Ask the user when if the exception could be relaunched with other privileged
177 * @param listener The listener where return the relaunch result
179 public static synchronized void translateException(
180 final Context context, final Throwable ex,
181 final boolean quiet, final boolean askUser,
182 final OnRelaunchCommandResult listener) {
184 //Get the appropriate message for the exception
185 int msgResId = R.string.msgs_unknown;
186 boolean toast = true;
187 int cc = KNOWN_EXCEPTIONS.length;
188 for (int i = 0; i < cc; i++) {
189 if (KNOWN_EXCEPTIONS[i].getCanonicalName().compareTo(
190 ex.getClass().getCanonicalName()) == 0) {
191 msgResId = KNOWN_EXCEPTIONS_IDS[i];
192 toast = KNOWN_EXCEPTIONS_TOAST[i];
197 //Check exceptions that can be asked to user
198 if (ex instanceof RelaunchableException && askUser) {
199 ((Activity)context).runOnUiThread(new Runnable() {
202 askUser(context, (RelaunchableException)ex, quiet, listener);
208 //Audit the exception
209 Log.e(context.getClass().getSimpleName(), "Error detected", ex); //$NON-NLS-1$
212 final int fMsgResId = msgResId;
213 final boolean fToast = toast;
215 ((Activity)context).runOnUiThread(new Runnable() {
220 DialogHelper.showToast(context, fMsgResId, Toast.LENGTH_SHORT);
223 DialogHelper.createErrorDialog(
224 context, R.string.error_title, fMsgResId);
225 DialogHelper.delegateDialogShow(context, dialog);
227 } catch (Exception e) {
228 Log.e(context.getClass().getSimpleName(),
229 "ExceptionUtil. Failed to show dialog", ex); //$NON-NLS-1$
237 * Method that ask the user for an operation and re-execution of the command.
239 * @param context The current context
240 * @param relaunchable The exception that contains the command that must be re-executed.
241 * @param listener The listener where return the relaunch result
245 final Context context,
246 final RelaunchableException relaunchable,
248 final OnRelaunchCommandResult listener) {
251 boolean isPrivileged = false;
253 isPrivileged = ConsoleBuilder.getConsole(context).isPrivileged();
254 } catch (Throwable ex) {
258 // If console is privileged there is not need to change
259 // If we are in a ChRooted environment, resolve the error without doing anymore
260 if (relaunchable instanceof InsufficientPermissionsException &&
262 FileManagerApplication.getAccessMode().compareTo(AccessMode.SAFE) == 0)) {
264 context, relaunchable, quiet, false, null);
267 if (listener != null) {
268 listener.onFailed(relaunchable);
273 //Create a yes/no dialog and ask the user
274 AlertDialog alert = DialogHelper.createYesNoDialog(
276 R.string.confirm_operation,
277 relaunchable.getQuestionResourceId(),
278 new DialogInterface.OnClickListener() {
280 public void onClick(DialogInterface dialog, int which) {
281 if (which == DialogInterface.BUTTON_POSITIVE) {
282 //Run the executable again
284 //Prepare the system before re-launch the command
285 prepare(context, relaunchable);
287 //Re-execute the command
288 List<SyncResultExecutable> executables =
289 relaunchable.getExecutables();
290 int cc = executables.size();
291 for (int i = 0; i < cc; i++) {
292 SyncResultExecutable executable = executables.get(i);
293 Object result = CommandHelper.reexecute(
294 context, executable, null);
295 if (relaunchable.getTask() != null) {
296 relaunchable.getTask().execute(result);
300 // Operation complete
301 if (listener != null) {
302 listener.onSuccess();
305 } catch (Throwable ex) {
306 //Capture the exception, this time in quiet mode, if the
307 //exception is the same
309 ex.getClass().getName().compareTo(
310 relaunchable.getClass().getName()) == 0;
312 context, ex, quiet, !ask, listener);
315 if (listener != null) {
316 listener.onFailed(ex);
320 // Operation cancelled
321 if (listener != null) {
322 listener.onCancelled();
327 DialogHelper.delegateDialogShow(context, alert);
331 * Method that prepares the system for re-execute the command.
333 * @param context The current context
334 * @param relaunchable The {@link RelaunchableException} reference
337 static void prepare(final Context context, final RelaunchableException relaunchable) {
338 //- This exception need change the console before re-execute
339 if (relaunchable instanceof InsufficientPermissionsException) {
340 ConsoleBuilder.changeToPrivilegedConsole(context);
345 * Method that prints the exception to an string
347 * @param cause The exception
348 * @return String The stack trace in an string
350 public static String toStackTrace(Exception cause) {
351 StringWriter sw = new StringWriter();
352 PrintWriter pw = new PrintWriter(sw);
354 cause.printStackTrace(pw);
355 return sw.toString();
359 } catch (Exception e) {/**NON BLOCK**/}