/* * This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html. * * This file is a derivative of code released under the terms listed below. * */ /* * Copyright (c) 2013, * Tobias Blaschke <code@tobiasblaschke.de> * All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The names of the contributors may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * POSSIBILITY OF SUCH DAMAGE. */ package com.ibm.wala.dalvik.ipa.callgraph.androidModel; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import com.ibm.wala.classLoader.CallSiteReference; import com.ibm.wala.classLoader.IClass; import com.ibm.wala.classLoader.IField; import com.ibm.wala.classLoader.IMethod; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.FlatInstantiator; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.IInstantiationBehavior; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.IInstantiationBehavior.InstanceBehavior; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.Instantiator; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.parameters.ReuseParameters; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.structure.AbstractAndroidModel; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.AndroidBoot; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.AndroidStartComponentTool; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.ExternalModel; import com.ibm.wala.dalvik.ipa.callgraph.androidModel.stubs.SystemServiceModel; import com.ibm.wala.dalvik.ipa.callgraph.impl.AndroidEntryPoint; import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters; import com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters.StarterFlags; import com.ibm.wala.dalvik.util.AndroidComponent; import com.ibm.wala.dalvik.util.AndroidEntryPointManager; import com.ibm.wala.dalvik.util.AndroidTypes; import com.ibm.wala.ipa.callgraph.AnalysisCache; import com.ibm.wala.ipa.callgraph.AnalysisOptions; import com.ibm.wala.ipa.callgraph.AnalysisScope; import com.ibm.wala.ipa.callgraph.CGNode; import com.ibm.wala.ipa.cha.IClassHierarchy; import com.ibm.wala.ipa.cha.IClassHierarchyDweller; import com.ibm.wala.ipa.summaries.MethodSummary; import com.ibm.wala.ipa.summaries.SummarizedMethod; import com.ibm.wala.ipa.summaries.SummarizedMethodWithNames; import com.ibm.wala.ipa.summaries.VolatileMethodSummary; import com.ibm.wala.shrikeBT.IInvokeInstruction; import com.ibm.wala.ssa.ConstantValue; import com.ibm.wala.ssa.SSAAbstractInvokeInstruction; import com.ibm.wala.ssa.SSAInstruction; import com.ibm.wala.ssa.SSAPhiInstruction; import com.ibm.wala.types.Descriptor; import com.ibm.wala.types.FieldReference; import com.ibm.wala.types.MethodReference; import com.ibm.wala.types.Selector; import com.ibm.wala.types.TypeName; import com.ibm.wala.types.TypeReference; import com.ibm.wala.util.CancelException; import com.ibm.wala.util.MonitorUtil; import com.ibm.wala.util.MonitorUtil.IProgressMonitor; import com.ibm.wala.util.ssa.ParameterAccessor; import com.ibm.wala.util.ssa.ParameterAccessor.Parameter; import com.ibm.wala.util.ssa.SSAValue; import com.ibm.wala.util.ssa.SSAValue.TypeKey; import com.ibm.wala.util.ssa.SSAValue.VariableKey; import com.ibm.wala.util.ssa.SSAValue.WeaklyNamedKey; import com.ibm.wala.util.ssa.SSAValueManager; import com.ibm.wala.util.ssa.TypeSafeInstructionFactory; import com.ibm.wala.util.strings.Atom; // For debug: /** * The model to be executed at application start. * * This method models the lifecycle of an Android Application. By doing so it calls all AndroidEntryPoints * set in the AnalysisOptions. * <p> * Between the calls to the AndroidEntryPoints special behavior is inserted. You can change that behavior * by implementing an AbstractAndroidModel or set one of the existing ones in the AnalysisOptions. * * Additionally care of how types are instantiated is taken. You can change this behavior by setting the * IInstanciationBehavior in the AnalysisOptions. * * Smaller Models exist: * * MiniModel calls all components of a specific type (for example all Activities) * * MicroModel calls a single specific component * * ExternalModel doesn't call anything but fiddles with the data on its own * * All these Models are added to a synthetic AndroidModelClass. * * @author Tobias Blaschke <code@tobiasblaschke.de> */ public class AndroidModel /* makes SummarizedMethod */ implements IClassHierarchyDweller { private static Logger logger = LoggerFactory.getLogger(AndroidModel.class); private final Atom name = Atom.findOrCreateAsciiAtom("AndroidModel"); public MethodReference mRef; protected IClassHierarchy cha; protected AnalysisOptions options; protected AnalysisCache cache; private AbstractAndroidModel labelSpecial; private IInstantiationBehavior instanceBehavior; private SSAValueManager paramManager; private ParameterAccessor modelAcc; private ReuseParameters reuseParameters; protected final AnalysisScope scope; protected VolatileMethodSummary body; // private JavaInstructionFactory instructionFactory; private IProgressMonitor monitor; private int maxProgress; /* * static: "boot" only once. How to assert done by the right one? */ protected static boolean doBoot = true; protected IClass klass; protected boolean built; protected SummarizedMethod model; public AndroidModel(final IClassHierarchy cha, final AnalysisOptions options, final AnalysisCache cache) { this.options = options; this.cha = cha; this.cache = cache; this.built = false; this.scope = options.getAnalysisScope(); this.instanceBehavior = AndroidEntryPointManager.MANAGER.getInstantiationBehavior(cha); } /** * Generates the model on a sub-set of Entrypoints. * * Asks {@link #selectEntryPoint(AndroidEntryPoint)} for each EntryPoint known to the AndroidEntryPointManager, * if the EntryPoint should be included in the model. Then calls {@link #build(Atom, Iterable<? extends Entrypoint>)} * on these. * * @param name The name the generated method will be known as */ protected void build(Atom name) throws CancelException { final List<AndroidEntryPoint> restrictedEntries = new ArrayList<AndroidEntryPoint>(); for (AndroidEntryPoint ep: AndroidEntryPointManager.ENTRIES) { if (selectEntryPoint(ep)) { restrictedEntries.add(ep); } } build(name, restrictedEntries); } public Atom getName() { return this.name; } public boolean isStatic() { return true; } public TypeName getReturnType() { return TypeReference.VoidName; } public Descriptor getDescriptor() throws CancelException { return getMethod().getDescriptor(); } /** * Generate the SummarizedMethod for the model (in this.model). * * The actual generated model depends on the on the properties of this overloaded class. Most * generated methods should reside in the AndroidModelClass and take AndroidComponents as well * as some parameters (these marked REUSE) to the EntryPoints of the components. * * Use {@link #getMethod()} to retrieve the method generated here or getMethodAs to get a version * which is wrapped to another signature. * * @param name The name the generated method will be known as * @param entrypoints The functions to call additionally to boot-code and XXX */ protected void build(Atom name, Collection<? extends AndroidEntryPoint> entrypoints) throws CancelException { // register this.klass = cha.lookupClass(AndroidModelClass.ANDROID_MODEL_CLASS); if (this.klass == null) { // add to cha logger.info("Adding model-class to cha"); this.klass = AndroidModelClass.getInstance(cha); cha.addClass(this.klass); } this.reuseParameters = new ReuseParameters(this.instanceBehavior, this); // this.instructionFactory = new JavaInstructionFactory(); // TODO: TSIF // Complete the signature of the method reuseParameters.collectParameters(entrypoints); this.mRef = reuseParameters.toMethodReference(null); this.modelAcc = new ParameterAccessor(this.mRef, (! isStatic())); this.paramManager = new SSAValueManager(modelAcc); final Selector selector = this.mRef.getSelector(); final AndroidModelClass mClass = AndroidModelClass.getInstance(cha); if (mClass.containsMethod(selector)) { logger.info("Returning existing {}", selector); assert (mClass.getMethod(selector) instanceof SummarizedMethod); this.model = (SummarizedMethod) mClass.getMethod(selector); return; } this.body = new VolatileMethodSummary(new MethodSummary(this.mRef)); this.body.setStatic(true); this.labelSpecial = AndroidEntryPointManager.MANAGER.makeModelBehavior(this.body, new TypeSafeInstructionFactory(cha), this.paramManager, entrypoints); this.monitor = AndroidEntryPointManager.MANAGER.getProgressMonitor(); this.maxProgress = entrypoints.size(); AndroidModel.doBoot &= AndroidEntryPointManager.MANAGER.getDoBootSequence(); // BUILD this.monitor.beginTask("Building " + name, this.maxProgress); populate(entrypoints); assert (cha.lookupClass(AndroidModelClass.ANDROID_MODEL_CLASS) != null) : "Adding the class failed!"; if (this.klass == null) { throw new IllegalStateException("Could not find ANDROID_MODEL_CLASS in cha."); } this.body.setLocalNames(this.paramManager.makeLocalNames()); this.model = new SummarizedMethodWithNames(this.mRef, this.body, this.klass) { @Override public TypeReference getParameterType (int i) { IClassHierarchy cha = getClassHierarchy(); TypeReference tRef = super.getParameterType(i); if (tRef.isClassType()) { if (cha.lookupClass(tRef) != null) { return tRef; } else { for (IClass c : cha) { if (c.getName().toString().equals(tRef.getName().toString())) { return c.getReference(); } } } return tRef; //throw new IllegalStateException("Error looking up " + tRef); } else { return tRef; } } }; this.built = true; } private void register(SummarizedMethod model) { IClass klass = getDeclaringClass(); ((AndroidModelClass)klass).setMacroModel(model); } /** * Building the SummarizedMethod is delayed upon the first class to this method. * * @return the method for this model as generated by build() */ public SummarizedMethod getMethod() throws CancelException { if (!built) { build(this.name); register(this.model); } return this.model; } /** * The class the Method representing this Model resides in. * * Most likely the AndroidModelClass. */ public IClass getDeclaringClass() { return this.klass; } /** * Overridden by models to restraint Entrypoints. * * For each entrypoint this method is queried if it should be part of the model. * * @param ep The EntryPoint in question * @return if the given EntryPoint shall be part of the model */ protected boolean selectEntryPoint(AndroidEntryPoint ep) { return true; } /** * Add Instructions to the model. * * {@link #build(Iterable<?extends Entrypoint>)} prepares the MethodSummary, then calls populate() to * add the instructions, then finishes the model. Populate is only an extra function to shorten build(), * calling it doesn't make sense in an other context. */ private void populate(Iterable<? extends AndroidEntryPoint> entrypoints) throws CancelException { assert (! built) : "You can only build once"; int currentProgress = 0; final TypeSafeInstructionFactory tsif = new TypeSafeInstructionFactory(this.cha); final Instantiator instantiator = new Instantiator (this.body, tsif, this.paramManager, this.cha, this.mRef, this.scope); boolean enteredASection = false; // // Add preparing code to the model // if (AndroidModel.doBoot) { // final Set<Parameter> allActivities = new HashSet<Parameter>(modelAcc.allExtend(AndroidTypes.ActivityName, getClassHierarchy())); //assert(allActivities.size() > 0) : "There are no Activities in the Model"; // XXX // final IntentStarters.StartInfo toolInfo = IntentStarters.StartInfo.makeContextFree(null); // final AndroidStartComponentTool tool = new AndroidStartComponentTool(this.cha, this.mRef, toolInfo.getFlags(), // /* caller */ null, tsif, modelAcc, this.paramManager, this.body, /* self */ null, toolInfo, /* callerNd */ null); final SSAValue application; { final SSAValue tmpApp = modelAcc.firstExtends(AndroidTypes.ApplicationName, this.cha); if (tmpApp != null) { application = tmpApp; } else { // Generate a real one? logger.warn("I didn't get an application. Generating a new object."); application = paramManager.getUnmanaged(AndroidTypes.Application, "app"); this.body.addConstant(application.getNumber(), new ConstantValue(null)); application.setAssigned(); } } final SSAValue nullIntent; { nullIntent = paramManager.getUnmanaged(AndroidTypes.Intent, "nullIntent"); this.body.addConstant(nullIntent.getNumber(), new ConstantValue(null)); nullIntent.setAssigned(); } final SSAValue nullBinder; { nullBinder = paramManager.getUnmanaged(AndroidTypes.IBinder, "nullBinder"); this.body.addConstant(nullBinder.getNumber(), new ConstantValue(null)); nullBinder.setAssigned(); } logger.info("Adding Boot-Code to the Android model"); { final AndroidBoot boot = new AndroidBoot(null); boot.addBootCode(tsif, null, paramManager, this.body); //tool.attachActivities(allActivities, application, boot.getMainThread(), /* Should be application context TODO */ // boot.getPackageContext(), nullBinder, nullIntent); } // TODO: Assign context to the other components } logger.info("Populating the AndroidModel with {} entryPoints", this.maxProgress); for (final AndroidEntryPoint ep : entrypoints) { this.monitor.subTask(ep.getMethod().getReference().getSignature() ); if (! selectEntryPoint(ep)) { assert(false): "The ep should not reach here!"; logger.warn("SKIP: " + ep); currentProgress++; continue; } // // Is special handling to be inserted? // if (this.labelSpecial.hadSectionSwitch(ep.order)) { logger.info("Adding special handling before: {}.", ep); this.labelSpecial.enter(ep.getSection(), body.getNextProgramCounter()); enteredASection = true; } // // Collect arguments to ep // if there are multiple paramses call the entrypoint multiple times // List<List<SSAValue>> paramses = new ArrayList<List<SSAValue>>(1); { final List<Integer> mutliTypePositions = new ArrayList<Integer>(); { // Add single-type parameters and collect positions for multi-type final List<SSAValue> params = new ArrayList<SSAValue>(ep.getNumberOfParameters()); paramses.add(params); for (int i = 0; i < ep.getNumberOfParameters(); ++i) { if (ep.getParameterTypes(i).length != 1) { logger.debug("Got multiple types: {}", Arrays.toString(ep.getParameterTypes(i))); mutliTypePositions.add(i); params.add(null); // will get set later } else { for (final TypeReference type : ep.getParameterTypes(i)) { if (this.instanceBehavior.getBehavior(type.getName(), ep.getMethod(), null) == InstanceBehavior.REUSE) { params.add(this.paramManager.getCurrent(new TypeKey(type.getName()))); } else if (type.isPrimitiveType()) { params.add(paramManager.getUnmanaged(type, "p")); } else { // It is an CREATE parameter final boolean asManaged = false; final VariableKey key = null; // auto-generates UniqueKey final Set<SSAValue> seen = null; params.add(instantiator.createInstance(type, asManaged, key, seen)); } } } } } // Now for the mutliTypePositions: we'll build the Cartesian product for these for (int positionInMutliTypePosition = 0; positionInMutliTypePosition < mutliTypePositions.size(); ++positionInMutliTypePosition) { final Integer multiTypePosition = mutliTypePositions.get(positionInMutliTypePosition); final TypeReference[] typesOnPosition = ep.getParameterTypes(multiTypePosition); final int typeCountOnPosition = typesOnPosition.length; { // Extend the list size to hold the product final List<List<SSAValue>> new_paramses = new ArrayList<List<SSAValue>>(paramses.size() * typeCountOnPosition); for (int i = 0; i < typeCountOnPosition; ++i) { //new_paramses.addAll(paramses); *grrr* JVM! You could copy at least null - but noooo... for (final List<SSAValue> params : paramses) { final List<SSAValue> new_params = new ArrayList<SSAValue>(params.size()); new_params.addAll(params); new_paramses.add(new_params); } } paramses = new_paramses; } { // set the current multiTypePosition for (int i = 0; i < paramses.size(); ++i) { final List<SSAValue> params = paramses.get(i); assert(params.get(multiTypePosition) == null) : "Expected null, got " + params.get(multiTypePosition) + " iter " + i; // XXX: This could be faster, but well... final TypeReference type = typesOnPosition[(i * (positionInMutliTypePosition + 1)) % typeCountOnPosition]; if (this.instanceBehavior.getBehavior(type.getName(), ep.getMethod(), null) == InstanceBehavior.REUSE) { params.set(multiTypePosition, this.paramManager.getCurrent(new TypeKey(type.getName()))); } else if (type.isPrimitiveType()) { params.set(multiTypePosition, paramManager.getUnmanaged(type, "p")); } else { // It is an CREATE parameter final boolean asManaged = false; final VariableKey key = null; // auto-generates UniqueKey final Set<SSAValue> seen = null; params.set(multiTypePosition, instantiator.createInstance(type, asManaged, key, seen)); } } } } } /*{ // DEBUG if (paramses.size() > 1) { System.out.println("\n\nParamses on " + ep.getMethod().getSignature() + ":"); for (final List<SSAValue> params : paramses) { System.out.println("\t" + params); } } } // */ // // Insert the call optionally handling its return value // for (final List<SSAValue> params : paramses) { logger.debug("Adding Call to {}.{}", ep.getMethod().getDeclaringClass().getName(), ep.getMethod().getName()); final int callPC = body.getNextProgramCounter(); final CallSiteReference site = ep.makeSite(callPC); final SSAAbstractInvokeInstruction invokation; final SSAValue exception = paramManager.getException(); // will hold the exception object of ep if (ep.getMethod().getReturnType().equals(TypeReference.Void)) { invokation = tsif.InvokeInstruction(callPC, params, exception, site); this.body.addStatement(invokation); } else { // Check if we have to mix in the return value of this ep using a Phi final TypeReference returnType = ep.getMethod().getReturnType(); final TypeKey returnKey = new TypeKey(returnType.getName()); if (this.paramManager.isSeen(returnKey)) { // if it's seen it most likely is a REUSE-Type. However probably it makes sense for // other types too so we don't test on isReuse. logger.debug("Mixing in return type of this EP"); final SSAValue oldValue = this.paramManager.getCurrent(returnKey); this.paramManager.invalidate(returnKey); final SSAValue returnValue = paramManager.getUnallocated(returnType, returnKey); invokation = tsif.InvokeInstruction(callPC, returnValue, params, exception, site); this.body.addStatement(invokation); this.paramManager.setAllocation(returnValue, invokation); // ... and Phi things together ... this.paramManager.invalidate(returnKey); final SSAValue newValue = this.paramManager.getFree(returnType, returnKey); final int phiPC = body.getNextProgramCounter(); final List<SSAValue> toPhi = new ArrayList<SSAValue>(2); toPhi.add(oldValue); toPhi.add(returnValue); final SSAPhiInstruction phi = tsif.PhiInstruction(phiPC, newValue, toPhi); this.body.addStatement(phi); this.paramManager.setPhi(newValue, phi); } else { // Just throw away the return value final SSAValue returnValue = paramManager.getUnmanaged(returnType, new SSAValue.UniqueKey()); invokation = tsif.InvokeInstruction(callPC, returnValue, params, exception, site); this.body.addStatement(invokation); } } } this.monitor.worked(++currentProgress); MonitorUtil.throwExceptionIfCanceled(this.monitor); } logger.debug("All EntryPoints have been added - now closing the model"); // Close all sections by "jumping over" the remaining labels if (enteredASection) { labelSpecial.finish(body.getNextProgramCounter()); } this.monitor.done(); } /** * Get method of the Model in an other Signature. * * Generates a new Method that wraps the model so it can be called using the given Signature. * Flags control the behavior of that wrapper. * * Arguments to the wrapping function are "connected through" to the model based on their type only, * so if there are multiple Arguments of the same type this may yield to unexpected connections. * * This method is called by the IntentCoentextInterpreter. * * @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentContextInterpreter * @see com.ibm.wala.dalvik.ipa.callgraph.propagation.cfa.IntentStarters * * @param asMethod The signature to generate * @param flags Control the behavior of the wrapper, may be null * @param caller The class of the caller; only needed depending on the flags * @param info The IntentSterter used * @param callerNd CGNoodle of the caller - may be null * @return A wrapper that calls the model */ public SummarizedMethod getMethodAs(MethodReference asMethod, TypeReference caller, IntentStarters.StartInfo info, CGNode callerNd) throws CancelException { Set<StarterFlags> flags = info.getFlags(); //System.out.println("\n\nAS: " + asMethod + "\n\n"); if (!built) { getMethod(); } if (asMethod == null) { throw new IllegalArgumentException("asMethod may not be null"); } if (flags == null) { flags = Collections.emptySet(); } final TypeSafeInstructionFactory instructionFactory = new TypeSafeInstructionFactory(getClassHierarchy()); final ParameterAccessor acc = new ParameterAccessor(asMethod, /* hasImplicitThis: */ true); //final AndroidModelParameterManager pm = new AndroidModelParameterManager(acc); final SSAValueManager pm = new SSAValueManager(acc); if (callerNd != null) { pm.breadCrumb = "Caller: " + caller + " Context: " + callerNd.getContext() + " Model: " + this.getClass() + " Name: " + this.getName(); } else { pm.breadCrumb = "Caller: " + caller + " Model: " + this.getClass(); } final VolatileMethodSummary redirect = new VolatileMethodSummary(new MethodSummary(asMethod)); redirect.setStatic(false); final Instantiator instantiator = new Instantiator(redirect, instructionFactory, pm, this.cha, asMethod, this.scope); final Parameter self; { self = acc.getThisAs(caller); pm.setAllocation(self, null); //self = acc.getThis(); } final ParameterAccessor modelAcc = new ParameterAccessor(this.model); /*{ // DEBUG System.out.println("FOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO: Calleeeeeee" + this.model); for (SSAValue calleeParam : modelAcc.all()) { System.out.println("\tCalleeArg: " + calleeParam); } try { System.out.println("\tCalleeP1: " + modelAcc.getParameter(1)); System.out.println("\tCalleeM0: " + this.model.getParameterType(0)); System.out.println("\tCalleeP2: " + modelAcc.getParameter(2)); System.out.println("\tCalleeM1: " + this.model.getParameterType(1)); MethodReference modelRef = this.model.getReference(); System.out.println("\tmRef: " + modelRef); System.out.println("\tCalleeR0: " + modelRef.getParameterType(0)); System.out.println("\tCalleeR1: " + modelRef.getParameterType(1)); } catch (Exception e) { } }*/ final List<Parameter> modelsActivities = modelAcc.allExtend(AndroidTypes.ActivityName, getClassHierarchy()); // are in models scope final List<SSAValue> allActivities = new ArrayList<SSAValue>(modelsActivities.size()); // create instances in this scope for (Parameter activity: modelsActivities) { final TypeReference activityType = activity.getType(); final Parameter inAsMethod = acc.firstOf(activityType); if (inAsMethod != null) { allActivities.add(inAsMethod); } else { final Atom fdName = activityType.getName().getClassName(); final AndroidModelClass mClass = AndroidModelClass.getInstance(cha); if (AndroidEntryPointManager.MANAGER.doFlatComponents()) { if (mClass.getField(fdName) != null) { final IField field = mClass.getField(fdName); final int instPC = redirect.getNextProgramCounter(); final SSAValue target = pm.getUnallocated(activityType, new SSAValue.WeaklyNamedKey(activityType.getName(), "got" + fdName.toString())); final SSAInstruction getInst = instructionFactory.GetInstruction(instPC, target, field.getReference()); redirect.addStatement(getInst); pm.setAllocation(target, getInst); allActivities.add(target); } else { final SSAValue newInstance = instantiator.createInstance(activityType, false, null, null); allActivities.add(newInstance); mClass.putField(fdName, activityType); final int instPC = redirect.getNextProgramCounter(); final FieldReference fdRef = FieldReference.findOrCreate(mClass.getReference(), fdName, activityType); final SSAInstruction putInst = instructionFactory.PutInstruction(instPC, newInstance, fdRef); redirect.addStatement(putInst); System.out.println("All activities new: " + newInstance); } } else { final SSAValue newInstance = instantiator.createInstance(activityType, false, null, null); allActivities.add(newInstance); } } } assert(allActivities.size() == modelsActivities.size()); // The defaults for connectThrough final Set<SSAValue> defaults = new HashSet<SSAValue>(); { // Calls that don't take a bundle usually call through with a null-bundle final SSAValue nullBundle = pm.getUnmanaged(AndroidTypes.Bundle, "nullBundle"); redirect.addConstant(nullBundle.getNumber(), new ConstantValue(null)); nullBundle.setAssigned(); defaults.add(nullBundle); } { // We may have an incoming parameter of [Intent - unpack it to Intent final TypeName intentArray = TypeName.findOrCreate("[Landroid/content/Intent"); final SSAValue incoming = acc.firstOf(intentArray); // TODO: Take all not only the first if (incoming != null) { final VariableKey unpackedIntentKey = new WeaklyNamedKey(AndroidTypes.IntentName, "unpackedIntent"); final SSAValue unpackedIntent = pm.getUnallocated(AndroidTypes.Intent, unpackedIntentKey); final int pc = redirect.getNextProgramCounter(); final SSAInstruction fetch = instructionFactory.ArrayLoadInstruction(pc, unpackedIntent, incoming, 0); redirect.addStatement(fetch); pm.setAllocation(unpackedIntent, fetch); defaults.add(unpackedIntent); } } final SSAValue intent = acc.firstExtends(AndroidTypes.Intent, cha); final AndroidStartComponentTool tool = new AndroidStartComponentTool(getClassHierarchy(), asMethod, flags, caller, instructionFactory, acc, pm, redirect, self, info, callerNd); final AndroidTypes.AndroidContextType contextType; final SSAValue androidContext; // of AndroidTypes.Context: The callers android-context androidContext = tool.fetchCallerContext(); contextType = tool.typeOfCallerContext(); try { // Add additional Info if Exception occurs... // TODO: Check, that caller is an activity where necessary! //final SSAValue iBinder = tool.fetchIBinder(androidContext); //tool.assignIBinder(iBinder, allActivities); if (intent != null) { tool.setIntent(intent, allActivities); } else if (! info.isSystemService()) { // it's normal for SystemServices logger.warn("Got no Intent in call to: {} as {}", this.name, asMethod); } // Call the model { logger.debug("Calling model: {}", this.model.getReference().getName()); final List<SSAValue> redirectParams = acc.connectThrough(modelAcc, new HashSet<SSAValue>(allActivities), defaults, getClassHierarchy(), /* IInstantiator this.createInstance(type, redirect, pm) */ instantiator, false, null, null); final int callPC = redirect.getNextProgramCounter(); final CallSiteReference site = CallSiteReference.make(callPC, this.model.getReference(), IInvokeInstruction.Dispatch.STATIC); final SSAAbstractInvokeInstruction invokation; final SSAValue exception = pm.getException(); if (this.model.getReference().getReturnType().equals(TypeReference.Void)) { invokation = instructionFactory.InvokeInstruction(callPC, redirectParams, exception, site); } else { // it's startExternal or SystemService if (this instanceof SystemServiceModel) { final SSAValue svc = pm.getUnmanaged(TypeReference.JavaLangObject, "systemService"); invokation = instructionFactory.InvokeInstruction(callPC, svc, redirectParams, exception, site); // SHORTCUT: redirect.addStatement(invokation); if (instructionFactory.isAssignableFrom(svc.getType(), svc.getValidIn().getReturnType())) { final int returnPC = redirect.getNextProgramCounter(); final SSAInstruction returnInstruction = instructionFactory.ReturnInstruction(returnPC, svc); redirect.addStatement(returnInstruction); } final IClass declaringClass = this.cha.lookupClass(asMethod.getDeclaringClass()); if (declaringClass == null) { throw new IllegalStateException("Unable to retreive te IClass of " + asMethod.getDeclaringClass() + " from " + "Method " + asMethod.toString()); } redirect.setLocalNames(pm.makeLocalNames()); SummarizedMethod override = new SummarizedMethodWithNames(mRef, redirect, declaringClass); return override; } else if (this instanceof ExternalModel) { final SSAValue trash = pm.getUnmanaged(AndroidTypes.Intent, "trash"); invokation = instructionFactory.InvokeInstruction(callPC, trash, redirectParams, exception, site); } else { throw new UnsupportedOperationException("Can't handle a " + this.model.getClass()); } } redirect.addStatement(invokation); } // Optionally call onActivityResult if (flags.contains(StarterFlags.CALL_ON_ACTIVITY_RESULT) && // TODO: Test multiple activities (! flags.contains(StarterFlags.CONTEXT_FREE))) { // TODO: Doesn't this work without context? // Collect all Activity.mResultCode and Activity.mResultData // Result information of all activities. final List<SSAValue> resultCodes = new ArrayList<SSAValue>(); final List<SSAValue> resultData = new ArrayList<SSAValue>(); final SSAValue mResultCode; // = Phi(resultCodes) final SSAValue mResultData; // = Phi(resultData) tool.fetchResults(resultCodes, resultData, allActivities); if (resultCodes.size() == 0) { logger.error("Can't read back results from the started Activity => Can't call onActivityResult - " + "The Activity has to be marked as REUSE in the IInstructionBehavior."); throw new IllegalStateException("The call " + asMethod + " from " + caller + " failed, as the model " + this.model + " did not take an activity to read the result from"); } mResultCode = tool.addPhi(resultCodes); mResultData = tool.addPhi(resultData); { // Send back the results // TODO: Assert caller is an Activity final SSAValue outRequestCode = acc.firstOf(TypeReference.Int); // TODO: Check is's the right parameter logger.debug("Calling onActivityResult"); final int callPC = redirect.getNextProgramCounter(); // void onActivityResult (int requestCode, int resultCode, Intent data) final Selector mSel = Selector.make("onActivityResult(IILandroid/content/Intent;)V"); final MethodReference mRef = MethodReference.findOrCreate(caller, mSel); final CallSiteReference site = CallSiteReference.make(callPC, mRef, IInvokeInstruction.Dispatch.VIRTUAL); //final SSAValue exception = new SSAValue(pm.getUnmanaged(), TypeReference.JavaLangException, asMethod, "exception"); final SSAValue exception = pm.getException(); final List<SSAValue> params = new ArrayList<SSAValue>(); params.add(self); params.add(outRequestCode); // Was an agument to start... params.add(mResultCode); params.add(mResultData); final SSAInstruction invokation = instructionFactory.InvokeInstruction(callPC, params, exception, site); redirect.addStatement(invokation); logger.info("Calling this.onActivityResult"); } // */ } final IClass declaringClass = this.cha.lookupClass(asMethod.getDeclaringClass()); if (declaringClass == null) { throw new IllegalStateException("Unable to retreive te IClass of " + asMethod.getDeclaringClass() + " from " + "Method " + asMethod.toString()); } // TODO: Throw into an other loader redirect.setLocalNames(pm.makeLocalNames()); SummarizedMethod override = new SummarizedMethodWithNames(mRef, redirect, declaringClass); //assert(asMethod.getReturnType().equals(TypeReference.Void)) : "getMethodAs does not support return values. Requested: " + // asMethod.getReturnType().toString(); // TODO: Implement return override; } catch (Exception e) { e.printStackTrace(); System.err.println("\nOccured in getMethodAs with parameters of:"); System.err.println(acc.dump()); System.err.println("\tpm=\t" + pm); System.err.println("\tself=\t" + self); System.err.println("\tmodelAcc=\t" + acc.dump()); //final List<Parameter> modelsActivities = modelAcc.allExtend(AndroidTypes.ActivityName, getClassHierarchy()); // are in models scope //final List<SSAValue> allActivities = new ArrayList<SSAValue>(modelsActivities.size()); // create instances in this scope System.err.println("\tcontextType=\t" + contextType); System.err.println("\tandroidContetx=\t" + androidContext); System.err.println("\tasMethod=\t" + asMethod); System.err.println("\tcaller=\t" + caller); System.err.println("\tinfo=\t" + info); System.err.println("\tcallerND=\t" + callerNd); System.err.println("\tthis=\t" + this.getClass().toString()); System.err.println("\tthis.name=\t" + this.name); throw new IllegalStateException(e); } } /** * Creates an "encapsulated" version of the model. * * The generated method will take no parameters. New instances for REUSE-Parameters will * be created. * * This variant is useful for the start of an analysis. */ public IMethod getMethodEncap() throws CancelException { final MethodReference asMethod; { final TypeReference clazz = AndroidModelClass.ANDROID_MODEL_CLASS; final Atom methodName = Atom.concat(this.getName(), Atom.findOrCreateAsciiAtom("Encap")); //final TypeName returnType = this.getReturnType(); final TypeName returnType = TypeReference.VoidName; final Descriptor descr = Descriptor.findOrCreate(new TypeName[]{}, returnType); asMethod = MethodReference.findOrCreate(clazz, methodName, descr); } final AndroidModelClass mClass = AndroidModelClass.getInstance(cha); if (mClass.containsMethod(asMethod.getSelector())) { // There's already an encap for this method return mClass.getMethod(asMethod.getSelector()); } final VolatileMethodSummary encap = new VolatileMethodSummary(new MethodSummary(asMethod)); encap.setStatic(true); final TypeSafeInstructionFactory instructionFactory = new TypeSafeInstructionFactory(getClassHierarchy()); final ParameterAccessor acc = new ParameterAccessor(asMethod, /* hasImplicitThis: */ false); final SSAValueManager pm = new SSAValueManager(acc); pm.breadCrumb = "Encap: " + this.getClass().toString(); final SummarizedMethod model = getMethod(); final List<SSAValue> params = new ArrayList<SSAValue>(); { // Collect Params final FlatInstantiator instantiator = new FlatInstantiator(encap, instructionFactory, pm, this.cha, asMethod, this.scope); for (int i = 0; i < model.getNumberOfParameters(); ++i) { final TypeReference argT = model.getParameterType(i); final SSAValue arg; if ( ( AndroidEntryPointManager.MANAGER.doFlatComponents()) && (AndroidComponent.isAndroidComponent(argT, cha)) ) { // Get / Put filed in AndroidModelClass for Android-Components final Atom fdName = argT.getName().getClassName(); if (mClass.getField(fdName) != null) { final IField field = mClass.getField(fdName); final int instPC = encap.getNextProgramCounter(); arg = pm.getUnallocated(argT, new SSAValue.WeaklyNamedKey(argT.getName(), "got" + fdName.toString())); final SSAInstruction getInst = instructionFactory.GetInstruction(instPC, arg, field.getReference()); encap.addStatement(getInst); pm.setAllocation(arg, getInst); } else { arg = instantiator.createInstance(argT, false, null, null); mClass.putField(fdName, argT); final int instPC = encap.getNextProgramCounter(); final FieldReference fdRef = FieldReference.findOrCreate(mClass.getReference(), fdName, argT); final SSAInstruction putInst = instructionFactory.PutInstruction(instPC, arg, fdRef); encap.addStatement(putInst); } } else { final boolean managed = false; final SSAValue.VariableKey key = new SSAValue.TypeKey(argT.getName()); arg = instantiator.createInstance (argT, managed, key, null); } params.add(arg); } } { // Call the model final int callPC = encap.getNextProgramCounter(); final CallSiteReference site = CallSiteReference.make(callPC, model.getReference(), IInvokeInstruction.Dispatch.STATIC); final SSAValue exception = pm.getException(); final SSAAbstractInvokeInstruction invokation = instructionFactory.InvokeInstruction(callPC, params, exception, site); encap.addStatement(invokation); } encap.setLocalNames(pm.makeLocalNames()); final SummarizedMethod method = new SummarizedMethodWithNames(asMethod, encap, mClass); mClass.addMethod(method); return method; } public IClassHierarchy getClassHierarchy() { return this.cha; } @Override public String toString() { return "<" + this.getClass() + " name=" + this.name + " />"; } }