package org.commcare.preferences; import android.content.SharedPreferences; import android.support.v4.util.Pair; import android.util.Base64; import android.util.Log; import org.commcare.CommCareApp; import org.commcare.CommCareApplication; import org.commcare.activities.FormEntryActivity; import org.commcare.dalvik.BuildConfig; import org.commcare.models.AndroidSessionWrapper; import org.commcare.session.CommCareSession; import org.commcare.util.CommCarePlatform; import org.commcare.utils.SessionStateUninitException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; /** * Logic to save password and user's session and restore them during auto-login * when dev option is enabled * * @author Phillip Mates (pmates@dimagi.com). */ public class DevSessionRestorer { private static final String TAG = DevSessionRestorer.class.getSimpleName(); /** * @return Username and password of last login; null if auto-login not * enabled */ public static Pair<String, String> getAutoLoginCreds(boolean force) { if (force || autoLoginEnabled()) { SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); String lastUser = prefs.getString(CommCarePreferences.LAST_LOGGED_IN_USER, ""); String lastPass = prefs.getString(CommCarePreferences.LAST_PASSWORD, ""); if (!"".equals(lastPass)) { return new Pair<>(lastUser, lastPass); } } return null; } public static void storeAutoLoginCreds(String username, String password) { tryAutoLoginPasswordSave(password, true); SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); prefs.edit().putString(CommCarePreferences.LAST_LOGGED_IN_USER, username).apply(); } /** * Save password into app preferences if auto-login is enabled */ public static void tryAutoLoginPasswordSave(String password, boolean force) { if (force || autoLoginEnabled()) { SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); prefs.edit().putString(CommCarePreferences.LAST_PASSWORD, password).commit(); } } private static boolean autoLoginEnabled() { return BuildConfig.DEBUG && DeveloperPreferences.isAutoLoginEnabled(); } public static void enableAutoLogin() { CommCareApplication.instance().getCurrentApp().getAppPreferences() .edit() .putString(DeveloperPreferences.ENABLE_AUTO_LOGIN, CommCarePreferences.YES) .apply(); } public static void enableSessionSaving() { CommCareApplication.instance().getCurrentApp().getAppPreferences() .edit() .putString(DeveloperPreferences.ENABLE_SAVE_SESSION, CommCarePreferences.YES) .apply(); } public static void clearPassword(SharedPreferences prefs) { prefs.edit().remove(CommCarePreferences.LAST_PASSWORD).commit(); } /** * Builds a session object using serialized data stored in the app * preferences. Currently doesn't support restoring the session stack, only * the current frame. */ public static AndroidSessionWrapper restoreSessionFromPrefs(CommCarePlatform platform) { SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); String serializedSession = prefs.getString(CommCarePreferences.CURRENT_SESSION, null); if (serializedSession != null) { try { byte[] sessionBytes = Base64.decode(serializedSession, Base64.DEFAULT); DataInputStream stream = new DataInputStream(new ByteArrayInputStream(sessionBytes)); CommCareSession restoredSession = CommCareSession.restoreSessionFromStream(platform, stream); Log.i(TAG, "Restoring session from storage"); return new AndroidSessionWrapper(restoredSession); } catch (Exception e) { clearSession(prefs); Log.w(TAG, "Restoring session from serialized file failed"); } } // no saved session or failed to restore; return a blank session return new AndroidSessionWrapper(platform); } /** * Save the current session frame to app shared preferences. */ public static void saveSessionToPrefs() { CommCareApp ccApp = CommCareApplication.instance().getCurrentApp(); if (ccApp == null) { return; } String serializedSession = getSerializedSessionString(); String formEntrySession = FormEntryActivity.getFormEntrySessionString(); ccApp.getAppPreferences().edit() .putString(CommCarePreferences.CURRENT_SESSION, serializedSession) .putString(CommCarePreferences.CURRENT_FORM_ENTRY_SESSION, formEntrySession) .apply(); } public static String getSerializedSessionString() { ByteArrayOutputStream baos = new ByteArrayOutputStream(); DataOutputStream serializedStream = new DataOutputStream(baos); try { CommCareApplication.instance().getCurrentSession().serializeSessionState(serializedStream); } catch (IOException e) { Log.w(TAG, "Failed to serialize session"); return ""; } catch (SessionStateUninitException e) { Log.w(TAG, "Attempting to save a non-existent session"); return ""; } String serializedSession = Base64.encodeToString(baos.toByteArray(), Base64.DEFAULT); try { serializedStream.close(); } catch (IOException e) { Log.d(TAG, "Failed to close session serialization stream."); } return serializedSession; } public static boolean savedSessionPresent() { SharedPreferences prefs = CommCareApplication.instance().getCurrentApp().getAppPreferences(); String serializedSession = prefs.getString(CommCarePreferences.CURRENT_SESSION, null); return serializedSession != null; } public static void clearSession() { clearSession(CommCareApplication.instance().getCurrentApp().getAppPreferences()); } private static void clearSession(SharedPreferences prefs) { prefs.edit() .remove(CommCarePreferences.CURRENT_SESSION) .remove(CommCarePreferences.CURRENT_FORM_ENTRY_SESSION) .commit(); } }