1919import com .android .internal .os .RuntimeInit ;
2020
2121import android .app .ActivityManager ;
22+ import android .app .ActivityManager .ProcessErrorStateInfo ;
2223import android .content .Context ;
24+ import android .content .ComponentName ;
25+ import android .content .Intent ;
26+ import android .content .pm .PackageManager ;
27+ import android .content .pm .ResolveInfo ;
2328import android .test .AndroidTestCase ;
2429import android .util .Log ;
2530
31+ import java .util .ArrayList ;
32+ import java .util .Collection ;
33+ import java .util .HashSet ;
2634import java .util .Iterator ;
2735import java .util .List ;
36+ import java .util .Set ;
2837
2938/**
3039 * This smoke test is designed to quickly sniff for any error conditions
3140 * encountered after initial startup.
3241 */
3342public class ProcessErrorsTest extends AndroidTestCase {
3443
35- private final String TAG = "ProcessErrorsTest" ;
44+ private static final String TAG = "ProcessErrorsTest" ;
3645
3746 protected ActivityManager mActivityManager ;
47+ protected PackageManager mPackageManager ;
3848
3949 @ Override
4050 public void setUp () throws Exception {
4151 super .setUp ();
42- mActivityManager = (ActivityManager )
52+ mActivityManager = (ActivityManager )
4353 getContext ().getSystemService (Context .ACTIVITY_SERVICE );
54+ mPackageManager = getContext ().getPackageManager ();
4455 }
4556
4657 public void testSetUpConditions () throws Exception {
4758 assertNotNull (mActivityManager );
59+ assertNotNull (mPackageManager );
4860 }
4961
5062 public void testNoProcessErrors () throws Exception {
51- List <ActivityManager .ProcessErrorStateInfo > errList ;
63+ final String reportMsg = checkForProcessErrors ();
64+ if (reportMsg != null ) {
65+ Log .w (TAG , reportMsg );
66+ }
67+
68+ // report a non-empty list back to the test framework
69+ assertNull (reportMsg , reportMsg );
70+ }
71+
72+ private String checkForProcessErrors () throws Exception {
73+ List <ProcessErrorStateInfo > errList ;
5274 errList = mActivityManager .getProcessesInErrorState ();
5375
5476 // note: this contains information about each process that is currently in an error
5577 // condition. if the list is empty (null) then "we're good".
5678
5779 // if the list is non-empty, then it's useful to report the contents of the list
58- // we'll put a copy in the log, and we'll report it back to the framework via the assert.
5980 final String reportMsg = reportListContents (errList );
60- if (reportMsg != null ) {
61- Log .w (TAG , reportMsg );
81+ return reportMsg ;
82+ }
83+
84+ /**
85+ * A test that runs all Launcher-launchable activities and verifies that no ANRs or crashes
86+ * happened while doing so.
87+ * <p />
88+ * FIXME: Doesn't detect multiple crashing apps properly, since the crash dialog for the
89+ * FIXME: first app doesn't go away.
90+ */
91+ public void testRunAllActivities () throws Exception {
92+ final Intent home = new Intent (Intent .ACTION_MAIN );
93+ home .addCategory (Intent .CATEGORY_HOME );
94+ home .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
95+
96+ final Intent launchable = new Intent (Intent .ACTION_MAIN );
97+ launchable .addCategory (Intent .CATEGORY_LAUNCHER );
98+ final List <ResolveInfo > activities = mPackageManager .queryIntentActivities (launchable , 0 );
99+ final Set <ProcessError > errSet = new HashSet <ProcessError >();
100+
101+ for (ResolveInfo info : activities ) {
102+ Log .i (TAG , String .format ("Got %s/%s" , info .activityInfo .packageName ,
103+ info .activityInfo .name ));
104+
105+ // build an Intent to launch the app
106+ final ComponentName component = new ComponentName (info .activityInfo .packageName ,
107+ info .activityInfo .name );
108+ final Intent intent = new Intent (Intent .ACTION_MAIN );
109+ intent .setComponent (component );
110+ intent .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK );
111+
112+ // launch app, and wait 7 seconds for it to start/settle
113+ getContext ().startActivity (intent );
114+ try {
115+ Thread .sleep (7000 );
116+ } catch (InterruptedException e ) {
117+ // ignore
118+ }
119+
120+ // See if there are any errors
121+ Collection <ProcessErrorStateInfo > procs = mActivityManager .getProcessesInErrorState ();
122+ if (procs != null ) {
123+ errSet .addAll (ProcessError .fromCollection (procs ));
124+ }
125+
126+ // Send the "home" intent and wait 2 seconds for us to get there
127+ getContext ().startActivity (home );
128+ try {
129+ Thread .sleep (2000 );
130+ } catch (InterruptedException e ) {
131+ // ignore
132+ }
133+ }
134+
135+ if (!errSet .isEmpty ()) {
136+ fail (String .format ("Got %d errors: %s" , errSet .size (),
137+ reportWrappedListContents (errSet )));
62138 }
63-
64- // report a non-empty list back to the test framework
65- assertNull (reportMsg , errList );
66139 }
67-
140+
141+ private String reportWrappedListContents (Collection <ProcessError > errList ) {
142+ List <ProcessErrorStateInfo > newList = new ArrayList <ProcessErrorStateInfo >(errList .size ());
143+ for (ProcessError err : errList ) {
144+ newList .add (err .info );
145+ }
146+ return reportListContents (newList );
147+ }
148+
68149 /**
69150 * This helper function will dump the actual error reports.
70151 *
71152 * @param errList The error report containing one or more error records.
72153 * @return Returns a string containing all of the errors.
73154 */
74- private String reportListContents (List < ActivityManager . ProcessErrorStateInfo > errList ) {
155+ private String reportListContents (Collection < ProcessErrorStateInfo > errList ) {
75156 if (errList == null ) return null ;
76157
77158 StringBuilder builder = new StringBuilder ();
78159
79- Iterator <ActivityManager . ProcessErrorStateInfo > iter = errList .iterator ();
160+ Iterator <ProcessErrorStateInfo > iter = errList .iterator ();
80161 while (iter .hasNext ()) {
81- ActivityManager . ProcessErrorStateInfo entry = iter .next ();
162+ ProcessErrorStateInfo entry = iter .next ();
82163
83164 String condition ;
84165 switch (entry .condition ) {
@@ -96,8 +177,77 @@ private String reportListContents(List<ActivityManager.ProcessErrorStateInfo> er
96177 builder .append ("Process error " ).append (condition ).append (" " );
97178 builder .append (" " ).append (entry .shortMsg );
98179 builder .append (" detected in " ).append (entry .processName ).append (" " ).append (entry .tag );
180+ builder .append ("\n " );
99181 }
100182 return builder .toString ();
101183 }
102-
184+
185+ /**
186+ * A {@link ProcessErrorStateInfo} wrapper class that hashes how we want (so that equivalent
187+ * crashes are considered equal).
188+ */
189+ private static class ProcessError {
190+ public final ProcessErrorStateInfo info ;
191+
192+ public ProcessError (ProcessErrorStateInfo newInfo ) {
193+ info = newInfo ;
194+ }
195+
196+ public static Collection <ProcessError > fromCollection (Collection <ProcessErrorStateInfo > in )
197+ {
198+ List <ProcessError > out = new ArrayList <ProcessError >(in .size ());
199+ for (ProcessErrorStateInfo info : in ) {
200+ out .add (new ProcessError (info ));
201+ }
202+ return out ;
203+ }
204+
205+ private boolean strEquals (String a , String b ) {
206+ if ((a == null ) && (b == null )) {
207+ return true ;
208+ } else if ((a == null ) || (b == null )) {
209+ return false ;
210+ } else {
211+ return a .equals (b );
212+ }
213+ }
214+
215+ @ Override
216+ public boolean equals (Object other ) {
217+ if (other == null ) return false ;
218+ if (!(other instanceof ProcessError )) return false ;
219+ ProcessError peOther = (ProcessError ) other ;
220+
221+ return (info .condition == peOther .info .condition )
222+ && strEquals (info .longMsg , peOther .info .longMsg )
223+ && (info .pid == peOther .info .pid )
224+ && strEquals (info .processName , peOther .info .processName )
225+ && strEquals (info .shortMsg , peOther .info .shortMsg )
226+ && strEquals (info .stackTrace , peOther .info .stackTrace )
227+ && strEquals (info .tag , peOther .info .tag )
228+ && (info .uid == peOther .info .uid );
229+ }
230+
231+ private int hash (Object obj ) {
232+ if (obj == null ) {
233+ return 13 ;
234+ } else {
235+ return obj .hashCode ();
236+ }
237+ }
238+
239+ @ Override
240+ public int hashCode () {
241+ int code = 17 ;
242+ code += info .condition ;
243+ code *= hash (info .longMsg );
244+ code += info .pid ;
245+ code *= hash (info .processName );
246+ code *= hash (info .shortMsg );
247+ code *= hash (info .stackTrace );
248+ code *= hash (info .tag );
249+ code += info .uid ;
250+ return code ;
251+ }
252+ }
103253}
0 commit comments