1d423d8580069520c23e96384ccda272f5692d69dBrett Chabot/*
2d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * Copyright (C) 2012 The Android Open Source Project
3d423d8580069520c23e96384ccda272f5692d69dBrett Chabot *
4d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * Licensed under the Apache License, Version 2.0 (the "License");
5d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * you may not use this file except in compliance with the License.
6d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * You may obtain a copy of the License at
7d423d8580069520c23e96384ccda272f5692d69dBrett Chabot *
8d423d8580069520c23e96384ccda272f5692d69dBrett Chabot *      http://www.apache.org/licenses/LICENSE-2.0
9d423d8580069520c23e96384ccda272f5692d69dBrett Chabot *
10d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * Unless required by applicable law or agreed to in writing, software
11d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * distributed under the License is distributed on an "AS IS" BASIS,
12d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * See the License for the specific language governing permissions and
14d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * limitations under the License.
15d423d8580069520c23e96384ccda272f5692d69dBrett Chabot */
16d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
17d423d8580069520c23e96384ccda272f5692d69dBrett Chabotpackage com.android.applaunchtest;
18d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
19d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.app.ActivityManager;
20d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.app.ActivityManager.ProcessErrorStateInfo;
21d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.content.ComponentName;
22d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.content.Context;
23d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.content.Intent;
24d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.content.pm.PackageManager;
25d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.content.pm.ResolveInfo;
26d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.test.InstrumentationTestCase;
27d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport android.util.Log;
28d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
29d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport java.util.ArrayList;
30d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport java.util.Collection;
31d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport java.util.Iterator;
32d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport java.util.LinkedHashSet;
33d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport java.util.List;
34d423d8580069520c23e96384ccda272f5692d69dBrett Chabotimport java.util.Set;
35d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
36d423d8580069520c23e96384ccda272f5692d69dBrett Chabot/**
37d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * Simple tests that launches a specified app, and waits for a configurable amount of time for
38d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * crashes and ANRs.
39d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * <p/>
40d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * If no crashes occur, test is considered passed.
41d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * <p/>
42d423d8580069520c23e96384ccda272f5692d69dBrett Chabot * Derived from frameworks/base/tests/SmokeTests/... . TODO: consider refactoring to share code
43d423d8580069520c23e96384ccda272f5692d69dBrett Chabot */
44d423d8580069520c23e96384ccda272f5692d69dBrett Chabotpublic class AppLaunchTest extends InstrumentationTestCase {
45d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
46d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private static final String TAG = "AppLaunchTest";
47d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
48d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private ActivityManager mActivityManager;
49d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private PackageManager mPackageManager;
50d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private String mPackageName;
51d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private Context mContext;
52d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private long mWaitTime;
53d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
54d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
55d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * {@inheritDoc}
56d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
57d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    @Override
58d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    public void setUp() throws Exception {
59d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        super.setUp();
60d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        mContext = getInstrumentation().getTargetContext();
61d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertNotNull("failed to get context", mContext);
62d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
63d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        mActivityManager = (ActivityManager)
64d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                mContext.getSystemService(Context.ACTIVITY_SERVICE);
65d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        mPackageManager = mContext.getPackageManager();
66d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertNotNull("failed to get activity manager", mActivityManager);
67d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertNotNull("failed to get package manager", mPackageManager);
68d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
69d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertTrue("Unexpected runner: AppLaunchRunner must be used",
70d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                getInstrumentation() instanceof AppLaunchRunner);
71d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        AppLaunchRunner runner = (AppLaunchRunner)getInstrumentation();
72d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        mPackageName = runner.getAppPackageName();
73d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        mWaitTime  = runner.getAppWaitTime();
74d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertNotNull("package name to launch was not provided", mPackageName);
75d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertNotNull("time to wait for app launch was not provided", mWaitTime);
76d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
77d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
78d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
79d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * A test that runs Launcher-launchable activity for given package name and verifies that no
80d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * ANRs or crashes happened while doing so.
81d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
82d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    public void testLaunchActivity() throws Exception {
83d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Set<ProcessError> errSet = new LinkedHashSet<ProcessError>();
84d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
85d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        ResolveInfo app = getLauncherActivity(mPackageName, mPackageManager);
86d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        assertNotNull(String.format("Could not find launchable activity for %s", mPackageName),
87d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                app);
88d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Collection<ProcessError> errProcs = runOneActivity(app, mWaitTime);
89d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        if (errProcs != null) {
90d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            errSet.addAll(errProcs);
91d423d8580069520c23e96384ccda272f5692d69dBrett Chabot         }
92d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
93d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        if (!errSet.isEmpty()) {
94d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            fail(String.format("Detected %d errors on launch of app %s:\n%s", errSet.size(),
95d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    mPackageName, reportWrappedListContents(errSet)));
96d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
97d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
98d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
99d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
100d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * A method to run the specified Activity and return a {@link Collection} of the Activities that
101d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * were in an error state, as listed by {@link ActivityManager.getProcessesInErrorState()}.
102d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * <p />
103d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * The method will launch the app, wait for waitTime seconds, check for apps in the error state
104d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * and then return.
105d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
106d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    public Collection<ProcessError> runOneActivity(ResolveInfo app, long appLaunchWait) {
107d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
108d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        Log.i(TAG, String.format("Running activity %s/%s", app.activityInfo.packageName,
109d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                app.activityInfo.name));
110d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
111d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // We check for any Crash or ANR dialogs that are already up, and we ignore them.  This is
112d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // so that we don't report crashes that were caused by prior apps.
113d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Collection<ProcessError> preErrProcs =
114d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                ProcessError.fromCollection(mActivityManager.getProcessesInErrorState());
115d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
116d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // launch app, and waitfor it to start/settle
117d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Intent intent = intentForActivity(app);
118d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        mContext.startActivity(intent);
119d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        try {
120d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            Thread.sleep(appLaunchWait);
121d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        } catch (InterruptedException e) {
122d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            // ignore
123d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
124d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
125d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // TODO: inject event to see if app is responding. The smoke tests press 'Home', but
126d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // we don't want to do that here because we want to take screenshot on app launch
127d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
128d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // See if there are any errors.  We wait until down here to give ANRs as much time as
129d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // possible to occur.
130d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Collection<ProcessError> errProcs =
131d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                ProcessError.fromCollection(mActivityManager.getProcessesInErrorState());
132d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
133d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // Distinguish the asynchronous crashes/ANRs from the synchronous ones by checking the
134d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // crash package name against the package name for {@code app}
135d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        if (errProcs != null) {
136d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            Iterator<ProcessError> errIter = errProcs.iterator();
137d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            while (errIter.hasNext()) {
138d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                ProcessError err = errIter.next();
139d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                if (!packageMatches(app, err)) {
140d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    // crash in another package. Just log it for now
141d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    Log.w(TAG, String.format("Detected crash in %s when launching %s",
142d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                            err.info.processName, app.activityInfo.packageName));
143d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    errIter.remove();
144d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                }
145d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
146d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
147d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // Take the difference between the remaining current error processes and the ones that were
148d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // present when we started.  The result is guaranteed to be:
149d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // 1) Errors that are pertinent to this app's package
150d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        // 2) Errors that are pertinent to this particular app invocation
151d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        if (errProcs != null && preErrProcs != null) {
152d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            errProcs.removeAll(preErrProcs);
153d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
154d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
155d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        return errProcs;
156d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
157d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
158d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
159d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * A helper function that checks whether the specified error could have been caused by the
160d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * specified app.
161d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     *
162d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * @param app The app to check against
163d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * @param err The error that we're considering
164d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
165d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private static boolean packageMatches(ResolveInfo app, ProcessError err) {
166d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final String appPkg = app.activityInfo.packageName;
167d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final String errPkg = err.info.processName;
168d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        Log.d(TAG, String.format("packageMatches(%s, %s)", appPkg, errPkg));
169d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        return appPkg.equals(errPkg);
170d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
171d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
172d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
173d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * A helper function to get the launchable activity for the given package name.
174d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
175d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    static ResolveInfo getLauncherActivity(String packageName, PackageManager pm) {
176d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Intent launchable = new Intent(Intent.ACTION_MAIN);
177d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        launchable.addCategory(Intent.CATEGORY_LAUNCHER);
178d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        launchable.setPackage(packageName);
179d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        return pm.resolveActivity(launchable, 0);
180d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
181d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
182d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
183d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * A helper function to create an {@link Intent} to run, given a {@link ResolveInfo} specifying
184d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * an activity to be launched.
185d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
186d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    static Intent intentForActivity(ResolveInfo app) {
187d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final ComponentName component = new ComponentName(app.activityInfo.packageName,
188d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                app.activityInfo.name);
189d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        final Intent intent = new Intent(Intent.ACTION_MAIN);
190d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        intent.setComponent(component);
191d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
192d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        intent.addFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
193d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        return intent;
194d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
195d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
196d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
197d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * Report error reports for {@link ProcessErrorStateInfo} instances that are wrapped inside of
198d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * {@link ProcessError} instances.  Just unwraps and calls
199d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * {@see reportListContents(Collection<ProcessErrorStateInfo>)}.
200d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
201d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    static String reportWrappedListContents(Collection<ProcessError> errList) {
202d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        List<ProcessErrorStateInfo> newList = new ArrayList<ProcessErrorStateInfo>(errList.size());
203d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        for (ProcessError err : errList) {
204d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            newList.add(err.info);
205d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
206d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        return reportListContents(newList);
207d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
208d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
209d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
210d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * This helper function will dump the actual error reports.
211d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     *
212d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * @param errList The error report containing one or more error records.
213d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * @return Returns a string containing all of the errors.
214d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
215d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    private static String reportListContents(Collection<ProcessErrorStateInfo> errList) {
216d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        if (errList == null) return null;
217d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
218d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        StringBuilder builder = new StringBuilder();
219d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
220d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        Iterator<ProcessErrorStateInfo> iter = errList.iterator();
221d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        while (iter.hasNext()) {
222d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            ProcessErrorStateInfo entry = iter.next();
223d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
224d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            String condition;
225d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            switch (entry.condition) {
226d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            case ActivityManager.ProcessErrorStateInfo.CRASHED:
227d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                condition = "a CRASH";
228d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                break;
229d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            case ActivityManager.ProcessErrorStateInfo.NOT_RESPONDING:
230d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                condition = "an ANR";
231d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                break;
232d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            default:
233d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                condition = "an unknown error";
234d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                break;
235d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
236d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
237d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            builder.append(String.format("Process %s encountered %s (%s)", entry.processName,
238d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    condition, entry.shortMsg));
239d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            if (entry.condition == ActivityManager.ProcessErrorStateInfo.CRASHED) {
240d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                builder.append(String.format(" with stack trace:\n%s\n", entry.stackTrace));
241d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
242d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            builder.append("\n");
243d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
244d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        return builder.toString();
245d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
246d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
247d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    /**
248d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
249d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     * crashes are considered equal).
250d423d8580069520c23e96384ccda272f5692d69dBrett Chabot     */
251d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    static class ProcessError {
252d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        public final ProcessErrorStateInfo info;
253d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
254d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        public ProcessError(ProcessErrorStateInfo newInfo) {
255d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            info = newInfo;
256d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
257d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
258d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        public static Collection<ProcessError> fromCollection(Collection<ProcessErrorStateInfo> in)
259d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                {
260d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            if (in == null) {
261d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                return null;
262d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
263d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
264d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            List<ProcessError> out = new ArrayList<ProcessError>(in.size());
265d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            for (ProcessErrorStateInfo info : in) {
266d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                out.add(new ProcessError(info));
267d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
268d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            return out;
269d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
270d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
271d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        private boolean strEquals(String a, String b) {
272d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            if ((a == null) && (b == null)) {
273d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                return true;
274d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            } else if ((a == null) || (b == null)) {
275d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                return false;
276d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            } else {
277d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                return a.equals(b);
278d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
279d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
280d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
281d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        @Override
282d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        public boolean equals(Object other) {
283d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            if (other == null) return false;
284d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            if (!(other instanceof ProcessError)) return false;
285d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            ProcessError peOther = (ProcessError) other;
286d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
287d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            return (info.condition == peOther.info.condition)
288d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && strEquals(info.longMsg, peOther.info.longMsg)
289d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && (info.pid == peOther.info.pid)
290d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && strEquals(info.processName, peOther.info.processName)
291d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && strEquals(info.shortMsg, peOther.info.shortMsg)
292d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && strEquals(info.stackTrace, peOther.info.stackTrace)
293d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && strEquals(info.tag, peOther.info.tag)
294d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                    && (info.uid == peOther.info.uid);
295d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
296d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
297d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        private int hash(Object obj) {
298d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            if (obj == null) {
299d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                return 13;
300d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            } else {
301d423d8580069520c23e96384ccda272f5692d69dBrett Chabot                return obj.hashCode();
302d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            }
303d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
304d423d8580069520c23e96384ccda272f5692d69dBrett Chabot
305d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        @Override
306d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        public int hashCode() {
307d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            int code = 17;
308d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code += info.condition;
309d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code *= hash(info.longMsg);
310d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code += info.pid;
311d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code *= hash(info.processName);
312d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code *= hash(info.shortMsg);
313d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code *= hash(info.stackTrace);
314d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code *= hash(info.tag);
315d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            code += info.uid;
316d423d8580069520c23e96384ccda272f5692d69dBrett Chabot            return code;
317d423d8580069520c23e96384ccda272f5692d69dBrett Chabot        }
318d423d8580069520c23e96384ccda272f5692d69dBrett Chabot    }
319d423d8580069520c23e96384ccda272f5692d69dBrett Chabot}
320