org.deviceconnect.android.manager.DConnectMessageService.java Source code

Java tutorial

Introduction

Here is the source code for org.deviceconnect.android.manager.DConnectMessageService.java

Source

/*
 DConnectMessageService.java
 Copyright (c) 2014 NTT DOCOMO,INC.
 Released under the MIT license
 http://opensource.org/licenses/mit-license.php
 */
package org.deviceconnect.android.manager;

import android.app.PendingIntent;
import android.app.Service;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;

import org.deviceconnect.android.event.Event;
import org.deviceconnect.android.event.EventManager;
import org.deviceconnect.android.event.cache.MemoryCacheController;
import org.deviceconnect.android.localoauth.CheckAccessTokenResult;
import org.deviceconnect.android.localoauth.ClientPackageInfo;
import org.deviceconnect.android.localoauth.LocalOAuth2Main;
import org.deviceconnect.android.logger.AndroidHandler;
import org.deviceconnect.android.manager.DConnectLocalOAuth.OAuthData;
import org.deviceconnect.android.manager.DevicePluginManager.DevicePluginEventListener;
import org.deviceconnect.android.manager.hmac.HmacManager;
import org.deviceconnect.android.manager.policy.OriginParser;
import org.deviceconnect.android.manager.policy.Whitelist;
import org.deviceconnect.android.manager.profile.AuthorizationProfile;
import org.deviceconnect.android.manager.profile.DConnectAvailabilityProfile;
import org.deviceconnect.android.manager.profile.DConnectDeliveryProfile;
import org.deviceconnect.android.manager.profile.DConnectFilesProfile;
import org.deviceconnect.android.manager.profile.DConnectServiceDiscoveryProfile;
import org.deviceconnect.android.manager.profile.DConnectSystemProfile;
import org.deviceconnect.android.manager.request.DConnectRequest;
import org.deviceconnect.android.manager.request.DConnectRequestManager;
import org.deviceconnect.android.manager.request.DiscoveryDeviceRequest;
import org.deviceconnect.android.manager.request.RegisterNetworkServiceDiscovery;
import org.deviceconnect.android.manager.setting.SettingActivity;
import org.deviceconnect.android.manager.util.DConnectUtil;
import org.deviceconnect.android.message.MessageUtils;
import org.deviceconnect.android.profile.DConnectProfile;
import org.deviceconnect.android.profile.DConnectProfileProvider;
import org.deviceconnect.android.profile.ServiceDiscoveryProfile;
import org.deviceconnect.android.provider.FileManager;
import org.deviceconnect.message.DConnectMessage;
import org.deviceconnect.message.intent.message.IntentDConnectMessage;
import org.deviceconnect.profile.ServiceDiscoveryProfileConstants;

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

/**
 * DConnectMessage??.
 * @author NTT DOCOMO, INC.
 */
public abstract class DConnectMessageService extends Service
        implements DConnectProfileProvider, DevicePluginEventListener {
    /** ??. */
    private static final String DCONNECT_DOMAIN = ".deviceconnect.org";
    /** ???. */
    private static final String LOCALHOST_DCONNECT = "localhost" + DCONNECT_DOMAIN;
    /** file?. */
    private static final String ORIGIN_FILE = "file://";
    /** ???. */
    private static final String[] IGNORED_ORIGINS = { ORIGIN_FILE };

    /** Notification ID.*/
    private static final int ONGOING_NOTIFICATION_ID = 4035;

    /** ID?. */
    public static final String SEPARATOR = ".";

    /** ?receiver?. */
    public static final String SEPARATOR_SESSION = "@";

    /** ?. */
    private static final int ERROR_CODE = Integer.MIN_VALUE;

    /** URI??. */
    private static final String SCHEME_LAUNCH = "dconnect";

    /** . */
    protected final Logger mLogger = Logger.getLogger("dconnect.manager");

    /** dConnect Manager???. */
    private String mDConnectDomain = LOCALHOST_DCONNECT;

    /** . */
    private Map<String, DConnectProfile> mProfileMap = new HashMap<String, DConnectProfile>();

    /** ???. */
    private DConnectProfile mDeliveryProfile;

    /** ?. */
    protected DConnectRequestManager mRequestManager;

    /** ??. */
    protected DevicePluginManager mPluginMgr;

    /** DeviceConnect?. */
    protected DConnectSettings mSettings;

    /** ??. */
    protected FileManager mFileMgr;

    /** Local OAuth???. */
    private DConnectLocalOAuth mLocalOAuth;

    /** HMAC?. */
    private HmacManager mHmacManager;

    /** ?. */
    private Whitelist mWhitelist;

    /** ??. */
    protected boolean mRunningFlag;

    @Override
    public IBinder onBind(final Intent intent) {
        return null;
    }

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

        if (BuildConfig.DEBUG) {
            AndroidHandler handler = new AndroidHandler("dconnect.manager");
            handler.setFormatter(new SimpleFormatter());
            handler.setLevel(Level.ALL);
            mLogger.addHandler(handler);
            mLogger.setLevel(Level.ALL);
        } else {
            mLogger.setLevel(Level.OFF);
        }

        // ???
        EventManager.INSTANCE.setController(new MemoryCacheController());

        // Local OAuth??
        LocalOAuth2Main.initialize(getApplicationContext());

        // DConnect
        mSettings = DConnectSettings.getInstance();
        mSettings.load(this);

        // ?
        mFileMgr = new FileManager(this);

        // ???Local OAuth
        mLocalOAuth = new DConnectLocalOAuth(this);

        // ????
        mPluginMgr = new DevicePluginManager(this, mDConnectDomain);
        mPluginMgr.setEventListener(this);

        // ?
        addProfile(new AuthorizationProfile());
        addProfile(new DConnectAvailabilityProfile());
        addProfile(new DConnectServiceDiscoveryProfile(this, mPluginMgr));
        addProfile(new DConnectFilesProfile(this));
        addProfile(new DConnectSystemProfile(this, mPluginMgr));

        // dConnect Manager????????????
        setDeliveryProfile(new DConnectDeliveryProfile(mPluginMgr, mLocalOAuth, mSettings.requireOrigin()));
    }

    @Override
    public void onDestroy() {
        stopDConnect();
        LocalOAuth2Main.destroy();
        super.onDestroy();
    }

    @Override
    public int onStartCommand(final Intent intent, final int flags, final int startId) {
        super.onStartCommand(intent, flags, startId);
        if (intent == null) {
            mLogger.warning("intent is null.");
            return START_STICKY;
        }

        if (!mRunningFlag) {
            return START_STICKY;
        }

        String action = intent.getAction();
        if (action == null) {
            mLogger.warning("action is null.");
            return START_STICKY;
        }

        String scheme = intent.getScheme();
        if (SCHEME_LAUNCH.equals(scheme)) {
            String key = intent.getStringExtra(IntentDConnectMessage.EXTRA_KEY);
            String origin = intent.getStringExtra(IntentDConnectMessage.EXTRA_ORIGIN);
            if (key != null && !TextUtils.isEmpty(origin)) {
                mHmacManager.updateKey(origin, key);
            }
            return START_STICKY;
        }

        if (checkAction(action)) {
            onRequestReceive(intent);
        } else if (IntentDConnectMessage.ACTION_RESPONSE.equals(action)) {
            onResponseReceive(intent);
        } else if (IntentDConnectMessage.ACTION_EVENT.equals(action)) {
            onEventReceive(intent);
        } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
            mPluginMgr.checkAndAddDevicePlugin(intent);
        } else if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
            mPluginMgr.checkAndRemoveDevicePlugin(intent);
        }

        return START_STICKY;
    }

    /**
     * Intent?????????.
     * @param request Intent
     */
    public void onRequestReceive(final Intent request) {
        // ?????????
        int requestCode = request.getIntExtra(IntentDConnectMessage.EXTRA_REQUEST_CODE, ERROR_CODE);
        if (requestCode == ERROR_CODE) {
            mLogger.warning("Illegal requestCode in onRequestReceive. requestCode=" + requestCode);
            return;
        }

        // ????????
        if (mFileMgr != null) {
            mFileMgr.checkAndRemove();
        }

        // ??Intent??
        Intent response = new Intent(IntentDConnectMessage.ACTION_RESPONSE);
        response.putExtra(DConnectMessage.EXTRA_RESULT, DConnectMessage.RESULT_ERROR);
        response.putExtra(DConnectMessage.EXTRA_REQUEST_CODE, requestCode);

        // ??
        String profileName = request.getStringExtra(DConnectMessage.EXTRA_PROFILE);
        OriginError error = checkOrigin(request);
        switch (error) {
        case NOT_SPECIFIED:
            MessageUtils.setInvalidOriginError(response, "Origin is not specified.");
            sendResponse(request, response);
            return;
        case NOT_UNIQUE:
            MessageUtils.setInvalidOriginError(response, "The specified origin is not unique.");
            sendResponse(request, response);
            return;
        case NOT_ALLOWED:
            // NOTE: Local OAuth?API??
            DConnectProfile profile = getProfile(profileName);
            if (profile != null && profile instanceof AuthorizationProfile) {
                ((AuthorizationProfile) profile).onInvalidOrigin(request, response);
            }

            MessageUtils.setInvalidOriginError(response, "The specified origin is not allowed.");
            sendResponse(request, response);
            return;
        default:
            break;
        }

        if (profileName == null) {
            MessageUtils.setNotSupportProfileError(response);
            sendResponse(request, response);
            return;
        }

        if (mSettings.isUseALocalOAuth()) {
            // ??
            String accessToken = request.getStringExtra(AuthorizationProfile.PARAM_ACCESS_TOKEN);
            CheckAccessTokenResult result = LocalOAuth2Main.checkAccessToken(accessToken, profileName,
                    DConnectLocalOAuth.IGNORE_PROFILES);
            if (result.checkResult()) {
                executeRequest(request, response);
            } else {
                if (accessToken == null) {
                    MessageUtils.setEmptyAccessTokenError(response);
                } else if (!result.isExistAccessToken()) {
                    MessageUtils.setNotFoundClientId(response);
                } else if (!result.isExistClientId()) {
                    MessageUtils.setNotFoundClientId(response);
                } else if (!result.isExistScope()) {
                    MessageUtils.setScopeError(response);
                } else if (!result.isNotExpired()) {
                    MessageUtils.setExpiredAccessTokenError(response);
                } else {
                    MessageUtils.setAuthorizationError(response);
                }
                sendResponse(request, response);
            }
        } else {
            executeRequest(request, response);
        }
    }

    /**
     * ???.
     * <p>
     * ????OFF??????????
     * </p>
     * @param request ??
     * @return ????
     */
    private OriginError checkOrigin(final Intent request) {
        if (!mSettings.requireOrigin()) {
            return OriginError.NONE;
        }
        String originParam = request.getStringExtra(IntentDConnectMessage.EXTRA_ORIGIN);
        if (originParam == null) {
            return OriginError.NOT_SPECIFIED;
        }
        String[] origins = originParam.split(" ");
        if (origins.length != 1) {
            return OriginError.NOT_UNIQUE;
        }
        if (!allowsOrigin(request)) {
            return OriginError.NOT_ALLOWED;
        }
        return OriginError.NONE;
    }

    /**
     * ???.
     * @param response ?Intent
     */
    public void onResponseReceive(final Intent response) {
        // ?????????
        int requestCode = response.getIntExtra(IntentDConnectMessage.EXTRA_REQUEST_CODE, ERROR_CODE);
        if (requestCode == ERROR_CODE) {
            mLogger.warning("Illegal requestCode in onResponseReceive. requestCode=" + requestCode);
            return;
        }

        // ????
        mRequestManager.setResponse(response);
    }

    /**
     * ??.
     * @param event Intent
     */
    public void onEventReceive(final Intent event) {
        String sessionKey = event.getStringExtra(DConnectMessage.EXTRA_SESSION_KEY);
        String serviceId = event.getStringExtra(DConnectMessage.EXTRA_SERVICE_ID);
        String profile = event.getStringExtra(DConnectMessage.EXTRA_PROFILE);
        String inter = event.getStringExtra(DConnectMessage.EXTRA_INTERFACE);
        String attribute = event.getStringExtra(DConnectMessage.EXTRA_ATTRIBUTE);

        if (BuildConfig.DEBUG) {
            mLogger.info(String.format(
                    "onEventReceive: [sessionKey: %s serviceId: %s profile: %s inter: %s attribute: %s]",
                    sessionKey, serviceId, profile, inter, attribute));
        }

        if (sessionKey != null) {
            // ?receiver??
            String receiver = null;
            int index = sessionKey.indexOf(SEPARATOR_SESSION);
            if (index > 0) {
                receiver = sessionKey.substring(index + 1);
                sessionKey = sessionKey.substring(0, index);
            }
            // ????ID?
            String pluginId = convertSessionKey2PluginId(sessionKey);
            String key = convertSessionKey2Key(sessionKey);
            DevicePlugin plugin = mPluginMgr.getDevicePlugin(pluginId);
            if (plugin == null) {
                mLogger.warning("plugin is null.");
                return;
            }
            String did = mPluginMgr.appendServiceId(plugin, serviceId);
            event.putExtra(DConnectMessage.EXTRA_SESSION_KEY, key);

            // Local OAuth????????clientId????
            // ???
            if (ServiceDiscoveryProfileConstants.PROFILE_NAME.equals(profile)
                    || ServiceDiscoveryProfileConstants.ATTRIBUTE_ON_SERVICE_CHANGE.equals(attribute)) {

                // network service discovery?????networkService??????
                Bundle service = (Bundle) event.getParcelableExtra(ServiceDiscoveryProfile.PARAM_NETWORK_SERVICE);
                String id = service.getString(ServiceDiscoveryProfile.PARAM_ID);
                did = mPluginMgr.appendServiceId(plugin, id);

                // ID
                replaceServiceId(event, plugin);

                OAuthData oauth = mLocalOAuth.getOAuthData(did);
                if (oauth == null && plugin != null) {
                    createClientOfDevicePlugin(plugin, did, event);
                } else {
                    // ???
                    List<Event> evts = EventManager.INSTANCE.getEventList(profile, attribute);
                    for (int i = 0; i < evts.size(); i++) {
                        Event evt = evts.get(i);
                        event.putExtra(DConnectMessage.EXTRA_SESSION_KEY, evt.getSessionKey());
                        sendEvent(evt.getReceiverName(), event);
                    }
                }
            } else {
                replaceServiceId(event, plugin);
                sendEvent(receiver, event);
            }
        } else {
            mLogger.warning("onEventReceive: sessionKey is null.");
        }
    }

    /**
     * ?.
     * @param request 
     * @param response ?
     */
    private void executeRequest(final Intent request, final Intent response) {
        // ?DeviceConnectManager??
        request.putExtra(DConnectMessage.EXTRA_PRODUCT, getString(R.string.app_name));
        request.putExtra(DConnectMessage.EXTRA_VERSION, DConnectUtil.getVersionName(this));

        boolean send = false;
        String profileName = request.getStringExtra(DConnectMessage.EXTRA_PROFILE);
        DConnectProfile profile = getProfile(profileName);
        if (profile != null) {
            send = profile.onRequest(request, response);
        }
        if (!send) {
            sendDeliveryProfile(request, response);
        }
    }

    /**
     * ?ID???.
     * 
     * 
     * @param sessionkey 
     * @return ID
     */
    private String convertSessionKey2PluginId(final String sessionkey) {
        int index = sessionkey.lastIndexOf(SEPARATOR);
        if (index > 0) {
            return sessionkey.substring(index + 1);
        }
        return sessionkey;
    }

    /**
     * ???????????.
     * @param sessionkey 
     * @return ?
     */
    private String convertSessionKey2Key(final String sessionkey) {
        int index = sessionkey.lastIndexOf(SEPARATOR);
        if (index > 0) {
            return sessionkey.substring(0, index);
        }
        return sessionkey;
    }

    /**
     * ?.
     * @param request ?
     */
    public void addRequest(final DConnectRequest request) {
        mRequestManager.addRequest(request);
    }

    /**
     * DConnectLocalOAuth???.
     * @return DConnectLocalOAuth?
     */
    public DConnectLocalOAuth getLocalOAuth() {
        return mLocalOAuth;
    }

    @Override
    public List<DConnectProfile> getProfileList() {
        List<DConnectProfile> profileList = new ArrayList<DConnectProfile>(mProfileMap.values());
        return profileList;
    }

    @Override
    public void addProfile(final DConnectProfile profile) {
        if (profile != null) {
            profile.setContext(this);
            mProfileMap.put(profile.getProfileName(), profile);
        }
    }

    @Override
    public void removeProfile(final DConnectProfile profile) {
        if (profile != null) {
            mProfileMap.remove(profile.getProfileName());
        }
    }

    /**
     * ??????????????.
     * @param profile 
     */
    public void setDeliveryProfile(final DConnectProfile profile) {
        if (profile != null) {
            profile.setContext(this);
            mDeliveryProfile = profile;
        }
    }

    /**
     * ?????DConnectProfile??.
     * ?????DConnectProfile???????null??
     * @param name ??
     * @return DConnectProfile?
     */
    public DConnectProfile getProfile(final String name) {
        if (name == null) {
            return null;
        }
        return mProfileMap.get(name);
    }

    @Override
    public void onDeviceFound(final DevicePlugin plugin) {
        RegisterNetworkServiceDiscovery req = new RegisterNetworkServiceDiscovery();
        req.setContext(this);
        req.setSessionKey(plugin.getServiceId());
        req.setDestination(plugin);
        req.setDevicePluginManager(mPluginMgr);
        addRequest(req);
    }

    @Override
    public void onDeviceLost(final DevicePlugin plugin) {
        mLocalOAuth.deleteOAuthDatas(plugin.getServiceId());
    }

    /**
     * ???dConnect???.
     * @param action 
     * @return dConnect????true, ???false
     */
    private boolean checkAction(final String action) {
        return (action.equals(IntentDConnectMessage.ACTION_GET) || action.equals(IntentDConnectMessage.ACTION_PUT)
                || action.equals(IntentDConnectMessage.ACTION_POST)
                || action.equals(IntentDConnectMessage.ACTION_DELETE));
    }

    /**
     * DConnectManager?
     */
    protected synchronized void startDConnect() {
        mRunningFlag = true;

        // ?
        mSettings.load(this);

        if (BuildConfig.DEBUG) {
            mLogger.info("Settings");
            mLogger.info("    SSL: " + mSettings.isSSL());
            mLogger.info("    Host: " + mSettings.getHost());
            mLogger.info("    Port: " + mSettings.getPort());
            mLogger.info("    LocalOAuth: " + mSettings.isUseALocalOAuth());
            mLogger.info("    OriginBlock: " + mSettings.isBlockingOrigin());
        }

        // HMAC?
        mHmacManager = new HmacManager(this);
        // ?
        mWhitelist = new Whitelist(this);
        // ???
        mRequestManager = new DConnectRequestManager();
        // ??
        mPluginMgr.createDevicePluginList();
        showNotification();
    }

    /**
     * DConnectManager??.
     */
    protected synchronized void stopDConnect() {
        mRunningFlag = false;

        mRequestManager.shutdown();
        hideNotification();
    }

    /**
     * ??????.
     * 
     * ??????
     * ?????????????????
     * 
     * @param request 
     * @param response ?
     */
    private void sendDeliveryProfile(final Intent request, final Intent response) {
        mDeliveryProfile.onRequest(request, response);
    }

    /**
     * ?ID??.
     * <br>
     * 
     * ??????ID?????ID?????
     * dConnect Manager???IDID?????DNS???????
     *
     * @param event Intent
     * @param plugin ???
     */
    private void replaceServiceId(final Intent event, final DevicePlugin plugin) {
        String serviceId = event.getStringExtra(IntentDConnectMessage.EXTRA_SERVICE_ID);
        event.putExtra(IntentDConnectMessage.EXTRA_SERVICE_ID, mPluginMgr.appendServiceId(plugin, serviceId));
    }

    /**
     * ??
     */
    protected void showNotification() {
        Intent notificationIntent = new Intent(getApplicationContext(), SettingActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(getApplicationContext(), 0, notificationIntent, 0);
        NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext());
        builder.setContentIntent(pendingIntent);
        builder.setTicker(getString(R.string.app_name));
        builder.setContentTitle(getString(R.string.app_name));
        builder.setContentText(DConnectUtil.getIPAddress(this) + ":" + mSettings.getPort());
        int iconType = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP ? R.drawable.icon : R.drawable.on_icon;
        builder.setSmallIcon(iconType);

        startForeground(ONGOING_NOTIFICATION_ID, builder.build());
    }

    /**
     * ??
     */
    protected void hideNotification() {
        stopForeground(true);
    }

    /**
     * ????.
     * @param plugin ???
     * @param serviceId ID
     * @param event ??
     */
    private void createClientOfDevicePlugin(final DevicePlugin plugin, final String serviceId, final Intent event) {
        Intent intent = new Intent(IntentDConnectMessage.ACTION_GET);
        intent.setComponent(plugin.getComponentName());
        intent.putExtra(DConnectMessage.EXTRA_PROFILE, ServiceDiscoveryProfileConstants.PROFILE_NAME);
        intent.putExtra(DConnectMessage.EXTRA_SERVICE_ID, serviceId);

        DiscoveryDeviceRequest request = new DiscoveryDeviceRequest();
        request.setContext(this);
        request.setLocalOAuth(mLocalOAuth);
        request.setUseAccessToken(true);
        request.setRequireOrigin(true);
        request.setDestination(plugin);
        request.setRequest(intent);
        request.setEvent(event);
        request.setDevicePluginManager(mPluginMgr);
        addRequest(request);
    }

    /**
     * ??Intent??.
     * @param request 
     * @param response ???
     * @return ???Intent
     */
    protected Intent createResponseIntent(final Intent request, final Intent response) {
        int requestCode = request.getIntExtra(IntentDConnectMessage.EXTRA_REQUEST_CODE, ERROR_CODE);
        ComponentName cn = request.getParcelableExtra(IntentDConnectMessage.EXTRA_RECEIVER);

        Intent intent = new Intent(response);
        intent.putExtra(IntentDConnectMessage.EXTRA_REQUEST_CODE, requestCode);
        intent.putExtra(IntentDConnectMessage.EXTRA_PRODUCT, getString(R.string.app_name));
        intent.putExtra(IntentDConnectMessage.EXTRA_VERSION, DConnectUtil.getVersionName(this));

        // HMAC?
        String origin = request.getStringExtra(IntentDConnectMessage.EXTRA_ORIGIN);
        if (origin == null) {
            String accessToken = request.getStringExtra(DConnectMessage.EXTRA_ACCESS_TOKEN);
            if (accessToken != null) {
                origin = findOrigin(accessToken);
            }
        }
        if (origin != null) {
            if (mHmacManager.usesHmac(origin)) {
                String nonce = request.getStringExtra(IntentDConnectMessage.EXTRA_NONCE);
                if (nonce != null) {
                    String hmac = mHmacManager.generateHmac(origin, nonce);
                    if (hmac != null) {
                        intent.putExtra(IntentDConnectMessage.EXTRA_HMAC, hmac);
                    }
                }
            }
        } else {
            mLogger.warning("Origin is not found.");
        }

        intent.setComponent(cn);
        return intent;
    }

    /**
     * ???Origin??.
     * 
     * @param accessToken 
     * @return Origin
     */
    private String findOrigin(final String accessToken) {
        ClientPackageInfo packageInfo = LocalOAuth2Main.findClientPackageInfoByAccessToken(accessToken);
        if (packageInfo == null) {
            return null;
        }
        // Origin is a package name of LocalOAuth client.
        return packageInfo.getPackageInfo().getPackageName();
    }

    /**
     * ???????????.
     * 
     * @param request ???
     * @return ????????<code>true</code>?
     *      ????????<code>false</code>
     */
    private boolean allowsOrigin(final Intent request) {
        String originExp = request.getStringExtra(IntentDConnectMessage.EXTRA_ORIGIN);
        if (originExp == null) {
            // NOTE: ???????????
            // ?????????????.
            return false;
        }
        for (int i = 0; i < IGNORED_ORIGINS.length; i++) {
            if (originExp.equals(IGNORED_ORIGINS[i])) {
                return true;
            }
        }
        if (!mSettings.isBlockingOrigin()) {
            return true;
        }
        return mWhitelist.allows(OriginParser.parse(originExp));
    }

    /**
     * ??????.
     * @param request ??
     * @param response ???
     */
    public void sendResponse(final Intent request, final Intent response) {
        sendBroadcast(createResponseIntent(request, response));
    }

    /**
     * ??.
     * @param receiver ??BroadcastReceiver
     * @param event ??
     */
    public void sendEvent(final String receiver, final Intent event) {
        if (BuildConfig.DEBUG) {
            mLogger.info(String.format("sendEvent: %s intent: %s", receiver, event.getExtras()));
        }
        Intent targetIntent = new Intent(event);
        targetIntent.setComponent(ComponentName.unflattenFromString(receiver));
        sendBroadcast(targetIntent);
    }

    /**
     * Origin????.
     */
    private enum OriginError {
        /**
         * ??.
         */
        NONE,

        /**
         * ?????????.
         */
        NOT_SPECIFIED,

        /**
         * 2??????????.
         */
        NOT_UNIQUE,

        /**
         * ?????????(???????)???.
         */
        NOT_ALLOWED
    }
}