1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.tests.sigtest;
18
19import android.app.Activity;
20import android.content.Intent;
21import android.content.res.Resources;
22import android.content.res.Resources.NotFoundException;
23import android.os.Bundle;
24
25import org.xmlpull.v1.XmlPullParserException;
26
27import java.io.BufferedReader;
28import java.io.IOException;
29import java.io.InputStream;
30import java.io.InputStreamReader;
31import java.lang.reflect.Field;
32import java.util.ArrayList;
33import java.util.HashMap;
34import java.util.HashSet;
35
36/**
37 * This class is used for Signature Test. It will started by the Instrumentation class,
38 * and send back the test result via IBinder.
39 */
40public class SignatureTestActivity extends Activity {
41    static final String BUNDLE_EXTRA_SIG_TEST = "sigtest";
42
43    static final String BUNDLE_KEY_RESULT = "result";
44    static final String BUNDLE_KEY_MISSING_CLASS = "missing_class";
45    static final String BUNDLE_KEY_MISSING_INTERFACE = "missing_interface";
46    static final String BUNDLE_KEY_MISSING_METHOD = "missing_method";
47    static final String BUNDLE_KEY_MISSING_FIELD = "missing_field";
48    static final String BUNDLE_KEY_MISMATCH_CLASS = "mismatch_class_signature";
49    static final String BUNDLE_KEY_MISMATCH_INTERFACE = "mismatch_interface_signature";
50    static final String BUNDLE_KEY_MISMATCH_METHOD = "mismatch_method_signature";
51    static final String BUNDLE_KEY_MISMATCH_FIELD = "mismatch_field_signature";
52    static final String BUNDLE_KEY_CAUGHT_EXCEPTION = "caught_exception";
53
54
55    static final int GET_SIG_TEST_RESULT_TRANSACTION = 101;
56
57    private DeviceResultObserver mResultObserver;
58
59    /**
60     * Define the type of the signature check failures.
61     */
62    public static enum FAILURE_TYPE {
63        MISSING_CLASS,
64        MISSING_INTERFACE,
65        MISSING_METHOD,
66        MISSING_FIELD,
67        MISMATCH_CLASS,
68        MISMATCH_INTERFACE,
69        MISMATCH_METHOD,
70        MISMATCH_FIELD,
71        CAUGHT_EXCEPTION,
72    }
73
74    static final HashMap<FAILURE_TYPE, String> FAILURE_TYPE_TO_KEY =
75            new HashMap<FAILURE_TYPE, String>();
76    static {
77        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISSING_CLASS, BUNDLE_KEY_MISSING_CLASS);
78        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISSING_INTERFACE, BUNDLE_KEY_MISSING_INTERFACE);
79        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISSING_METHOD, BUNDLE_KEY_MISSING_METHOD);
80        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISSING_FIELD, BUNDLE_KEY_MISSING_FIELD);
81        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISMATCH_CLASS, BUNDLE_KEY_MISMATCH_CLASS);
82        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISMATCH_INTERFACE, BUNDLE_KEY_MISMATCH_INTERFACE);
83        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISMATCH_METHOD, BUNDLE_KEY_MISMATCH_METHOD);
84        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.MISMATCH_FIELD, BUNDLE_KEY_MISMATCH_FIELD);
85        FAILURE_TYPE_TO_KEY.put(FAILURE_TYPE.CAUGHT_EXCEPTION, BUNDLE_KEY_CAUGHT_EXCEPTION);
86    }
87
88    @Override
89    protected void onCreate(Bundle savedInstanceState) {
90        super.onCreate(savedInstanceState);
91
92        mResultObserver = new DeviceResultObserver();
93        start();
94        mResultObserver.sendResult(getIntent());
95    }
96
97    /**
98     * Start the signature test.
99     */
100    @SuppressWarnings("unchecked")
101    private void start() {
102        SignatureTest sigTest = new SignatureTest(mResultObserver);
103
104        HashSet<String> excludeSet = getExcludedSet();
105
106        Resources r = getResources();
107
108        Class rClass = R.xml.class;
109        Field[]fs = rClass.getFields();
110        for (Field f : fs) {
111            if (!excludeSet.contains(f.getName())) {
112                try {
113                    sigTest.start(r.getXml(f.getInt(rClass)));
114                } catch (Exception e) {
115                    mResultObserver.notifyFailure(FAILURE_TYPE.CAUGHT_EXCEPTION, e.getMessage(),
116                            e.getMessage());
117                }
118            }
119        }
120    }
121
122    /**
123     * Get the excluded package set, which is defined by res/raw/excludepackages.txt.
124     *
125     * @return The excluded package set.
126     */
127    private HashSet<String> getExcludedSet() {
128        HashSet<String> excludeSet = new HashSet<String>();
129
130        Resources r = getResources();
131        InputStream excludepackage = r.openRawResource(R.raw.excludepackages);
132        BufferedReader reader = new BufferedReader(new InputStreamReader(excludepackage));
133        try {
134            String p = null;
135            while (true) {
136                p = reader.readLine();
137                if (p == null || p.equals("")) {
138                    break;
139                }
140                excludeSet.add(p);
141            }
142            reader.close();
143        } catch (IOException e) {
144            throw new RuntimeException(e);
145        }
146
147        return excludeSet;
148    }
149
150    Bundle mBundle;
151
152    /**
153     * This class is an implementation of the ResultObserver. And it aims to
154     * record the result in the Bundle, and send back to the Instrumentation class
155     * after all results has been recorded.
156     */
157    final class DeviceResultObserver implements ResultObserver {
158        DeviceResultObserver() {
159            mBundle = new Bundle();
160            mBundle.putStringArrayList(BUNDLE_KEY_MISSING_FIELD, new ArrayList<String>());
161            mBundle.putStringArrayList(BUNDLE_KEY_MISSING_METHOD, new ArrayList<String>());
162            mBundle.putStringArrayList(BUNDLE_KEY_MISMATCH_CLASS,
163                    new ArrayList<String>());
164            mBundle.putStringArrayList(BUNDLE_KEY_MISMATCH_FIELD,
165                    new ArrayList<String>());
166            mBundle.putStringArrayList(BUNDLE_KEY_MISMATCH_METHOD,
167                    new ArrayList<String>());
168            mBundle.putStringArrayList(BUNDLE_KEY_MISSING_CLASS, new ArrayList<String>());
169            mBundle.putStringArrayList(BUNDLE_KEY_MISSING_INTERFACE,
170                    new ArrayList<String>());
171            mBundle.putStringArrayList(BUNDLE_KEY_MISMATCH_INTERFACE,
172                    new ArrayList<String>());
173            mBundle.putStringArrayList(BUNDLE_KEY_CAUGHT_EXCEPTION,
174                    new ArrayList<String>());
175        }
176
177        /**
178         * This method is called when all the results has been recorded. And this method
179         * will save the results in IBinder and send back to the Instrumentation class.
180         *
181         * @param i The intent to carry the result.
182         */
183        @SuppressWarnings("deprecation")
184        public void sendResult(Intent i) {
185            SignatureTestLog.d("Send result");
186            if ((mBundle.getStringArrayList(BUNDLE_KEY_MISSING_FIELD).size() == 0)
187                    && (mBundle.getStringArrayList(BUNDLE_KEY_MISSING_CLASS).size() == 0)
188                    && (mBundle.getStringArrayList(BUNDLE_KEY_MISSING_METHOD).size() == 0)
189                    && (mBundle.getStringArrayList(BUNDLE_KEY_MISSING_INTERFACE).size() == 0)
190                    && (mBundle.getStringArrayList(
191                            BUNDLE_KEY_MISMATCH_CLASS).size() == 0)
192                    && (mBundle.getStringArrayList(
193                            BUNDLE_KEY_MISMATCH_FIELD).size() == 0)
194                    && (mBundle.getStringArrayList(
195                            BUNDLE_KEY_MISMATCH_INTERFACE).size() == 0)
196                    && (mBundle.getStringArrayList(
197                            BUNDLE_KEY_MISMATCH_METHOD).size() == 0)
198                    && (mBundle.getStringArrayList(
199                            BUNDLE_KEY_CAUGHT_EXCEPTION).size() == 0)) {
200                SignatureTestLog.d("PASS");
201                mBundle.putBoolean(BUNDLE_KEY_RESULT, true);
202            } else {
203                SignatureTestLog.d("FAIL: " + mBundle.size());
204                mBundle.putBoolean(BUNDLE_KEY_RESULT, false);
205            }
206        }
207
208        public void notifyFailure(FAILURE_TYPE type,
209                                  String name,
210                                  String errorMessage) {
211            SignatureTestLog.d("Failure: ");
212            SignatureTestLog.d("   Type: " + type);
213            SignatureTestLog.d("   Name: " + name);
214            SignatureTestLog.d("   Why : " + errorMessage);
215            mBundle.getStringArrayList(SignatureTestActivity.FAILURE_TYPE_TO_KEY.get(type))
216                    .add(name);
217        }
218    }
219}
220