1fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott/*
2fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * Copyright (C) 2016 The Android Open Source Project
3fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *
4fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * Licensed under the Apache License, Version 2.0 (the "License");
5fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * you may not use this file except in compliance with the License.
6fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * You may obtain a copy of the License at
7fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *
8fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *      http://www.apache.org/licenses/LICENSE-2.0
9fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *
10fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * Unless required by applicable law or agreed to in writing, software
11fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * distributed under the License is distributed on an "AS IS" BASIS,
12fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * See the License for the specific language governing permissions and
14fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * limitations under the License.
15fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott */
16fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottpackage com.android.contacts;
17fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
18fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport android.app.Activity;
19fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport android.app.Instrumentation;
20fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport android.content.Context;
21fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport android.os.Bundle;
22aa5bdeb8bce9b273fa957b7cadbf447b99ea2d3bMarcus Hagerottimport android.os.Debug;
23fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport android.support.test.InstrumentationRegistry;
24fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport android.util.Log;
25fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
26fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport java.lang.reflect.InvocationTargetException;
27fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottimport java.lang.reflect.Method;
28fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
29fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott/**
30fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * Runs a single static method specified via the arguments.
31fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *
326927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott * Useful for manipulating the app state during manual testing. If the class argument is omitted
336927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott * this class will attempt to invoke a method in
346927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott * {@link com.android.contacts.tests.AdbHelpers}
35fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *
36fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * Valid signatures: void f(Context, Bundle), void f(Context), void f()
37fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *
38fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * Example usage:
39fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott * $ adb shell am instrument -e class com.android.contacts.Foo -e method bar -e someArg someValue\
40fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott *   -w com.google.android.contacts.tests/com.android.contacts.RunMethodInstrumentation
41fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott */
42fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerottpublic class RunMethodInstrumentation extends Instrumentation {
43fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
44fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    private static final String TAG = "RunMethod";
45fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
466927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott    private static final String DEFAULT_CLASS = "AdbHelpers";
476927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott
48fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    private String className;
49fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    private String methodName;
50fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    private Bundle args;
51fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
52fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    @Override
53fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    public void onCreate(Bundle arguments) {
54fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        super.onCreate(arguments);
55fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
56fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        InstrumentationRegistry.registerInstance(this, arguments);
57fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
586927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott        className = arguments.getString("class", getContext().getPackageName() + "." +
596927faf7e6e176246bca01d778bfbdff16635b6dMarcus Hagerott                DEFAULT_CLASS);
60fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        methodName = arguments.getString("method");
61fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        args = arguments;
62fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
6357a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang        if (Log.isLoggable(TAG, Log.DEBUG)) {
6457a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang            Log.d(TAG, "Running " + className + "." + methodName);
6557a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang            Log.d(TAG, "args=" + args);
6657a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang        }
67fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
68aa5bdeb8bce9b273fa957b7cadbf447b99ea2d3bMarcus Hagerott        if (arguments.containsKey("debug") && Boolean.parseBoolean(arguments.getString("debug"))) {
69aa5bdeb8bce9b273fa957b7cadbf447b99ea2d3bMarcus Hagerott            Debug.waitForDebugger();
70aa5bdeb8bce9b273fa957b7cadbf447b99ea2d3bMarcus Hagerott        }
71fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        start();
72fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    }
73fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
74073a091b4f94d7321b4e8f51e0ca240c5c264915Marcus Hagerott    @Override
75fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    public void onStart() {
7657a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang        if (Log.isLoggable(TAG, Log.DEBUG)) {
7757a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang            Log.d(TAG, "onStart");
7857a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang        }
79fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        super.onStart();
80fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
81fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        if (className == null || methodName == null) {
82fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            Log.e(TAG, "Must supply class and method");
83fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            finish(Activity.RESULT_CANCELED, null);
84fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            return;
85fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
86fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
879c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott        // Wait for the Application to finish creating.
889c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott        runOnMainSync(new Runnable() {
899c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott            @Override
909c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott            public void run() {
9157a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang                if (Log.isLoggable(TAG, Log.DEBUG)) {
9257a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang                    Log.d(TAG, "acquired main thread from instrumentation");
9357a0e98eed836b72dd3dc3540b9cac029266ed7cWenyi Wang                }
949c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott            }
959c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott        });
969c26abdc6458bb98c8354a2fb9ee9dda44ce2c45Marcus Hagerott
97fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        try {
98fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            invokeMethod(args, className, methodName);
99fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        } catch (Exception e) {
100fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            e.printStackTrace();
101fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            finish(Activity.RESULT_CANCELED, null);
102fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            return;
103fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
104fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        // Maybe should let the method determine when this is called.
105fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        finish(Activity.RESULT_OK, null);
106fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    }
107fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
108fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    private void invokeMethod(Bundle args, String className, String methodName) throws
109fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            InvocationTargetException, IllegalAccessException, NoSuchMethodException,
110fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            ClassNotFoundException {
111fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        Context context;
112fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        Class<?> clazz = null;
113fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        try {
114fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            // Try to load from App's code
115fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            clazz = getTargetContext().getClassLoader().loadClass(className);
116fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            context = getTargetContext();
117fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        } catch (Exception e) {
118fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            // Try to load from Test App's code
119fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            clazz = getContext().getClassLoader().loadClass(className);
120fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            context = getContext();
121fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
122fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
123fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        Object[] methodArgs = null;
124fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        Method method = null;
125fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
126fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        try {
127fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            method = clazz.getMethod(methodName, Context.class, Bundle.class);
128fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            methodArgs = new Object[] { context, args };
129fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        } catch (NoSuchMethodException e) {
130fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
131fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
132fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        if (method != null) {
133fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            method.invoke(clazz, methodArgs);
134fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            return;
135fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
136fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
137fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        try {
138fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            method = clazz.getMethod(methodName, Context.class);
139fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            methodArgs = new Object[] { context };
140fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        } catch (NoSuchMethodException e) {
141fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
142fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
143fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        if (method != null) {
144fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            method.invoke(clazz, methodArgs);
145fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott            return;
146fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        }
147fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott
148fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        method = clazz.getMethod(methodName);
149fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott        method.invoke(clazz);
150fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott    }
151fd79e87e893054e20f86cbe5e0c48aa64ecd3955Marcus Hagerott}
152