com.ntsync.android.sync.activities.CreatePwdProgressDialog.java Source code

Java tutorial

Introduction

Here is the source code for com.ntsync.android.sync.activities.CreatePwdProgressDialog.java

Source

package com.ntsync.android.sync.activities;

/*
 * Copyright (C) 2014 Markus Grieder
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/gpl-3.0.html>. 
 */

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;

import org.apache.http.auth.AuthenticationException;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.NetworkErrorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.AsyncTask.Status;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.util.Base64;

import com.ntsync.android.sync.R;
import com.ntsync.android.sync.client.ClientKeyHelper;
import com.ntsync.android.sync.client.NetworkUtilities;
import com.ntsync.android.sync.client.ServerException;
import com.ntsync.android.sync.shared.Constants;
import com.ntsync.android.sync.shared.LogHelper;
import com.ntsync.shared.Pair;

/**
 * Progress Dialog with Task for Creating a new Private Key with optional
 * validation based on a current Private Key-Salt and PwdCheck (New Create /
 * Recreate)
 * 
 */
public class CreatePwdProgressDialog extends DialogFragment {

    /** The tag used to log to adb console. */
    private static final String TAG = "CreatePwdProgressDialog";

    public static final String PARAM_PWD = "pwd";

    private AsyncTask<Void, Void, Exception> createTask = null;

    private boolean success = false;

    private boolean canceled = false;

    private CreatePwdError taskError = null;

    /**
     * Creates a new Key
     * 
     * @param userName
     * @param pwdSalt
     * @param authtoken
     * @return invisible Dialog
     */
    public static CreatePwdProgressDialog newInstance(String userName, byte[] currPwdSalt) {
        CreatePwdProgressDialog dlg = new CreatePwdProgressDialog();

        Bundle args = new Bundle();
        args.putString(KeyPasswordActivity.PARAM_USERNAME, userName);
        args.putByteArray(KeyPasswordActivity.PARAM_SALT, currPwdSalt);

        dlg.setArguments(args);
        return dlg;
    }

    /**
     * Recreates a Key based on a existing Password
     * 
     * @param userName
     * @param pwdSalt
     * @param authtoken
     * @return invisible Dialog
     */
    public static CreatePwdProgressDialog newInstance(String userName, String pwd, byte[] currPwdSalt,
            byte[] pwdCheck) {
        CreatePwdProgressDialog dlg = new CreatePwdProgressDialog();

        Bundle args = new Bundle();
        args.putString(KeyPasswordActivity.PARAM_USERNAME, userName);
        args.putString(PARAM_PWD, pwd);
        args.putByteArray(KeyPasswordActivity.PARAM_SALT, currPwdSalt);
        args.putByteArray(KeyPasswordActivity.PARAM_CHECK, pwdCheck);
        if (pwd == null) {
            throw new IllegalArgumentException("pwd is null");
        }

        dlg.setArguments(args);
        return dlg;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        AccountManager accountManager = AccountManager.get(getActivity());
        Bundle args = getArguments();
        String mUsername = args.getString(KeyPasswordActivity.PARAM_USERNAME);
        byte[] pwdSalt = args.getByteArray(KeyPasswordActivity.PARAM_SALT);
        Account account = new Account(mUsername, Constants.ACCOUNT_TYPE);

        String pwd = args.getString(PARAM_PWD);
        byte[] pwdCheck = args.getByteArray(KeyPasswordActivity.PARAM_CHECK);

        // Retain to keep Task during conf changes
        setRetainInstance(true);
        setCancelable(false);

        if (pwd == null) {
            createTask = new CreatePwdTask(account, accountManager, pwdSalt);
        } else {
            createTask = new RecreateKeyTask(account, accountManager, pwdSalt, pwdCheck, pwd);
        }
        createTask.execute();
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ProgressDialog dialog = new ProgressDialog(getActivity());
        dialog.setMessage(getText(R.string.keypwd_activity_createpwd));
        dialog.setIndeterminate(true);

        return dialog;
    }

    @Override
    public void onResume() {
        super.onResume();
        if (createTask != null && createTask.getStatus() == Status.FINISHED) {
            deliverResult();
        }
    }

    protected void deliverResult() {
        // deliver result otherwise in onResume, when killed task will be
        // started again.
        if (isResumed()) {
            dismiss();
            if (resultListener != null) {
                resultListener.onCreateEnd(success, canceled, taskError);
            }
        }
    }

    /**
     * The activity that creates an instance of this dialog fragment must
     * implement this interface in order to receive event callbacks.
     */
    public interface CreatePwdDialogListener {
        /**
         * 
         * @param success
         * @param cancel
         * @param error
         *            if success is false, contains an error
         */
        void onCreateEnd(boolean success, boolean cancel, CreatePwdError error);
    }

    /**
     * Key Generation failed with an Error
     */
    public enum CreatePwdError {
        NETWORK_ERROR, KEY_GEN_FAILED, SERVER_ERROR, KEY_VALIDATION_FAILED
    }

    // Use this instance of the interface to deliver action events
    private CreatePwdDialogListener resultListener;

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        try {
            resultListener = (CreatePwdDialogListener) activity;
        } catch (ClassCastException e) {
            // The activity doesn't implement the interface, throw exception
            ClassCastException ex = new ClassCastException(
                    activity.toString() + " must implement CreatePwdDialogListener");
            ex.initCause(e);
            throw ex;
        }
    }

    /**
     * Represents an asynchronous task used to create Password
     */
    private class CreatePwdTask extends AsyncTask<Void, Void, Exception> {

        private Account account;
        private AccountManager acm;
        private byte[] oldSalt;

        CreatePwdTask(Account account, AccountManager accountManager, byte[] oldSalt) {
            this.account = account;
            acm = accountManager;
            this.oldSalt = oldSalt != null ? oldSalt.clone() : null;
        }

        @Override
        protected Exception doInBackground(Void... params) {
            // Create new PWD and send to server.
            ClientKeyHelper.clearPrivateKeyData(account, acm);

            Exception ex = null;
            boolean saved = false;
            // Remove server data
            try {
                if (isCancelled()) {
                    return null;
                }
                ClientKeyHelper.getOrCreatePrivateKey(account, acm);

                if (isCancelled()) {
                    return null;
                }
                // Save Salt
                String salt = ClientKeyHelper.getSalt(account, acm);
                String pwdCheckStr = ClientKeyHelper.getPwdCheck(account, acm);
                if (salt != null && !isCancelled() && pwdCheckStr != null) {
                    saved = savePwdSalt(salt, pwdCheckStr);
                }
            } catch (NetworkErrorException e) {
                LogHelper.logI(TAG, "Failed to authenticate", e);
                ex = e;
            } catch (ServerException e) {
                LogHelper.logWCause(TAG, "Failed to create password due to a server error", e);
                ex = e;
            } catch (IOException e) {
                LogHelper.logE(TAG, "Failed to create key", e);
                ex = e;
            } catch (AuthenticationException e) {
                LogHelper.logWCause(TAG, "Authentication failed", e);
                ex = e;
            } catch (OperationCanceledException e) {
                LogHelper.logI(TAG, "Operation canceled", e);
            } catch (InvalidKeyException e) {
                LogHelper.logW(TAG, "Create key failed due to an invalid Key.", e);
                ex = e;
            }
            if (!saved) {
                ClientKeyHelper.clearPrivateKeyData(account, acm);
            }

            return ex;

        }

        private boolean savePwdSalt(String salt, String pwdCheckStr)
                throws OperationCanceledException, ServerException, NetworkErrorException, AuthenticationException {
            boolean retry;
            int retrycount = 1;
            boolean saved = false;
            String authToken = NetworkUtilities.blockingGetAuthToken(acm, account, null);
            if (authToken != null) {
                do {
                    retry = false;
                    try {
                        boolean saltSaved = NetworkUtilities.savePwdSalt(CreatePwdProgressDialog.this.getActivity(),
                                account.name, authToken, salt, Base64.encodeToString(oldSalt, Base64.DEFAULT), true,
                                pwdCheckStr);
                        if (saltSaved) {
                            ClientKeyHelper.setSaltSaved(account, acm);
                            saved = true;
                        }
                    } catch (AuthenticationException e) {
                        LogHelper.logDCause(TAG, "Authentification failed, trying to get new Token", e);
                        acm.invalidateAuthToken(Constants.ACCOUNT_TYPE, authToken);
                        if (retrycount == 0) {
                            throw e;
                        }
                        authToken = NetworkUtilities.blockingGetAuthToken(acm, account, null);
                        if (authToken != null) {
                            retry = true;
                            retrycount--;
                        } else {
                            throw e;
                        }
                    }
                } while (retry);
            }
            return saved;
        }

        @Override
        protected void onPostExecute(final Exception exception) {
            super.onPostExecute(exception);
            if (exception == null) {
                success = true;
                canceled = false;
                taskError = null;
            } else {
                success = false;
                canceled = false;
                taskError = CreatePwdError.KEY_GEN_FAILED;
                if (exception instanceof NetworkErrorException) {
                    taskError = CreatePwdError.NETWORK_ERROR;
                } else if (exception instanceof ServerException) {
                    taskError = CreatePwdError.SERVER_ERROR;
                }
            }
            deliverResult();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            canceled = true;
            taskError = null;
            success = false;
            deliverResult();
        }
    }

    /**
     * Represents an asynchronous task used to recreate a Key based on a
     * Password
     */
    private class RecreateKeyTask extends AsyncTask<Void, Void, Exception> {

        private final Account account;
        private final AccountManager acm;
        private byte[] currSalt;
        private byte[] pwdCheck;
        private final String pwd;

        RecreateKeyTask(Account account, AccountManager accountManager, byte[] currSalt, byte[] pwdCheck,
                String pwd) {
            this.account = account;
            acm = accountManager;
            this.pwdCheck = pwdCheck != null ? pwdCheck.clone() : null;
            this.pwd = pwd;
            this.currSalt = currSalt != null ? currSalt.clone() : null;
        }

        @Override
        protected Exception doInBackground(Void... params) {
            Exception ex = null;
            // Update CurrSalt because in the meantime the salt could have
            // changed from another client.
            updateCurrSalt();
            if (isCancelled()) {
                return null;
            }
            try {
                ClientKeyHelper.createKey(account, acm, pwd, currSalt, true, pwdCheck);
            } catch (UnsupportedEncodingException e) {
                LogHelper.logE(TAG, "Failed to Recreate Key", e);
                ex = e;
            } catch (InvalidKeyException e) {
                LogHelper.logI(TAG, "Recreate key failed due to an invalid password.", e);
                ex = e;
            }
            return ex;
        }

        private void updateCurrSalt() {
            String authtoken = null;
            try {
                authtoken = NetworkUtilities.blockingGetAuthToken(acm, account, getActivity());
                if (authtoken != null) {
                    byte[] newKeySaltAndKeyCheck = NetworkUtilities
                            .getKeySalt(CreatePwdProgressDialog.this.getActivity(), account.name, authtoken);
                    if (newKeySaltAndKeyCheck != null) {
                        Pair<byte[], byte[]> saltPwd = ClientKeyHelper.splitSaltPwdCheck(newKeySaltAndKeyCheck);
                        currSalt = saltPwd.left;
                        pwdCheck = saltPwd.right;
                    }
                }
            } catch (AuthenticationException ex) {
                acm.invalidateAuthToken(Constants.ACCOUNT_TYPE, authtoken);
                LogHelper.logI(TAG, "Authentification failed, Don't update current salt.", ex);
            } catch (OperationCanceledException e) {
                LogHelper.logI(TAG, "Authentification canceled, couldn't update current salt.", e);
            } catch (NetworkErrorException e) {
                LogHelper.logI(TAG, "Could not update current salt.", e);
            } catch (ServerException e) {
                LogHelper.logI(TAG, "Could not update current salt.", e);
            }
        }

        @Override
        protected void onPostExecute(final Exception exception) {
            super.onPostExecute(exception);
            if (exception == null) {
                success = true;
                canceled = false;
                taskError = null;
            } else {
                success = false;
                canceled = false;
                taskError = CreatePwdError.KEY_VALIDATION_FAILED;
            }
            deliverResult();
        }

        @Override
        protected void onCancelled() {
            super.onCancelled();
            success = false;
            canceled = true;
            taskError = null;
            deliverResult();
        }
    }
}