Java tutorial
/*************************************************************************** * Copyright (C) 2016 iObserve Project (https://www.iobserve-devops.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ***************************************************************************/ package org.iobserve.mobile.instrument.bytecode; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.iobserve.mobile.agent.core.AndroidAgent; import org.iobserve.mobile.agent.core.ExternalConfiguration; import org.iobserve.mobile.instrument.config.InstrumentationConfiguration; import org.iobserve.mobile.instrument.config.xml.ConnectionInfoXml; import org.iobserve.mobile.instrument.config.xml.InstrumentationPointConfigurationXml; import org.objectweb.asm.MethodVisitor; import org.objectweb.asm.Opcodes; import org.objectweb.asm.Type; import org.objectweb.asm.commons.AdviceAdapter; import android.app.Activity; /** * Bytecode instrumenter which is responsible for instrumenting Android Activity * classes {@link Activity}. * * @author Robert Heinrich * @author David Monschein * */ public class ActivitiyBytecodeInstrumenter implements IBytecodeInstrumenter { /** String constant for method name for {@link Activity#onCreate}. */ private static final String ACTIVITY_ONCREATE = "onCreate"; /** String constant for method name of {@link Activity#onStart}. */ private static final String ACTIVITY_ONSTART = "onStart"; /** String constant for method name of {@link Activity#onDestroy}. */ private static final String ACTIVITY_ONDESTROY = "onDestroy"; /** String constant for method name of {@link Activity#onStop}. */ private static final String ACTIVITY_ONSTOP = "onStop"; /** Logger for this class. */ private static final Logger LOG = LogManager.getLogger(ActivitiyBytecodeInstrumenter.class); /** Type for {@link ExternalConfiguration}. */ private static final Type EXTCONFIGURATION_TYPE = Type.getType(ExternalConfiguration.class); /** Type for {@link AndroidAgent}. */ private static final Type ANDROIDAGENT_TYPE = Type.getType(AndroidAgent.class); /** * Information about the server which persists the monitoring data. -> This * has to be set at instrumentation time! */ private ConnectionInfoXml connectionConfig; /** * Mapping between methods of the Activity and methods which should be * called in the agent. */ private Map<String, String> activityPointMapping; // CORRESPONDENT METHODS /** * Method link to {@link ExternalConfiguration#setBeaconUrl(String)}. */ private Method setBeaconUrlMethod; /** * Type for the {@link ExternalConfiguration#setBeaconUrl(String)} method. */ private Type setBeaconUrlType; /** * Method link to {@link ExternalConfiguration#setHelloUrl(String)}. */ private Method setHelloUrlMethod; /** * Type for the {@link ExternalConfiguration#setHelloUrl(String)} method. */ private Type setHelloUrlType; /** * Method link to {@link AndroidAgent#onStartActivity(Activity)}. */ private Method onStartActivityMethod; /** * Type for the {@link AndroidAgent#onStartActivity(Activity)} method. */ private Type onStartActivityType; /** * Method link to {@link AndroidAgent#onStopActivity(Activity)}. */ private Method onStopActivityMethod; /** * Type for the {@link AndroidAgent#onStopActivity(Activity)} method. */ private Type onStopActivityType; /** * Creates a new bytecode instrumenter for an activity class. */ public ActivitiyBytecodeInstrumenter() { try { setBeaconUrlMethod = ExternalConfiguration.class.getMethod("setBeaconUrl", String.class); setBeaconUrlType = Type.getType(setBeaconUrlMethod); setHelloUrlMethod = ExternalConfiguration.class.getMethod("setHelloUrl", String.class); setHelloUrlType = Type.getType(setHelloUrlMethod); onStartActivityMethod = AndroidAgent.class.getMethod("onStartActivity", Activity.class); onStartActivityType = Type.getType(onStartActivityMethod); onStopActivityMethod = AndroidAgent.class.getMethod("onStopActivity", Activity.class); onStopActivityType = Type.getType(onStopActivityMethod); } catch (NoSuchMethodException | SecurityException e) { e.printStackTrace(); LOG.warn("Couldn't find all corresponding methods!"); } } /** * Creates a instance of the InitBytecodeInstrumenter. * * @param configuration * configuration which specifies the mapping between activity * methods and agent methods. */ public ActivitiyBytecodeInstrumenter(final InstrumentationConfiguration configuration) { this(); this.connectionConfig = configuration.getXmlConfiguration().getConnectionInfo(); this.activityPointMapping = new HashMap<String, String>(); for (InstrumentationPointConfigurationXml point : configuration.getXmlConfiguration() .getInstrumentationRootConfiguration().getInstrPointConfigs()) { this.activityPointMapping.put(point.getType(), point.getValue()); } } /** * {@inheritDoc} */ @Override public void onMethodEnter(final String owner, final String name, final String desc, final AdviceAdapter parent, final MethodVisitor mv) { if (ACTIVITY_ONCREATE.equals(name)) { final String belAgentPoint = activityPointMapping.get("onCreate"); // INSERT CONFIG CALLS mv.visitLdcInsn(connectionConfig.getBeaconUrl()); mv.visitMethodInsn(Opcodes.INVOKESTATIC, EXTCONFIGURATION_TYPE.getInternalName(), setBeaconUrlMethod.getName(), setBeaconUrlType.getDescriptor(), false); mv.visitLdcInsn(connectionConfig.getHelloUrl()); mv.visitMethodInsn(Opcodes.INVOKESTATIC, EXTCONFIGURATION_TYPE.getInternalName(), setHelloUrlMethod.getName(), setHelloUrlType.getDescriptor(), false); // INIT AGENT mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESTATIC, ANDROIDAGENT_TYPE.getInternalName(), belAgentPoint, "(Landroid/app/Activity;)V", false); } else if (ACTIVITY_ONDESTROY.equals(name)) { final String belAgentPoint = activityPointMapping.get("onDestroy"); mv.visitMethodInsn(Opcodes.INVOKESTATIC, ANDROIDAGENT_TYPE.getInternalName(), belAgentPoint, "()V", false); } else if (ACTIVITY_ONSTART.equals(name)) { mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESTATIC, ANDROIDAGENT_TYPE.getInternalName(), onStartActivityMethod.getName(), onStartActivityType.getDescriptor(), false); } else if (ACTIVITY_ONSTOP.equals(name)) { mv.visitVarInsn(Opcodes.ALOAD, 0); mv.visitMethodInsn(Opcodes.INVOKESTATIC, ANDROIDAGENT_TYPE.getInternalName(), onStopActivityMethod.getName(), onStopActivityType.getDescriptor(), false); } } /** * {@inheritDoc} */ @Override public void onMethodExit(final int opcode, final String owner, final String name, final String desc, final AdviceAdapter parent, final MethodVisitor mv) { } }