88import java .util .List ;
99import java .util .Map ;
1010import java .util .Set ;
11+ import java .util .concurrent .Executors ;
12+ import java .util .concurrent .TimeUnit ;
1113
1214import heros .solver .Pair ;
1315import soot .MethodOrMethodContext ;
2830import soot .jimple .infoflow .android .entryPointCreators .AndroidEntryPointUtils ;
2931import soot .jimple .infoflow .memory .IMemoryBoundedSolver ;
3032import soot .jimple .infoflow .memory .ISolverTerminationReason ;
33+ import soot .jimple .infoflow .util .ReusableExecutor ;
3134import soot .jimple .infoflow .util .SystemClassHandler ;
3235import soot .util .HashMultiMap ;
3336import soot .util .MultiMap ;
@@ -93,23 +96,34 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
9396
9497 // Find the mappings between classes and layouts
9598 findClassLayoutMappings ();
96-
97- // Process the callback classes directly reachable from the
98- // entry points
99- for (SootClass sc : entryPointClasses ) {
100- // Check whether we're still running
101- if (isKilled != null )
102- break ;
103-
104- List <MethodOrMethodContext > methods = new ArrayList <MethodOrMethodContext >(
105- entryPointUtils .getLifecycleMethods (sc ));
106-
107- // Check for callbacks registered in the code
108- analyzeReachableMethods (sc , methods );
109-
110- // Check for method overrides
111- analyzeMethodOverrideCallbacks (sc );
112- analyzeClassInterfaceCallbacks (sc , sc , sc );
99+ ReusableExecutor executionService = new ReusableExecutor (
100+ Executors .newFixedThreadPool (Runtime .getRuntime ().availableProcessors ()));
101+
102+ try {
103+ // Process the callback classes directly reachable from the
104+ // entry points
105+ for (SootClass sc : entryPointClasses ) {
106+ // Check whether we're still running
107+ if (isKilled != null )
108+ break ;
109+
110+ List <MethodOrMethodContext > methods = new ArrayList <MethodOrMethodContext >(
111+ entryPointUtils .getLifecycleMethods (sc ));
112+
113+ // Check for callbacks registered in the code
114+ analyzeReachableMethods (executionService , sc , methods );
115+
116+ // Check for method overrides
117+ analyzeMethodOverrideCallbacks (sc );
118+ analyzeClassInterfaceCallbacks (sc , sc , sc );
119+ }
120+ } finally {
121+ executionService .shutdown ();
122+ try {
123+ executionService .awaitTermination (1 , TimeUnit .DAYS );
124+ } catch (InterruptedException e ) {
125+ logger .error ("Interrupted searching for callbacks" );
126+ }
113127 }
114128 reachableChangedListener = Scene .v ().getReachableMethods ().listener ();
115129 logger .info ("Callback analysis done." );
@@ -132,47 +146,58 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
132146 // Incremental mode, only process the worklist
133147 logger .info (String .format ("Running incremental callback analysis for %d components..." ,
134148 callbackWorklist .size ()));
149+ ReusableExecutor executionService = new ReusableExecutor (
150+ Executors .newFixedThreadPool (Runtime .getRuntime ().availableProcessors ()));
135151
136152 MultiMap <SootClass , SootMethod > workList = new HashMultiMap <>(callbackWorklist );
137- for (Iterator <SootClass > it = workList .keySet ().iterator (); it .hasNext ();) {
138- // Check whether we're still running
139- if (isKilled != null )
140- break ;
141-
142- SootClass componentClass = it .next ();
143- Set <SootMethod > callbacks = callbackWorklist .get (componentClass );
144- callbackWorklist .remove (componentClass );
145-
146- Set <SootClass > activityComponents = fragmentClassesRev .get (componentClass );
147- if (activityComponents == null || activityComponents .isEmpty ())
148- activityComponents = Collections .singleton (componentClass );
149-
150- // Check whether we're already beyond the maximum number
151- // of callbacks for the current component
152- if (config .getCallbackConfig ().getMaxCallbacksPerComponent () > 0
153- && callbacks .size () > config .getCallbackConfig ().getMaxCallbacksPerComponent ()) {
154- callbackMethods .remove (componentClass );
155- entryPointClasses .remove (componentClass );
156- continue ;
157- }
153+ try {
154+ for (Iterator <SootClass > it = workList .keySet ().iterator (); it .hasNext ();) {
155+ // Check whether we're still running
156+ if (isKilled != null )
157+ break ;
158+
159+ SootClass componentClass = it .next ();
160+ Set <SootMethod > callbacks = callbackWorklist .get (componentClass );
161+ callbackWorklist .remove (componentClass );
162+
163+ Set <SootClass > activityComponents = fragmentClassesRev .get (componentClass );
164+ if (activityComponents == null || activityComponents .isEmpty ())
165+ activityComponents = Collections .singleton (componentClass );
166+
167+ // Check whether we're already beyond the maximum number
168+ // of callbacks for the current component
169+ if (config .getCallbackConfig ().getMaxCallbacksPerComponent () > 0
170+ && callbacks .size () > config .getCallbackConfig ().getMaxCallbacksPerComponent ()) {
171+ callbackMethods .remove (componentClass );
172+ entryPointClasses .remove (componentClass );
173+ continue ;
174+ }
158175
159- // Check for method overrides. The whole class might be new.
160- analyzeMethodOverrideCallbacks (componentClass );
161- for (SootClass activityComponent : activityComponents ) {
162- if (activityComponent == null )
163- activityComponent = componentClass ;
164- analyzeClassInterfaceCallbacks (componentClass , componentClass , activityComponent );
165- }
176+ // Check for method overrides. The whole class might be new.
177+ analyzeMethodOverrideCallbacks (componentClass );
178+ for (SootClass activityComponent : activityComponents ) {
179+ if (activityComponent == null )
180+ activityComponent = componentClass ;
181+ analyzeClassInterfaceCallbacks (componentClass , componentClass , activityComponent );
182+ }
166183
167- // Collect all methods that we need to analyze
168- List <MethodOrMethodContext > entryClasses = new ArrayList <>(callbacks .size ());
169- for (SootMethod sm : callbacks ) {
170- if (sm != null )
171- entryClasses .add (sm );
172- }
184+ // Collect all methods that we need to analyze
185+ List <MethodOrMethodContext > entryClasses = new ArrayList <>(callbacks .size ());
186+ for (SootMethod sm : callbacks ) {
187+ if (sm != null )
188+ entryClasses .add (sm );
189+ }
173190
174- // Check for further callback declarations
175- analyzeReachableMethods (componentClass , entryClasses );
191+ // Check for further callback declarations
192+ analyzeReachableMethods (executionService , componentClass , entryClasses );
193+ }
194+ } finally {
195+ executionService .shutdown ();
196+ try {
197+ executionService .awaitTermination (1 , TimeUnit .DAYS );
198+ } catch (InterruptedException e ) {
199+ logger .error ("Interrupted searching for callbacks" );
200+ }
176201 }
177202 logger .info ("Incremental callback analysis done." );
178203 }
@@ -186,32 +211,57 @@ protected void internalTransform(String phaseName, @SuppressWarnings("rawtypes")
186211 PackManager .v ().getPack ("wjtp" ).add (transform );
187212 }
188213
189- private void analyzeReachableMethods (SootClass lifecycleElement , List <MethodOrMethodContext > methods ) {
214+ @ Override
215+ protected boolean filterAccepts (SootClass lifecycleElement , SootClass targetClass ) {
216+ return super .filterAccepts (lifecycleElement , targetClass );
217+ }
218+
219+ @ Override
220+ protected boolean filterAccepts (SootClass lifecycleElement , SootMethod targetMethod ) {
221+ return super .filterAccepts (lifecycleElement , targetMethod );
222+ }
223+
224+ protected void analyzeReachableMethods (ReusableExecutor executionService , SootClass lifecycleElement ,
225+ List <MethodOrMethodContext > methods ) {
190226 // Make sure to exclude all other edges in the callgraph except for the
191227 // edges start in the lifecycle methods we explicitly pass in
192228 ComponentReachableMethods rm = new ComponentReachableMethods (config , lifecycleElement , methods );
193229 rm .update ();
194230
195231 // Scan for listeners in the class hierarchy
196232 QueueReader <MethodOrMethodContext > reachableMethods = rm .listener ();
233+ for (ICallbackFilter filter : callbackFilters )
234+ filter .setReachableMethods (rm );
235+
197236 while (reachableMethods .hasNext ()) {
198237 // Check whether we're still running
199238 if (isKilled != null )
200239 break ;
201240
202- for (ICallbackFilter filter : callbackFilters )
203- filter .setReachableMethods (rm );
204-
205241 SootMethod method = reachableMethods .next ().method ();
206242 if (method .isConcrete ()) {
207- analyzeMethodForCallbackRegistrations (lifecycleElement , method );
208- analyzeMethodForDynamicBroadcastReceiver (method );
209- analyzeMethodForServiceConnection (method );
210- analyzeMethodForFragmentTransaction (lifecycleElement , method );
211- analyzeMethodForViewPagers (lifecycleElement , method );
212- analyzeMethodForJavascriptInterfaces (method );
243+ executionService .execute (new Runnable () {
244+
245+ @ Override
246+ public void run () {
247+ analyzeMethodForCallbackRegistrations (lifecycleElement , method );
248+ analyzeMethodForDynamicBroadcastReceiver (method );
249+ analyzeMethodForServiceConnection (method );
250+ analyzeMethodForFragmentTransaction (lifecycleElement , method );
251+ analyzeMethodForViewPagers (lifecycleElement , method );
252+ analyzeMethodForJavascriptInterfaces (method );
253+ }
254+
255+ });
213256 }
214257 }
258+ try {
259+ //We need to wait here since the callback filters have the reachable methods, that
260+ //depend on the current component
261+ executionService .waitUntilFinished ();
262+ } catch (InterruptedException e ) {
263+ throw new RuntimeException (e );
264+ }
215265 }
216266
217267 @ Override
0 commit comments