org.deviceconnect.android.localoauth.LocalOAuth2Main.java Source code

Java tutorial

Introduction

Here is the source code for org.deviceconnect.android.localoauth.LocalOAuth2Main.java

Source

/*
 * Copyright 2005-2014 Restlet
 * 
 * The contents of this file are subject to the terms of one of the following
 * open source licenses: Apache 2.0 or LGPL 3.0 or LGPL 2.1 or CDDL 1.0 or EPL
 * 1.0 (the "Licenses"). You can select the license that you prefer but you may
 * not use this file except in compliance with one of these Licenses.
 * 
 * You can obtain a copy of the Apache 2.0 license at
 * http://www.opensource.org/licenses/apache-2.0
 * 
 * You can obtain a copy of the LGPL 3.0 license at
 * http://www.opensource.org/licenses/lgpl-3.0
 * 
 * You can obtain a copy of the LGPL 2.1 license at
 * http://www.opensource.org/licenses/lgpl-2.1
 * 
 * You can obtain a copy of the CDDL 1.0 license at
 * http://www.opensource.org/licenses/cddl1
 * 
 * You can obtain a copy of the EPL 1.0 license at
 * http://www.opensource.org/licenses/eclipse-1.0
 * 
 * See the Licenses for the specific language governing permissions and
 * limitations under the Licenses.
 * 
 * Alternatively, you can obtain a royalty free commercial license with less
 * limitations, transferable or non-transferable, directly at
 * http://www.restlet.com/products/restlet-framework
 * 
 * Restlet is a registered trademark of Restlet
 */
package org.deviceconnect.android.localoauth;

import android.content.BroadcastReceiver;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.os.Handler;
import android.os.Looper;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Base64;

import org.deviceconnect.android.BuildConfig;
import org.deviceconnect.android.localoauth.activity.ConfirmAuthActivity;
import org.deviceconnect.android.localoauth.exception.AuthorizationException;
import org.deviceconnect.android.localoauth.oauthserver.LoginPageServerResource;
import org.deviceconnect.android.localoauth.oauthserver.SampleUser;
import org.deviceconnect.android.localoauth.oauthserver.SampleUserManager;
import org.deviceconnect.android.localoauth.oauthserver.db.LocalOAuthOpenHelper;
import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteClient;
import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteClientManager;
import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteToken;
import org.deviceconnect.android.localoauth.oauthserver.db.SQLiteTokenManager;
import org.deviceconnect.android.localoauth.temp.RedirectRepresentation;
import org.deviceconnect.android.localoauth.temp.ResultRepresentation;
import org.deviceconnect.android.logger.AndroidHandler;
import org.json.JSONException;
import org.restlet.Context;
import org.restlet.Request;
import org.restlet.Response;
import org.restlet.data.ClientInfo;
import org.restlet.data.Cookie;
import org.restlet.data.Form;
import org.restlet.data.Reference;
import org.restlet.ext.oauth.AccessTokenServerResource;
import org.restlet.ext.oauth.AuthPageServerResource;
import org.restlet.ext.oauth.AuthorizationBaseServerResource;
import org.restlet.ext.oauth.AuthorizationServerResource;
import org.restlet.ext.oauth.OAuthException;
import org.restlet.ext.oauth.PackageInfoOAuth;
import org.restlet.ext.oauth.internal.Client;
import org.restlet.ext.oauth.internal.Client.ClientType;
import org.restlet.ext.oauth.internal.ClientManager;
import org.restlet.ext.oauth.internal.Scope;
import org.restlet.ext.oauth.internal.Token;
import org.restlet.ext.oauth.internal.TokenManager;
import org.restlet.representation.Representation;
import org.restlet.representation.StringRepresentation;
import org.restlet.util.Series;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;

/**
 * Local OAuth API.
 */
public class LocalOAuth2Main {

    /**
     * ????????.
     */
    public static final String ACTION_TOKEN_APPROVAL = "org.deviceconnect.android.localoauth.TOKEN_APPROVAL";

    /**
     * ???????Thread ID????.
     */
    public static final String EXTRA_THREAD_ID = "org.deviceconnect.android.localoauth.THREAD_ID";

    /**
     * ?????????????.
     */
    public static final String EXTRA_APPROVAL = "org.deviceconnect.android.localoauth.APPROVAL";

    /** (RedirectURI). */
    public static final String DUMMY_REDIRECT_URI = "dummyRedirectURI";

    /** (OriginalRef). */
    private static final String DUMMY_ORIGINAL_REF = "dummyOriginalRef";

    /** (Reference). */
    private static final String DUMMY_REFERENCE = "dummyReference";

    /** (Scope). */
    private static final String DUMMY_SCOPE1 = "scope1";

    /** UserManager. */
    private SampleUserManager mUserManager;

    /** ClientManager. */
    private ClientManager mClientManager;

    /** TokenManager. */
    private TokenManager mTokenManager;

    /** DBHelper. */
    private LocalOAuthOpenHelper mDbHelper;

    /** Database. */
    private SQLiteDatabase mDb;

    /** . */
    private Logger sLogger = Logger.getLogger("org.deviceconnect.localoauth");

    /** DBLock. */
    private final Object mLockForDbAccess = new Object();

    /** ???(??synchronized??). */
    private List<ConfirmAuthRequest> mRequestQueue = new ArrayList<>();

    /** ???Lock. */
    private final Object mLockForRequestQueue = new Object();

    private android.content.Context mContext;

    /**
     * .
     */
    public LocalOAuth2Main(final android.content.Context context) {
        this(context, "localoauth.db");
    }

    /**
     * .
     */
    public LocalOAuth2Main(final android.content.Context context, final String dbName) {
        // 
        Logger logger = sLogger;
        if (BuildConfig.DEBUG) {
            AndroidHandler handler = new AndroidHandler(logger.getName());
            handler.setFormatter(new SimpleFormatter());
            handler.setLevel(Level.ALL);
            logger.addHandler(handler);
            logger.setLevel(Level.ALL);
        } else {
            logger.setLevel(Level.OFF);
        }

        mContext = context;

        // DB??
        mDbHelper = new LocalOAuthOpenHelper(context, dbName);
        mDb = mDbHelper.getWritableDatabase();

        // ??
        mUserManager = new SampleUserManager();
        mClientManager = new SQLiteClientManager(mDb);
        mTokenManager = new SQLiteTokenManager(mDb);

        // 
        addUserData(SampleUser.LOCALOAUTH_USER, SampleUser.LOCALOAUTH_PASS);

        register(context);
    }

    /**
     * (1)-2.LocalOAuth?.
     */
    public void destroy() {
        unregister(mContext);

        // DB????
        if (mDbHelper != null) {
            mDbHelper.close();
        }

        mDb = null;
        mUserManager = null;
        mClientManager = null;
        mTokenManager = null;
        mDbHelper = null;
        mContext = null;
    }

    /**
     * SampleUserManager?.
     * @return SampleUserManager?
     */
    public SampleUserManager getSampleUserManager() {
        return mUserManager;
    }

    /**
     * (2)?.
     * <p>
     * ????????.
     * </p>
     * @param packageInfo (Android)??????<br>
     *            (Web)???????URL<br>
     *            ????????ID<br>
     * @return ??(ID, )?<br>
     *         null???
     * @throws AuthorizationException Authorization.
     */
    public ClientData createClient(final PackageInfoOAuth packageInfo) throws AuthorizationException {
        if (packageInfo == null) {
            throw new IllegalArgumentException("packageInfo is null.");
        } else if (packageInfo.getPackageName() == null) {
            throw new IllegalArgumentException("packageInfo.getPackageName() is null.");
        } else if (packageInfo.getPackageName().length() <= 0) {
            throw new IllegalArgumentException("packageInfo is empty.");
        } else if (!mDb.isOpen()) {
            throw new RuntimeException("Database is not opened.");
        }

        // ???????clientId?(DB????????)
        int clientCount = cleanupClient();

        // ????????
        if (clientCount >= LocalOAuth2Settings.CLIENT_MAX) {
            throw new AuthorizationException(AuthorizationException.CLIENT_COUNTS_IS_FULL);
        }

        // 
        ClientData clientData;

        synchronized (mLockForDbAccess) {
            try {
                mDb.beginTransaction();

                // ??ID??????
                Client client = mClientManager.findByPackageInfo(packageInfo);
                if (client != null) {
                    String clientId = client.getClientId();
                    removeTokenData(clientId);
                    removeClientData(clientId);
                }

                // ?????
                client = addClientData(packageInfo);
                clientData = new ClientData(client.getClientId(), String.copyValueOf(client.getClientSecret()));

                mDb.setTransactionSuccessful();
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            } finally {
                mDb.endTransaction();
            }
        }

        return clientData;
    }

    /**
     * (5)???.
     * <p>
     * - Activity?process?????Device Connect Manager?????<br>
     * - Device Connect Manager???????(0)onBind()?Bind?<br>
     * - Messenger, Handler ?LocalOAuth???<br>
     * 
     * - ????<br>
     * 
     * - (a)?????(?) =>
     * ?????/??Bind??Service?Message??<br>
     * 
     * - (b)???????? =>
     * ?????/??Bind??Service?Message??<br>
     * 
     * - (c)?????? => ???????(Message???)<br>
     * </p>
     * @param params 
     * @param listener (???????????)
     * @throws AuthorizationException Authorization.
     */
    public void confirmPublishAccessToken(final ConfirmAuthParams params, final PublishAccessTokenListener listener)
            throws AuthorizationException {
        if (params == null) {
            throw new IllegalArgumentException("confirmAuthParams is null.");
        } else if (listener == null) {
            throw new IllegalArgumentException("publishAccessTokenListener is null.");
        } else if (params.getContext() == null) {
            throw new IllegalArgumentException("Context is null.");
        } else if (params.getApplicationName() == null || params.getApplicationName().isEmpty()) {
            throw new IllegalArgumentException("ApplicationName is null.");
        } else if (params.getClientId() == null || params.getClientId().isEmpty()) {
            throw new IllegalArgumentException("ClientId is null.");
        } else if (params.getScopes() == null || params.getScopes().length <= 0) {
            throw new IllegalArgumentException("Scope is null.");
        } else if (!mDb.isOpen()) {
            throw new RuntimeException("Database is not opened.");
        }

        synchronized (mLockForDbAccess) {
            // ID?
            Thread thread = Thread.currentThread();
            final long threadId = thread.getId();

            // ?(???????("ja_JP")???????)
            String locale = Locale.getDefault().getLanguage();
            String[] splitLocales = locale.split("_");
            if (splitLocales.length > 1) {
                locale = splitLocales[0];
            }

            // ????devicePlugin.xml???????
            Map<String, DevicePluginXmlProfile> supportProfiles = null;
            if (params.isForDevicePlugin()) {
                supportProfiles = DevicePluginXmlUtil.getSupportProfiles(params.getContext(),
                        params.getContext().getPackageName());
            }
            String[] scopes = params.getScopes();
            String[] displayScopes = new String[scopes.length];
            for (int i = 0; i < scopes.length; i++) {
                // ??????
                displayScopes[i] = ScopeUtil.getDisplayScope(params.getContext(), scopes[i], locale,
                        supportProfiles);
            }

            // ??
            ConfirmAuthRequest request = new ConfirmAuthRequest(threadId, params, listener, displayScopes,
                    params.isAutoFlag());

            // ?
            enqueueRequest(request);

            // Activity??Bind?????????
            // Activity?
            if (mRequestQueue.size() <= 1) {
                startConfirmAuthActivity(pickupRequest());
            }
        }
    }

    /**
     * 1?????.
     * @param t ???
     * @return true???1?false?????
     */
    private static boolean checkTime(final long t) {
        return 0 <= t && t <= (LocalOAuth2Settings.ACCESS_TOKEN_GRACE_TIME * LocalOAuth2Settings.MSEC);
    }

    /**
     * (7)??.
     * 
     * @param accessToken ??
     * @param scope ?????????
     * @param specialScopes ???????????scopes(null??)
     * @return ??
     */
    public CheckAccessTokenResult checkAccessToken(final String accessToken, final String scope,
            final String[] specialScopes) {
        if (scope == null) {
            throw new IllegalArgumentException("scope is null.");
        }

        // true: ??ID?
        // false: ??ID??
        boolean isExistClientId = false;
        // true: ? / false: ??
        boolean isExistAccessToken = false;
        // true: ? / false: ??
        boolean isExistScope = false;
        // true: ? / false: ?
        boolean isNotExpired = false;

        // ???????
        if (specialScopes != null && Arrays.asList(specialScopes).contains(scope)) {
            return new CheckAccessTokenResult(true, true, true, true);
        }

        // ???????
        if (accessToken == null) {
            return new CheckAccessTokenResult(false, false, false, false);
        }

        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                mDb.beginTransaction();

                // ??
                SQLiteToken token = (SQLiteToken) mTokenManager.findTokenByAccessToken(accessToken);
                if (token != null) {
                    // ?
                    isExistAccessToken = true;
                    Scope[] scopes = token.getScope();
                    for (Scope s : scopes) {
                        // token.scope?"*"?????????????
                        if (BuildConfig.DEBUG && s.getScope().equals("*")) {
                            isExistScope = true; // ?
                            isNotExpired = true; // ?
                            break;
                        }

                        if (s.getScope().equals(scope)) {
                            isExistScope = true; // ?

                            if (s.getExpirePeriod() == 0) {
                                // ?0?????1??????
                                long t = System.currentTimeMillis() - token.getRegistrationDate();
                                if (checkTime(t) && token.isFirstAccess()) {
                                    isNotExpired = true;
                                }
                            } else if (s.getExpirePeriod() > 0) {
                                // ?1?????????????
                                isNotExpired = !s.isExpired();
                            } else {
                                // ?????????????
                                isNotExpired = false;
                            }
                            break;
                        }
                    }

                    // ????ID????
                    if (mClientManager.findById(token.getClientId()) != null) {
                        isExistClientId = true;
                    }

                    // ?
                    if (token.isFirstAccess()) {
                        token.dbUpdateTokenAccessTime(mDb);
                    }
                }

                mDb.setTransactionSuccessful();
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            } finally {
                mDb.endTransaction();
            }
        }

        CheckAccessTokenResult result = new CheckAccessTokenResult(isExistClientId, isExistAccessToken,
                isExistScope, isNotExpired);
        if (!result.checkResult()) {
            sLogger.warning("checkAccessToken() - error.");
            sLogger.warning(" - isExistClientId: " + isExistClientId);
            sLogger.warning(" - isExistAccessToken: " + isExistAccessToken);
            sLogger.warning(" - isExistScope:" + isExistScope);
            sLogger.warning(" - isNotExpired:" + isNotExpired);
            sLogger.warning(" - accessToken:" + accessToken);
            sLogger.warning(" - scope:" + scope);
        }
        return result;
    }

    /**
     * (10)???.
     * 
     * @param accessToken 
     * @return not null:  / null:????????????
     */
    public ClientPackageInfo findClientPackageInfoByAccessToken(final String accessToken) {
        if (accessToken == null) {
            throw new IllegalArgumentException("accessToken is null.");
        } else if (!mDb.isOpen()) {
            throw new RuntimeException("Database is not opened.");
        }

        ClientPackageInfo clientPackageInfo = null;
        synchronized (mLockForDbAccess) {
            try {
                SQLiteToken token = (SQLiteToken) mTokenManager.findTokenByAccessToken(accessToken);
                if (token != null) {
                    String clientId = token.getClientId();
                    if (clientId != null) {
                        Client client = mClientManager.findById(clientId);
                        if (client != null) {
                            clientPackageInfo = new ClientPackageInfo(client.getPackageInfo(), clientId);
                        }
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
        return clientPackageInfo;
    }

    /**
     * (13)-1.??(startAccessTokenListActivity()).
     * @return not null: ?? / null: ??
     */
    public SQLiteToken[] getAccessTokens() {
        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                // LocalOAuth???????
                return (SQLiteToken[]) mTokenManager.findTokens(SampleUser.USERNAME);
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * ????.
     * <p>
     * ???????null??
     * </p>
     * @param client 
     * @return 
     */
    public SQLiteToken getAccessToken(final Client client) {
        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                // LocalOAuth???????
                return (SQLiteToken) mTokenManager.findToken(client, SampleUser.USERNAME);
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * (13)-2.??????????(startAccessTokenListActivity().
     * 
     * @param tokenId ID
     */
    public void destroyAccessToken(final long tokenId) {
        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                mDb.beginTransaction();

                ((SQLiteTokenManager) mTokenManager).revokeToken(tokenId);

                mDb.setTransactionSuccessful();
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            } finally {
                mDb.endTransaction();
            }
        }
    }

    /**
     * (13)-3.DB?(startAccessTokenListActivity().
     * 
     */
    public void destroyAllAccessToken() {
        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                mDb.beginTransaction();

                mTokenManager.revokeAllTokens(SampleUser.USERNAME);

                mDb.setTransactionSuccessful();
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            } finally {
                mDb.endTransaction();
            }
        }
    }

    /**
     * (13)-4.ID????(startAccessTokenListActivity()).
     * @param clientId ID
     * @return not null:  / null: ??
     */
    public SQLiteClient findClientByClientId(final String clientId) {
        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                // LocalOAuth???????
                return (SQLiteClient) mClientManager.findById(clientId);
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * ???????clientId?(DB????????).
     * @return 
     */
    private int cleanupClient() {
        int clientCount;
        synchronized (mLockForDbAccess) {
            if (!mDb.isOpen()) {
                throw new RuntimeException("Database is not opened.");
            }

            try {
                mDb.beginTransaction();

                SQLiteClientManager mgr = (SQLiteClientManager) mClientManager;

                mgr.cleanupClient(LocalOAuth2Settings.CLIENT_CLEANUP_TIME);
                clientCount = mgr.countClients();

                mDb.setTransactionSuccessful();
            } catch (SQLiteException e) {
                throw new RuntimeException(e);
            } finally {
                mDb.endTransaction();
            }
        }

        return clientCount;
    }

    /**
     * ???.
     * @param threadId ?ID
     * @param isApproval ????true?????false
     */
    private void processApproval(final long threadId, final boolean isApproval) {
        // ??????????????
        ConfirmAuthRequest request = dequeueRequest(threadId, false);
        if (request != null && !request.isDoneResponse()) {
            request.setDoneResponse(true);
            request.stopTimer();

            PublishAccessTokenListener publishAccessTokenListener = request.getPublishAccessTokenListener();
            ConfirmAuthParams params = request.getConfirmAuthParams();

            if (isApproval) {
                AccessTokenData accessTokenData = null;
                AuthorizationException exception = null;

                synchronized (mLockForDbAccess) {
                    if (!mDb.isOpen()) {
                        exception = new AuthorizationException(AuthorizationException.SQLITE_ERROR);
                    } else {
                        try {
                            mDb.beginTransaction();

                            // ??????
                            // (ID???????)?
                            ((SQLiteTokenManager) mTokenManager).cleanup();

                            // 
                            accessTokenData = publishAccessToken(params);

                            mDb.setTransactionSuccessful();
                        } catch (AuthorizationException e) {
                            exception = e;
                        } catch (SQLiteException e) {
                            exception = new AuthorizationException(AuthorizationException.SQLITE_ERROR);
                        } finally {
                            mDb.endTransaction();
                        }
                    }
                }

                if (exception == null) {
                    // ???
                    callPublishAccessTokenListener(accessTokenData, publishAccessTokenListener);
                } else {
                    // ?????
                    callExceptionListener(exception, publishAccessTokenListener);
                }
            } else {
                // ????
                callPublishAccessTokenListener(null, publishAccessTokenListener);
            }

            new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
                @Override
                public void run() {
                    // Activity?????????????????
                    dequeueRequest(threadId, true);

                    // ???????????Activity?
                    final ConfirmAuthRequest nextRequest = pickupRequest();
                    if (nextRequest != null) {
                        startConfirmAuthActivity(nextRequest);
                    }
                }
            }, 2000);
        }
    }

    /**
     * ??.
     * 
     * @param params ????
     * @return 
     * @throws AuthorizationException Authorization.
     */
    private AccessTokenData publishAccessToken(final ConfirmAuthParams params) throws AuthorizationException {
        String clientId = params.getClientId();
        Client client = mClientManager.findById(clientId);
        if (client != null) {
            // AuthSession??ID?
            RedirectRepresentation redirectRepresentation = callAuthorizationServerResource(client, true, null);
            if (redirectRepresentation != null) {
                String sessionId = redirectRepresentation.getOptions().get(RedirectRepresentation.SESSION_ID)
                        .toString();
                // ?
                ResultRepresentation result = callLoginPageServerResource(SampleUser.LOCALOAUTH_USER,
                        SampleUser.LOCALOAUTH_PASS);
                if (result.getResult()) {
                    RedirectRepresentation result3 = callAuthorizationServerResource(client, false, sessionId);
                    if (result3 != null) {
                        // ??xml?????????
                        Map<String, DevicePluginXmlProfile> supportProfiles = null;
                        if (params.isForDevicePlugin()) {
                            supportProfiles = DevicePluginXmlUtil.getSupportProfiles(params.getContext(),
                                    params.getContext().getPackageName());
                        }

                        // (Scope??????)
                        String[] scopes = params.getScopes();
                        ArrayList<Scope> settingScopes = new ArrayList<>();
                        for (String scope : scopes) {
                            // ??xml?????????(??)
                            long expirePeriod = LocalOAuth2Settings.DEFAULT_TOKEN_EXPIRE_PERIOD;
                            if (supportProfiles != null) {
                                DevicePluginXmlProfile xmlProfile = supportProfiles.get(scope);
                                if (xmlProfile != null) {
                                    expirePeriod = xmlProfile.getExpirePeriod();
                                }
                            }

                            Scope s = new Scope(scope, System.currentTimeMillis(), expirePeriod);
                            settingScopes.add(s);
                        }

                        // ???
                        String applicationName = params.getApplicationName();
                        RedirectRepresentation result4 = callAuthPageServerResource(sessionId, settingScopes,
                                applicationName);
                        if (result4 != null) {
                            Map<String, Object> options = result4.getOptions();
                            String authCode = (String) options.get(AuthPageServerResource.CODE);
                            if (authCode != null) {
                                // ?
                                String accessToken = callAccessTokenServerResource(client, authCode,
                                        applicationName);
                                // ?
                                Token token = mTokenManager.findTokenByAccessToken(accessToken);

                                // ?
                                AccessTokenScope[] accessTokenScopes = scopesToAccessTokenScopes(token.getScope());
                                return new AccessTokenData(accessToken, token.getRegistrationDate(),
                                        accessTokenScopes);
                            }
                        }
                    }
                }
            }
        } else {
            throw new AuthorizationException(AuthorizationException.CLIENT_NOT_FOUND);
        }

        return null;
    }

    /**
     * ???.
     * 
     * @param accessTokenData 
     * @param publishAccessTokenListener 
     */
    private void callPublishAccessTokenListener(final AccessTokenData accessTokenData,
            final PublishAccessTokenListener publishAccessTokenListener) {
        if (publishAccessTokenListener != null) {
            // ???
            publishAccessTokenListener.onReceiveAccessToken(accessTokenData);
        } else {
            // ?????????????
            throw new RuntimeException("publishAccessTokenListener is null.");
        }
    }

    /**
     * ???.
     * 
     * @param exception 
     * @param publishAccessTokenListener 
     */
    private void callExceptionListener(final Exception exception,
            final PublishAccessTokenListener publishAccessTokenListener) {
        if (publishAccessTokenListener != null) {
            // ???
            publishAccessTokenListener.onReceiveException(exception);
        } else {
            // ?????????????
            throw new RuntimeException("publishAccessTokenListener is null.");
        }
    }

    /**
     * AuthorizationServerResource.
     * 
     * @param client 
     * @param initialize ?(true???Context??)
     * @param sessionId ID(????ID???????null?)
     * @return not null: RedirectRepresentation?? / null: 
     * @throws AuthorizationException Authorization.
     */
    private RedirectRepresentation callAuthorizationServerResource(final Client client, final boolean initialize,
            final String sessionId) throws AuthorizationException {

        // AuthorizationServerResource??
        if (initialize) {
            Context context = new Context(sLogger);
            AuthorizationServerResource.init(context);
        }

        // request, response? *
        Request request = new Request();
        request.setOriginalRef(new Reference(DUMMY_ORIGINAL_REF));
        Response response = new Response(request);
        request.setResourceRef(new Reference(DUMMY_REFERENCE));

        // ID?????Request??
        if (sessionId != null) {
            Series<Cookie> cookies = new Series<>(Cookie.class);
            cookies.add(AuthorizationBaseServerResource.ClientCookieID, sessionId);
            request.setCookies(cookies);
        }

        AuthorizationServerResource.init(request, response, mClientManager, mTokenManager);

        // Form??
        Form paramsA = new Form();
        paramsA.add(AuthorizationServerResource.CLIENT_ID, client.getClientId());
        paramsA.add(AuthorizationServerResource.REDIR_URI, DUMMY_REDIRECT_URI);
        paramsA.add(AuthorizationServerResource.RESPONSE_TYPE, "code");
        paramsA.add(AuthorizationServerResource.SCOPE, DUMMY_SCOPE1);

        // requestAuthorization?
        Representation representationA;
        try {
            representationA = AuthorizationServerResource.requestAuthorization(paramsA);
        } catch (OAuthException e) {
            throw new AuthorizationException(e);
        }

        // (?)
        if (representationA instanceof RedirectRepresentation) {
            return (RedirectRepresentation) representationA;
        }

        return null;
    }

    /**
     * LoginPageServerResource?.
     * 
     * @param userId ID
     * @param password 
     * @return (ResultRepresentation)
     */
    private ResultRepresentation callLoginPageServerResource(final String userId, final String password) {
        // ????ID????
        String sessionId = AuthorizationServerResource.getSessionId();
        Series<Cookie> cookies = new Series<>(Cookie.class);
        cookies.add(AuthorizationBaseServerResource.ClientCookieID, sessionId);

        // (B)??
        LoginPageServerResource.initResult();
        Request request = new Request();
        Reference requestReference = new Reference(DUMMY_ORIGINAL_REF);
        requestReference.addQueryParameter(LoginPageServerResource.USER_ID, userId);
        requestReference.addQueryParameter(LoginPageServerResource.PASSWORD, password);
        requestReference.addQueryParameter(LoginPageServerResource.CONTINUE,
                RedirectRepresentation.RedirectProc.requestAuthorization.toString());

        // QueryParameter??????????
        ArrayList<String> userIds = new ArrayList<>();
        userIds.add(userId);
        ArrayList<String> passwords = new ArrayList<>();
        passwords.add(password);
        ArrayList<String> continues = new ArrayList<>();
        continues.add(RedirectRepresentation.RedirectProc.requestAuthorization.toString());

        LoginPageServerResource.getQuery().put(LoginPageServerResource.USER_ID, userIds);
        LoginPageServerResource.getQuery().put(LoginPageServerResource.PASSWORD, passwords);
        LoginPageServerResource.getQuery().put(LoginPageServerResource.CONTINUE, continues);

        request.setCookies(cookies);
        request.setOriginalRef(requestReference);
        request.setResourceRef(requestReference);
        Response response = new Response(request);
        LoginPageServerResource.init(request, response);
        ResultRepresentation resultRepresentation;
        try {
            resultRepresentation = (ResultRepresentation) LoginPageServerResource.getPage(this);
        } catch (OAuthException e) {
            resultRepresentation = new ResultRepresentation();
            resultRepresentation.setResult(false);
            resultRepresentation.setError(e.getMessage(), e.getErrorDescription());
        }

        return resultRepresentation;
    }

    /**
     * AuthPageServerResource?.
     * 
     * @param sessionId ID
     * @param scopes 
     * @param applicationName ??
     * @return (RedirectRepresentation)
     */
    private RedirectRepresentation callAuthPageServerResource(final String sessionId, final ArrayList<Scope> scopes,
            final String applicationName) {

        Series<Cookie> cookies = new Series<>(Cookie.class);
        cookies.add(AuthorizationBaseServerResource.ClientCookieID, sessionId);

        // scopes????
        ArrayList<String> strScopes = ScopeUtil.scopesToStrings(scopes);

        ArrayList<String> actions = new ArrayList<>();
        actions.add(AuthPageServerResource.ACTION_ACCEPT);

        ArrayList<String> applicationNames = new ArrayList<>();
        applicationNames.add(applicationName);

        AuthPageServerResource.getQuery().put(AuthPageServerResource.SCOPE, strScopes);
        AuthPageServerResource.getQuery().put(AuthPageServerResource.GRANTED_SCOPE, new ArrayList<String>());
        AuthPageServerResource.getQuery().put(AuthPageServerResource.ACTION, actions);
        AuthPageServerResource.getQuery().put(AuthPageServerResource.APPLICATION_NAME, applicationNames);

        Request request = new Request();
        request.setCookies(cookies);
        Response response = new Response(request);
        AuthPageServerResource.init(request, response);

        RedirectRepresentation redirectRepresentation = null;
        try {
            Representation representation = AuthPageServerResource.showPage();
            if (representation != null) {
                if (representation instanceof RedirectRepresentation) {
                    redirectRepresentation = (RedirectRepresentation) representation;
                }
            }
        } catch (OAuthException e) {
            e.printStackTrace();
        }

        return redirectRepresentation;
    }

    /**
     * ??????.
     * 
     * @param client 
     * @param authCode ??(TokenManager.sessions???)
     * @param applicationName ??
     * @return not null:  / null: ?
     */
    private String callAccessTokenServerResource(final Client client, final String authCode,
            final String applicationName) {

        Request request = new Request();
        ClientInfo clientInfo = new ClientInfo();
        org.restlet.security.User user = new org.restlet.security.User(client.getClientId());
        clientInfo.setUser(user);
        request.setClientInfo(clientInfo);

        Response response = new Response(request);
        AccessTokenServerResource.init(request, response);

        // (???base64?)
        String base64ApplicationName = Base64.encodeToString(applicationName.getBytes(),
                Base64.URL_SAFE | Base64.NO_WRAP);
        StringRepresentation input = new StringRepresentation("grant_type=authorization_code&code=" + authCode + "&"
                + AccessTokenServerResource.REDIR_URI + "=" + DUMMY_REDIRECT_URI + "&"
                + AccessTokenServerResource.APPLICATION_NAME + "=" + base64ApplicationName);
        try {
            ResultRepresentation resultRepresentation = (ResultRepresentation) AccessTokenServerResource
                    .requestToken(input);
            if (resultRepresentation.getResult()) {
                return resultRepresentation.getText();
            }
        } catch (JSONException | OAuthException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * .
     * 
     * @param user ID
     * @param pass 
     */
    private void addUserData(final String user, final String pass) {
        mUserManager.addUser(user).setPassword(pass.toCharArray());
    }

    /**
     * .
     * 
     * @param packageInfo ??
     * @return 
     */
    private Client addClientData(final PackageInfoOAuth packageInfo) {
        String[] redirectURIs = { DUMMY_REDIRECT_URI };
        Map<String, Object> params = new HashMap<>();
        return mClientManager.createClient(packageInfo, ClientType.CONFIDENTIAL, redirectURIs, params);
    }

    /**
     * .
     * 
     * @param clientId ID
     */
    private void removeClientData(final String clientId) {
        Client client = mClientManager.findById(clientId);
        if (client != null) {
            mClientManager.deleteClient(clientId);
        }
    }

    /**
     * ????????.
     * @param clientId 
     */
    private void removeTokenData(final String clientId) {
        Client client = mClientManager.findById(clientId);
        if (client != null) {
            mTokenManager.revokeToken(client);
        }
    }

    /**
     * ?????.
     * 
     * @param request 
     */
    private void enqueueRequest(final ConfirmAuthRequest request) {
        synchronized (mLockForRequestQueue) {
            mRequestQueue.add(request);
        }
    }

    /**
     * ???????.
     * 
     * @return not null: ??? / null: ???
     */
    private ConfirmAuthRequest pickupRequest() {
        synchronized (mLockForRequestQueue) {
            while (mRequestQueue.size() > 0) {
                ConfirmAuthRequest request = mRequestQueue.get(0);
                if (System.currentTimeMillis() - request.getRequestTime() < 60 * 1000) {
                    return request;
                }
                mRequestQueue.remove(0);
            }
        }
        return null;
    }

    /**
     * threadId????????(?????).
     * 
     * @param threadId ??ID
     * @param isDeleteRequest true: ID????????? / false: ???
     * @return not null: ??? / null: ???(???threadId?????????)
     */
    private ConfirmAuthRequest dequeueRequest(final long threadId, final boolean isDeleteRequest) {
        ConfirmAuthRequest request = null;
        synchronized (mLockForRequestQueue) {
            // ID???
            int requestCount = mRequestQueue.size();
            for (int i = 0; i < requestCount; i++) {
                ConfirmAuthRequest req = mRequestQueue.get(i);
                if (req.getThreadId() == threadId) {
                    if (isDeleteRequest) {
                        // ID???????
                        request = mRequestQueue.remove(i);
                    } else {
                        // ID???
                        request = mRequestQueue.get(i);
                    }
                    break;
                }
            }
        }
        return request;
    }

    /**
     * Scope[]?AccessTokenScope[]?????.
     * @param scopes Scope[]?
     * @return AccessTokenScope[]?
     */
    private AccessTokenScope[] scopesToAccessTokenScopes(final Scope[] scopes) {
        if (scopes != null && scopes.length > 0) {
            AccessTokenScope[] accessTokenScopes = new AccessTokenScope[scopes.length];
            for (int i = 0; i < scopes.length; i++) {
                Scope scope = scopes[i];
                accessTokenScopes[i] = new AccessTokenScope(scope.getScope(), scope.getExpirePeriod());
            }
            return accessTokenScopes;
        }
        return null;
    }

    /**
     * ??????.
     * @param request 
     */
    public void startConfirmAuthActivity(final ConfirmAuthRequest request) {
        if (request == null) {
            return;
        }

        android.content.Context context = request.getConfirmAuthParams().getContext();
        long threadId = request.getThreadId();
        ConfirmAuthParams params = request.getConfirmAuthParams();
        String[] displayScopes = request.getDisplayScopes();

        // Activity(??????ApprovalHandler??)
        Intent intent = new Intent();
        intent.setClass(params.getContext(), ConfirmAuthActivity.class);
        intent.putExtra(ConfirmAuthActivity.EXTRA_THREAD_ID, threadId);
        if (params.getServiceId() != null) {
            intent.putExtra(ConfirmAuthActivity.EXTRA_SERVICE_ID, params.getServiceId());
        }
        intent.putExtra(ConfirmAuthActivity.EXTRA_APPLICATION_NAME, params.getApplicationName());
        intent.putExtra(ConfirmAuthActivity.EXTRA_SCOPES, params.getScopes());
        intent.putExtra(ConfirmAuthActivity.EXTRA_DISPLAY_SCOPES, displayScopes);
        intent.putExtra(ConfirmAuthActivity.EXTRA_REQUEST_TIME, request.getRequestTime());
        intent.putExtra(ConfirmAuthActivity.EXTRA_IS_FOR_DEVICEPLUGIN, params.isForDevicePlugin());
        if (!params.isForDevicePlugin()) {
            intent.putExtra(ConfirmAuthActivity.EXTRA_PACKAGE_NAME, context.getPackageName());
            intent.putExtra(ConfirmAuthActivity.EXTRA_KEYWORD, params.getKeyword());
        }
        intent.putExtra(ConfirmAuthActivity.EXTRA_AUTO_FLAG, request.isAutoFlag());
        intent.setFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(intent);

        request.startTimer(new ConfirmAuthRequest.OnTimeoutCallback() {
            @Override
            public void onTimeout() {
                processApproval(request.getThreadId(), false);
            }
        });
    }

    /**
     * ??????????LocalBroadcastReceiver???.
     *
     * @param context ?
     */
    private void register(android.content.Context context) {
        IntentFilter filter = new IntentFilter();
        filter.addAction(ACTION_TOKEN_APPROVAL);
        LocalBroadcastManager.getInstance(context).registerReceiver(mBroadcastReceiver, filter);
    }

    /**
     * ??????????LocalBroadcastReceiver???.
     *
     * @param context ?
     */
    private void unregister(android.content.Context context) {
        LocalBroadcastManager.getInstance(context).unregisterReceiver(mBroadcastReceiver);
    }

    /**
     * ??????????LocalBroadcastReceiver.
     */
    private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(android.content.Context context, Intent intent) {
            if (intent == null) {
                return;
            }

            String action = intent.getAction();
            if (ACTION_TOKEN_APPROVAL.equals(action)) {
                long threadId = intent.getLongExtra(EXTRA_THREAD_ID, -1);
                boolean isApproval = intent.getBooleanExtra(EXTRA_APPROVAL, false);
                processApproval(threadId, isApproval);
            }
        }
    };
}