/* EventProtocol.java Copyright (c) 2016 NTT DOCOMO,INC. Released under the MIT license http://opensource.org/licenses/mit-license.php */ package org.deviceconnect.android.manager.event; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import org.deviceconnect.android.manager.DConnectBroadcastReceiver; import org.deviceconnect.android.manager.DConnectMessageService; import org.deviceconnect.android.manager.DConnectService; import org.deviceconnect.android.manager.DevicePlugin; import org.deviceconnect.android.manager.DevicePluginManager; import org.deviceconnect.android.manager.util.DConnectUtil; import org.deviceconnect.android.manager.util.VersionName; import org.deviceconnect.android.profile.DConnectProfile; import org.deviceconnect.android.profile.ServiceDiscoveryProfile; import org.deviceconnect.message.DConnectMessage; import org.deviceconnect.message.intent.message.IntentDConnectMessage; /** * イベント登録手順. * * @author NTT DOCOMO, INC. */ public abstract class EventProtocol { private static final VersionName V100 = VersionName.parse("1.0.0"); private static final VersionName V110 = VersionName.parse("1.1.0"); static EventProtocol getInstance(final DConnectMessageService context, final Intent request) { final String appType = request.getStringExtra(DConnectService.EXTRA_INNER_TYPE); if (DConnectService.INNER_TYPE_HTTP.equals(appType)) { return new EventProtocol(context) { @Override protected EventSession createSession(final Intent request, final String serviceId, final String receiverId, final String pluginId) { final String accessToken = DConnectProfile.getAccessToken(request); final String profileName = DConnectProfile.getProfile(request); final String interfaceName = DConnectProfile.getInterface(request); final String attributeName = DConnectProfile.getAttribute(request); WebSocketEventSession session = new WebSocketEventSession(); session.setAccessToken(accessToken); session.setReceiverId(receiverId); session.setServiceId(serviceId); session.setPluginId(pluginId); session.setProfileName(profileName); session.setInterfaceName(interfaceName); session.setAttributeName(attributeName); session.setContext(context); return session; } }; } else { return new EventProtocol(context) { @Override protected EventSession createSession(final Intent request, final String serviceId, final String receiverId, final String pluginId) { final String accessToken = DConnectProfile.getAccessToken(request); final String profileName = DConnectProfile.getProfile(request); final String interfaceName = DConnectProfile.getInterface(request); final String attributeName = DConnectProfile.getAttribute(request); final ComponentName receiver = request.getParcelableExtra(DConnectMessage.EXTRA_RECEIVER); IntentEventSession session = new IntentEventSession(); session.setAccessToken(accessToken); session.setReceiverId(receiverId); session.setServiceId(serviceId); session.setPluginId(pluginId); session.setProfileName(profileName); session.setInterfaceName(interfaceName); session.setAttributeName(attributeName); session.setContext(context); session.setBroadcastReceiver(receiver); return session; } }; } } private DConnectMessageService mMessageService; public EventProtocol(final DConnectMessageService messageService) { mMessageService = messageService; } boolean removeSession(final EventSessionTable table, final Intent request, final DevicePlugin plugin) { String serviceId = DevicePluginManager.splitServiceId(plugin, DConnectProfile.getServiceID(request)); String receiverId = createReceiverId(mMessageService, request); if (receiverId == null) { return false; } EventSession query = createSession(request, serviceId, receiverId, plugin.getPluginId()); for (EventSession session : table.getAll()) { if (isSameSession(query, session)) { table.remove(session); if (plugin.getPluginSdkVersionName().compareTo(V100) == 0) { DConnectProfile.setSessionKey(request, createSessionKeyForPlugin(session)); } return true; } } return false; } boolean addSession(final EventSessionTable table, final Intent request, final DevicePlugin plugin) { String accessToken = request.getStringExtra(DConnectMessage.EXTRA_ACCESS_TOKEN); if (accessToken == null) { DConnectProfile.setAccessToken(request, plugin.getPluginId()); } String serviceId = DevicePluginManager.splitServiceId(plugin, DConnectProfile.getServiceID(request)); String receiverId = createReceiverId(mMessageService, request); if (receiverId == null) { return false; } EventSession session = createSession(request, serviceId, receiverId, plugin.getPluginId()); table.add(session); if (plugin.getPluginSdkVersionName().compareTo(V100) == 0) { DConnectProfile.setSessionKey(request, createSessionKeyForPlugin(session)); } return true; } public static String createSessionKeyForPlugin(final EventSession session) { StringBuilder result = new StringBuilder(); result.append(session.getReceiverId()) .append(DConnectMessageService.SEPARATOR) .append(session.getPluginId()); if (session instanceof IntentEventSession) { result.append(DConnectMessageService.SEPARATOR_SESSION) .append(((IntentEventSession) session).getBroadcastReceiver()); } return result.toString(); } protected abstract EventSession createSession(final Intent request, final String serviceId, final String receiverId, final String pluginId); public static String createReceiverId(final DConnectMessageService messageService, final Intent request) { String origin; if (getOrigin(request) == null && !messageService.requiresOrigin()) { origin = DConnectService.ANONYMOUS_ORIGIN; } else { origin = getOrigin(request); } final String receiverId; final String sessionKey = DConnectProfile.getSessionKey(request); if (sessionKey != null) { receiverId = sessionKey; } else { receiverId = origin; } return receiverId; } private static String getOrigin(Intent intent) { return intent.getStringExtra(IntentDConnectMessage.EXTRA_ORIGIN); } private static String md5(final String s) { try { return DConnectUtil.toMD5(s); } catch (Exception e) { // NOP. } return null; } public boolean isSameSession(final EventSession a, final EventSession b) { return isSame(a.getReceiverId(), b.getReceiverId()) && isSame(a.getServiceId(), b.getServiceId()) && isSame(a.getPluginId(), b.getPluginId()) && isSameIgnoreCase(a.getProfileName(), b.getProfileName()) // MEMO パスの大文字小文字を無視 && isSameIgnoreCase(a.getInterfaceName(), b.getInterfaceName()) && isSameIgnoreCase(a.getAttributeName(), b.getAttributeName()); } private boolean isSame(final String a, final String b) { if (a == null && b == null) { return true; } if (a != null) { return a.equals(b); } else { return b.equals(a); } } private boolean isSameIgnoreCase(final String a, final String b) { if (a == null && b == null) { return true; } if (a != null) { return a.equalsIgnoreCase(b); } else { return b.equalsIgnoreCase(a); } } /** * セッションキーからプラグインIDに変換する. * * @param sessionKey セッションキー * @return プラグインID */ static String convertSessionKey2PluginId(final String sessionKey) { int index = sessionKey.lastIndexOf(DConnectService.SEPARATOR); if (index > 0) { return sessionKey.substring(index + 1); } return sessionKey; } /** * デバイスプラグインからのセッションキーから前半分のクライアントのセッションキーに変換する. * @param sessionKey セッションキー * @return クライアント用のセッションキー */ static String convertSessionKey2Key(final String sessionKey) { int index = sessionKey.lastIndexOf(DConnectService.SEPARATOR); if (index > 0) { return sessionKey.substring(0, index); } return sessionKey; } public static Intent createRegistrationRequestForServiceChange(final Context context, final DevicePlugin plugin) { String profileName = ServiceDiscoveryProfile.PROFILE_NAME; String attributeName = ServiceDiscoveryProfile.ATTRIBUTE_ON_SERVICE_CHANGE; Intent request = createRegistrationRequest(context, plugin, profileName, null, attributeName); if (plugin.getPluginSdkVersionName().compareTo(V110) >= 0) { // NOTE: イベントハンドラーがあとでプラグインを特定するための情報 request.putExtra(DConnectMessage.EXTRA_ACCESS_TOKEN, plugin.getPluginId()); } return request; } public static Intent createRegistrationRequest(final Context context, final DevicePlugin plugin, final String profileName, final String interfaceName, final String attributeName) { return createEventRequest(context, IntentDConnectMessage.ACTION_PUT, plugin, profileName, interfaceName, attributeName); } public static Intent createUnregistrationRequest(final Context context, final DevicePlugin plugin, final String profileName, final String interfaceName, final String attributeName) { return createEventRequest(context, IntentDConnectMessage.ACTION_DELETE, plugin, profileName, interfaceName, attributeName); } private static Intent createEventRequest(final Context context, final String action, final DevicePlugin plugin, final String profileName, final String interfaceName, final String attributeName) { Intent request = new Intent(action); request.setFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES); request.setComponent(plugin.getComponentName()); request.putExtra(DConnectMessage.EXTRA_PROFILE, profileName); request.putExtra(DConnectMessage.EXTRA_INTERFACE, interfaceName); request.putExtra(DConnectMessage.EXTRA_ATTRIBUTE, attributeName); request.putExtra(DConnectMessage.EXTRA_RECEIVER, new ComponentName(context, DConnectBroadcastReceiver.class)); if (plugin.getPluginSdkVersionName().compareTo(V100) == 0) { request.putExtra(DConnectMessage.EXTRA_SESSION_KEY, plugin.getPluginId()); } return request; } }