Java tutorial
/* DevicePluginContext.java Copyright (c) 2018 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.message; import android.Manifest; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.net.ConnectivityManager; import android.net.wifi.WifiManager; import android.os.Bundle; import android.support.v4.content.ContextCompat; import org.deviceconnect.android.BuildConfig; import org.deviceconnect.android.IDConnectCallback; import org.deviceconnect.android.event.Event; import org.deviceconnect.android.event.EventManager; import org.deviceconnect.android.event.cache.EventCacheController; import org.deviceconnect.android.event.cache.MemoryCacheController; import org.deviceconnect.android.localoauth.CheckAccessTokenResult; import org.deviceconnect.android.localoauth.DevicePluginXmlProfile; import org.deviceconnect.android.localoauth.DevicePluginXmlUtil; import org.deviceconnect.android.localoauth.LocalOAuth2Main; import org.deviceconnect.android.profile.AuthorizationProfile; import org.deviceconnect.android.profile.DConnectProfile; import org.deviceconnect.android.profile.DConnectProfileProvider; import org.deviceconnect.android.profile.ServiceDiscoveryProfile; import org.deviceconnect.android.profile.SystemProfile; import org.deviceconnect.android.profile.spec.DConnectPluginSpec; import org.deviceconnect.android.service.DConnectService; import org.deviceconnect.android.service.DConnectServiceManager; import org.deviceconnect.android.service.DConnectServiceProvider; import org.deviceconnect.android.ssl.EndPointKeyStoreManager; import org.deviceconnect.android.ssl.KeyStoreCallback; import org.deviceconnect.android.ssl.KeyStoreError; import org.deviceconnect.android.ssl.KeyStoreManager; import org.deviceconnect.message.DConnectMessage; import org.deviceconnect.message.intent.message.IntentDConnectMessage; import org.deviceconnect.profile.AuthorizationProfileConstants; import org.deviceconnect.profile.AvailabilityProfileConstants; import org.deviceconnect.profile.ServiceDiscoveryProfileConstants; import org.deviceconnect.profile.SystemProfileConstants; import java.security.GeneralSecurityException; import java.security.KeyStore; import java.security.SecureRandom; import java.security.cert.Certificate; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.logging.Logger; import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManagerFactory; /** * ???. * * @author NTT DOCOMO, INC. */ public abstract class DevicePluginContext implements DConnectProfileProvider, DConnectProfile.Responder { /** * . */ private Logger mLogger = Logger.getLogger("org.deviceconnect.dplugin"); /** * LocalOAuth??. */ private static final String[] IGNORE_PROFILES = { AuthorizationProfileConstants.PROFILE_NAME.toLowerCase(), SystemProfileConstants.PROFILE_NAME.toLowerCase(), ServiceDiscoveryProfileConstants.PROFILE_NAME.toLowerCase() }; /** * . */ private Context mContext; /** * . */ private Map<String, DConnectProfile> mProfileMap = new HashMap<>(); /** * Device Connect Manager ????. */ private IDConnectCallback mIDConnectCallback; /** * ??. */ private DConnectServiceManager mServiceProvider; /** * ???. */ private DConnectPluginSpec mPluginSpec; /** * ??(Local OAuth). */ private LocalOAuth2Main mLocalOAuth2Main; /** * Wi-Fi?????????. * <p> * IP ???????????????? * </p> */ private BroadcastReceiver mWiFiBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { // requestAndNotifyKeyStore(); } }; /** * ??. */ private KeyStoreManager mKeyStoreMgr; /** * Local OAuth. * <p> * ?? true ???????? * </p> */ private boolean mUseLocalOAuth = true; /** * ?. */ private boolean mIsEnabled; /** * . * @param context */ public DevicePluginContext(final Context context) { if (context == null) { throw new IllegalArgumentException("context is null."); } mContext = context; // ??? EventManager.INSTANCE.setController(createEventCacheController()); // LocalOAuth?? mLocalOAuth2Main = new LocalOAuth2Main(context); try { // SPEC?? mPluginSpec = DConnectProfileHelper.loadPluginSpec(context, createSupportedProfiles()); } catch (Exception e) { if (BuildConfig.DEBUG) { mLogger.warning("Failed to load a profile spec."); } } // ??? mKeyStoreMgr = new EndPointKeyStoreManager(context, getKeyStoreFileName(), getCertificateAlias()); if (usesAutoCertificateRequest()) { requestAndNotifyKeyStore(); IntentFilter filter = new IntentFilter(); filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); context.registerReceiver(mWiFiBroadcastReceiver, filter); } // ? mServiceProvider = new DConnectServiceManager(); mServiceProvider.setContext(context); mServiceProvider.setPluginContext(this); mServiceProvider.setPluginSpec(mPluginSpec); // addProfile(new AuthorizationProfile(this, mLocalOAuth2Main)); addProfile(new ServiceDiscoveryProfile(mServiceProvider)); addProfile(getSystemProfile()); } /** * ????. * <p> * ??????????<br> * ???????????<br> * </p> * <p> * ??????????super.release(); ???? * </p> */ public void release() { if (mLocalOAuth2Main != null) { mLocalOAuth2Main.destroy(); mLocalOAuth2Main = null; } if (usesAutoCertificateRequest()) { mContext.unregisterReceiver(mWiFiBroadcastReceiver); } } /** * ????. * @return */ public Context getContext() { return mContext; } /** * ????. * * @return ? */ public DConnectServiceProvider getServiceProvider() { return mServiceProvider; } /** * ??????????. * * @return ? */ public DConnectPluginSpec getPluginSpec() { return mPluginSpec; } /** * Device Connect Manager ?????????. * * @param callback ? */ public void setIDConnectCallback(final IDConnectCallback callback) { mIDConnectCallback = callback; } /** * LocalOAuth?????. * @return LocalOAuth */ public LocalOAuth2Main getLocalOAuth2Main() { return mLocalOAuth2Main; } /** * ??????. * * @return ?? */ private Map<String, DevicePluginXmlProfile> createSupportedProfiles() { return DevicePluginXmlUtil.getSupportProfiles(getContext(), getPluginXmlResId()); } /** * ?????xml??ID????. * * @return ?? */ protected int getPluginXmlResId() { return DevicePluginXmlUtil.getPluginXmlResourceId(getContext(), getContext().getPackageName()); } /** * SystemProfile??. * <p> * SystemProfile???????SystemProfile????<br> * ??????SystemProfile??? * </p> * @return SystemProfile? */ protected abstract SystemProfile getSystemProfile(); /** * ??????. * * @param message ???????Intent */ public void handleMessage(final Intent message) { String action = message.getAction(); try { if (checkRequestAction(action)) { // Device Connect ???????????? MessageConverterHelper.convert(message); onRequest(message, MessageUtils.createResponseIntent(message)); } else if (checkManagerUninstall(message)) { onManagerUninstalled(); } else if (checkManagerLaunched(action)) { onManagerLaunched(); } else if (checkManagerTerminated(action)) { onManagerTerminated(); } else if (checkManagerEventTransmitDisconnect(action)) { onManagerEventTransmitDisconnected(message.getStringExtra(IntentDConnectMessage.EXTRA_ORIGIN)); } else if (checkDevicePluginReset(action)) { onDevicePluginReset(); } else if (checkDevicePluginEnabled(action)) { mIsEnabled = true; onDevicePluginEnabled(); } else if (checkDevicePluginDisabled(action)) { mIsEnabled = false; onDevicePluginDisabled(); } else { if (BuildConfig.DEBUG) { mLogger.warning("action is unknown type. " + action); } } } catch (Exception e) { if (BuildConfig.DEBUG) { e.printStackTrace(); mLogger.severe("DevicePluginContext#handleMessage : error=" + e.getMessage()); } } } /** * EventCacheController??. * * <p> * ??MemoryCacheController?.<br> * ?????????. * </p> * * @return EventCacheController? */ protected EventCacheController getEventCacheController() { return new MemoryCacheController(); } /** * EventCacheController?????. * <p> * ???MemoryCacheController???? * </p> * @return EventCacheController? */ private EventCacheController createEventCacheController() { EventCacheController ctrl = getEventCacheController(); if (ctrl == null) { ctrl = new MemoryCacheController(); } return ctrl; } /** * Device Connect Manager??????????????. * @return ???????<code>true</code>, ????????<code>false</code> */ protected boolean isEnabled() { return mIsEnabled; } /** * Local OAuth?. * <p> * ??false??????LocalOAuth?OFF????????<br> * ???true????????LocalOAuth?????? * </p> * @param use */ public void setUseLocalOAuth(final boolean use) { mUseLocalOAuth = use; } /** * Local OAuth??. * * @return ????true????false */ public boolean isUseLocalOAuth() { return mUseLocalOAuth; } /** * LocalOAuth??. * ?????????????????. * * @return LocalOAuth?? */ public String[] getIgnoredProfiles() { return IGNORE_PROFILES; } /** * ???Local OAuth???????. * * @param profileName ?? * @return ?????true????false */ public boolean isIgnoredProfile(final String profileName) { for (String name : getIgnoredProfiles()) { if (name.equalsIgnoreCase(profileName)) { // MEMO ?? return true; } } return false; } /// DConnectProfile#Responder Method /** * Device Connect Manager???????. * * @param response ? * @return ????true????false */ @Override public boolean sendResponse(final Intent response) { if (response == null) { throw new IllegalArgumentException("response is null."); } // TODO ????????? if (BuildConfig.DEBUG) { mLogger.info("sendResponse: " + response); mLogger.info("sendResponse Extra: " + response.getExtras()); } return sendMessage(response); } /** * Device Connect???. * * @param event * @param accessToken ?? * @return ????true????false? */ @Override public boolean sendEvent(final Intent event, final String accessToken) { // TODO ???????? if (event == null) { throw new IllegalArgumentException("Event is null."); } if (isUseLocalOAuth()) { CheckAccessTokenResult result = mLocalOAuth2Main.checkAccessToken(accessToken, event.getStringExtra(DConnectMessage.EXTRA_PROFILE), getIgnoredProfiles()); if (!result.checkResult()) { return false; } } if (BuildConfig.DEBUG) { mLogger.info("sendEvent: " + event); mLogger.info("sendEvent Extra: " + event.getExtras()); } return sendMessage(event); } /** * Device Connect???. * * @param event * @param bundle * @return ????true????false? */ @Override public boolean sendEvent(final Event event, final Bundle bundle) { Intent intent = EventManager.createEventMessage(event); Bundle original = intent.getExtras(); if (original != null) { original.putAll(bundle); intent.putExtras(original); } return sendEvent(intent, event.getAccessToken()); } /// DConnectProfileProvider Method @Override public List<DConnectProfile> getProfileList() { return new ArrayList<>(mProfileMap.values()); } @Override public DConnectProfile getProfile(final String name) { // XXXX ?? return name == null ? null : mProfileMap.get(name.toLowerCase()); } @Override public void addProfile(final DConnectProfile profile) { if (profile != null) { profile.setContext(mContext); profile.setPluginContext(this); profile.setResponder(this); // XXXX ?? mProfileMap.put(profile.getProfileName().toLowerCase(), profile); } } @Override public void removeProfile(final DConnectProfile profile) { if (profile != null) { // XXXX ?? mProfileMap.remove(profile.getProfileName().toLowerCase()); } } /** * ??????. * * @param request * @param response ? */ protected void onRequest(final Intent request, final Intent response) { if (BuildConfig.DEBUG) { mLogger.info("request: " + request); mLogger.info("request extras: " + request.getExtras()); } // ???? String profileName = request.getStringExtra(DConnectMessage.EXTRA_PROFILE); if (profileName == null) { MessageUtils.setNotSupportProfileError(response); sendResponse(response); return; } boolean send = true; if (isUseLocalOAuth()) { String accessToken = request.getStringExtra(AuthorizationProfile.PARAM_ACCESS_TOKEN); CheckAccessTokenResult result = mLocalOAuth2Main.checkAccessToken(accessToken, profileName.toLowerCase(), getIgnoredProfiles()); if (result.checkResult()) { send = executeRequest(profileName, 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); } } } else { send = executeRequest(profileName, request, response); } if (send) { sendResponse(response); } } /** * ??????. * <p> * ???????????????????? * </p> * @param profileName ?? * @param request * @param response ? * @return true????????????????????? */ protected boolean executeRequest(final String profileName, final Intent request, final Intent response) { DConnectProfile profile = getProfile(profileName); if (profile == null) { String serviceId = DConnectProfile.getServiceID(request); DConnectService service = getServiceProvider().getService(serviceId); if (service != null) { return service.onRequest(request, response); } else { MessageUtils.setNotFoundServiceError(response); return true; } } else { return profile.onRequest(request, response); } } /** * Intent Device Connect Manager ?????. * * @param intent ??Intent * @return ??????true????false */ private boolean sendMessage(final Intent intent) { if (mIDConnectCallback == null) { if (BuildConfig.DEBUG) { mLogger.severe("sendMessage: IDConnectCallback is not set."); } return false; } try { mIDConnectCallback.sendMessage(intent); } catch (Exception e) { if (BuildConfig.DEBUG) { mLogger.severe("sendMessage: exception occurred."); } return false; } return true; } /** * ???Device Connect??????. * @param action ?? * @return Device Connect????true????false */ private boolean checkRequestAction(String action) { return IntentDConnectMessage.ACTION_GET.equals(action) || IntentDConnectMessage.ACTION_POST.equals(action) || IntentDConnectMessage.ACTION_PUT.equals(action) || IntentDConnectMessage.ACTION_DELETE.equals(action); } /** * Device Connect Manager????????. * @param intent intent * @return ?true????false */ private boolean checkManagerUninstall(final Intent intent) { return Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(intent.getAction()) && intent.getExtras().getBoolean(Intent.EXTRA_DATA_REMOVED) && intent.getDataString().contains("package:org.deviceconnect.android.manager"); } /** * Device Connect Manager ????????. * @param action ?? * @return Manager ?true????false */ private boolean checkManagerLaunched(String action) { return IntentDConnectMessage.ACTION_MANAGER_LAUNCHED.equals(action); } /** * Device Connect Manager ????????. * @param action ?? * @return Manager ?true????false */ private boolean checkManagerTerminated(String action) { return IntentDConnectMessage.ACTION_MANAGER_TERMINATED.equals(action); } /** * Device Connect Manager ?Event ?????????. * @param action ?? * @return ??true????false */ private boolean checkManagerEventTransmitDisconnect(String action) { return IntentDConnectMessage.ACTION_EVENT_TRANSMIT_DISCONNECT.equals(action); } /** * Device Plug-in??Reset?????????. * @param action ?? * @return Reset???true????false */ private boolean checkDevicePluginReset(String action) { return IntentDConnectMessage.ACTION_DEVICEPLUGIN_RESET.equals(action); } /** * ????????. * @param action ?? * @return ?true????false */ private boolean checkDevicePluginEnabled(String action) { return IntentDConnectMessage.ACTION_DEVICEPLUGIN_ENABLED.equals(action); } /** * ????????. * @param action ?? * @return ?true????false */ private boolean checkDevicePluginDisabled(String action) { return IntentDConnectMessage.ACTION_DEVICEPLUGIN_DISABLED.equals(action); } /** * Device Connect Manager??????. * <p> * Device Connect Manager????????????? * ???????? * </p> */ protected void onManagerUninstalled() { if (BuildConfig.DEBUG) { mLogger.info("SDK : onManagerUninstalled"); } } /** * Device Connect Manager???????. * <p> * Device Connect Manager??????????????????? * </p> */ protected void onManagerLaunched() { if (BuildConfig.DEBUG) { mLogger.info("SDK : onManagerLaunched"); } } /** * Device Connect Manager???????. * <p> * Device Connect Manager??????????????????? * </p> */ protected void onManagerTerminated() { if (BuildConfig.DEBUG) { mLogger.info("SDK : onManagerTerminated"); } } /** * Device Connect Manager?Event???????. * <p> * Device Connect Manager?WebSocket????????????????? * ?????????????? * </p> * @param origin ????? */ protected void onManagerEventTransmitDisconnected(final String origin) { if (BuildConfig.DEBUG) { mLogger.info("SDK : onManagerEventTransmitDisconnected: " + origin); } } /** * Device Plug-in??Reset???????. * <p> * Device Connect Manager?????????????? * ??????????? * </p> */ protected void onDevicePluginReset() { if (BuildConfig.DEBUG) { mLogger.info("SDK : onDevicePluginReset"); } } /** * Device Connect Manager???????. */ protected void onDevicePluginEnabled() { if (BuildConfig.DEBUG) { mLogger.info("SDK : onEnabled"); } } /** * Device Connect Manager???????. */ protected void onDevicePluginDisabled() { if (BuildConfig.DEBUG) { mLogger.info("SDK : onDisabled"); } } // ?????? /** * ????????. * <p> * ???????????true???? * </p> * @return ????true????false */ protected boolean usesAutoCertificateRequest() { return false; } /** * ?????????. * <p> * ???keystore.p12 ??? * </p> * <p> * ???????????????? * </p> * @return ??? */ protected String getKeyStoreFileName() { return "keystore.p12"; } /** * ????????. * <p> * ????????? * </p> * <p> * ??????????????? * </p> * @return ?? */ protected String getCertificateAlias() { return getContext().getPackageName(); } /** * ??????. * * @param ipAddress IP * @param callback ??? */ public void requestKeyStore(final String ipAddress, final KeyStoreCallback callback) { mKeyStoreMgr.requestKeyStore(ipAddress, callback); } /** * ??????. */ private void requestAndNotifyKeyStore() { requestAndNotifyKeyStore(getCurrentIPAddress()); } /** * ??????. * * @param ipAddress IP */ private void requestAndNotifyKeyStore(final String ipAddress) { requestKeyStore(ipAddress, new KeyStoreCallback() { @Override public void onSuccess(final KeyStore keyStore, final Certificate cert, final Certificate rootCert) { onKeyStoreUpdated(keyStore, cert, rootCert); } @Override public void onError(final KeyStoreError error) { onKeyStoreUpdateError(error); } }); } /** * ??IP????. * * @return IP */ private String getCurrentIPAddress() { Context appContext = getContext().getApplicationContext(); int state = ContextCompat.checkSelfPermission(appContext, Manifest.permission.ACCESS_WIFI_STATE); if (state != PackageManager.PERMISSION_GRANTED) { return null; } // TODO IPv6 ????? // TODO Wi-Fi ??????? WifiManager wifiManager = (WifiManager) appContext.getSystemService(Context.WIFI_SERVICE); if (wifiManager != null) { int ipAddress = wifiManager.getConnectionInfo().getIpAddress(); return String.format(Locale.getDefault(), "%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff), (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff)); } return null; } /** * ?????????. * <p> * ??????????????????? * </p> * @param keyStore * @param cert * @param rootCert */ protected void onKeyStoreUpdated(final KeyStore keyStore, final Certificate cert, final Certificate rootCert) { } /** * ??????????. * <p> * ???????????????????? * </p> * @param error */ protected void onKeyStoreUpdateError(final KeyStoreError error) { } /** * SSLContext ?????. * <p> * ? Web ?????Manager???????????SSLContext ??? * </p> * @param keyStore * @return SSLContext? * @throws GeneralSecurityException SSLContext??????? */ protected SSLContext createSSLContext(final KeyStore keyStore) throws GeneralSecurityException { SSLContext sslContext = SSLContext.getInstance("TLS"); KeyManagerFactory keyManagerFactory = KeyManagerFactory .getInstance(KeyManagerFactory.getDefaultAlgorithm()); keyManagerFactory.init(keyStore, "0000".toCharArray()); TrustManagerFactory trustManagerFactory = TrustManagerFactory .getInstance(TrustManagerFactory.getDefaultAlgorithm()); trustManagerFactory.init(keyStore); sslContext.init(keyManagerFactory.getKeyManagers(), trustManagerFactory.getTrustManagers(), new SecureRandom()); return sslContext; } public void regsiterChangeIpAddress() { if (usesAutoCertificateRequest()) { IntentFilter filter = new IntentFilter(); mContext.registerReceiver(mWiFiBroadcastReceiver, filter); } } public void unregsiterChangeIpAddress() { if (usesAutoCertificateRequest()) { try { mContext.unregisterReceiver(mWiFiBroadcastReceiver); } catch (Exception e) { // ignore. } } } }