• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016, 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 
17 package android.test.crashcollector;
18 
19 import android.app.IActivityController;
20 import android.app.IActivityManager;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.os.IBinder;
24 import android.os.IBinder.DeathRecipient;
25 import android.os.Process;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 import android.os.SystemClock;
29 import android.util.Log;
30 
31 import java.io.File;
32 import java.text.SimpleDateFormat;
33 import java.util.Date;
34 import java.util.HashSet;
35 
36 /**
37  * Main class for the crash collector that installs an activity controller to monitor app errors
38  */
39 public class Collector {
40 
41     private static final String LOG_TAG = "CrashCollector";
42     private static final long CHECK_AM_INTERVAL_MS = 5 * 1000;
43     private static final long MAX_CHECK_AM_TIMEOUT_MS = 30 * 1000;
44     private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("yyyy-MM-dd-HH.mm.ss");
45     private static final File TOMBSTONES_PATH = new File("/data/tombstones");
46     private HashSet<String> mTombstones = null;
47 
48     /**
49      * Command-line entry point.
50      *
51      * @param args The command-line arguments
52      */
main(String[] args)53     public static void main(String[] args) {
54         // Set the process name showing in "ps" or "top"
55         Process.setArgV0("android.test.crashcollector");
56 
57         int resultCode = (new Collector()).run(args);
58         System.exit(resultCode);
59     }
60 
61     /**
62      * Command execution entry point
63      * @param args
64      * @return
65      * @throws RemoteException
66      */
run(String[] args)67     public int run(String[] args) {
68         // recipient for activity manager death so that command can survive runtime restart
69         final IBinder.DeathRecipient death = new DeathRecipient() {
70             @Override
71             public void binderDied() {
72                 synchronized (this) {
73                     notifyAll();
74                 }
75             }
76         };
77         IBinder am = blockUntilSystemRunning(MAX_CHECK_AM_TIMEOUT_MS);
78         if (am == null) {
79             print("FATAL: Cannot get activity manager, is system running?");
80             return -1;
81         }
82         IActivityController controller = new CrashCollector();
83         do {
84             try {
85                 // set activity controller
86                 IActivityManager iam = IActivityManager.Stub.asInterface(am);
87                 iam.setActivityController(controller, false);
88                 // register death recipient for activity manager
89                 am.linkToDeath(death, 0);
90             } catch (RemoteException re) {
91                 print("FATAL: cannot set activity controller, is system running?");
92                 re.printStackTrace();
93                 return -1;
94             }
95             // monitor runtime restart (crash/kill of system server)
96             synchronized (death) {
97                 while (am.isBinderAlive()) {
98                     try {
99                         Log.d(LOG_TAG, "Monitoring death of system server.");
100                         death.wait();
101                     } catch (InterruptedException e) {
102                         // ignore
103                     }
104                 }
105                 Log.w(LOG_TAG, "Detected crash of system server.");
106                 am = blockUntilSystemRunning(MAX_CHECK_AM_TIMEOUT_MS);
107             }
108         } while (true);
109         // for now running indefinitely, until a better mechanism is found to signal shutdown
110     }
111 
print(String line)112     private void print(String line) {
113         System.err.println(String.format("%s %s", TIME_FORMAT.format(new Date()), line));
114     }
115 
116     /**
117      * Blocks until system server is running, or timeout has reached
118      * @param timeout
119      * @return
120      */
blockUntilSystemRunning(long timeout)121     private IBinder blockUntilSystemRunning(long timeout) {
122         // waiting for activity manager to come back
123         long start = SystemClock.uptimeMillis();
124         IBinder am = null;
125         while (SystemClock.uptimeMillis() - start < MAX_CHECK_AM_TIMEOUT_MS) {
126             am = ServiceManager.checkService(Context.ACTIVITY_SERVICE);
127             if (am != null) {
128                 break;
129             } else {
130                 Log.d(LOG_TAG, "activity manager not ready yet, continue waiting.");
131                 try {
132                     Thread.sleep(CHECK_AM_INTERVAL_MS);
133                 } catch (InterruptedException e) {
134                     // break out of current loop upon interruption
135                     break;
136                 }
137             }
138         }
139         return am;
140     }
141 
checkNativeCrashes()142     private boolean checkNativeCrashes() {
143         String[] tombstones = TOMBSTONES_PATH.list();
144 
145         // shortcut path for usually empty directory, so we don't waste even
146         // more objects
147         if ((tombstones == null) || (tombstones.length == 0)) {
148             mTombstones = null;
149             return false;
150         }
151 
152         // use set logic to look for new files
153         HashSet<String> newStones = new HashSet<String>();
154         for (String x : tombstones) {
155             newStones.add(x);
156         }
157 
158         boolean result = (mTombstones == null) || !mTombstones.containsAll(newStones);
159 
160         // keep the new list for the next time
161         mTombstones = newStones;
162 
163         return result;
164     }
165 
166     private class CrashCollector extends IActivityController.Stub {
167 
168         @Override
activityStarting(Intent intent, String pkg)169         public boolean activityStarting(Intent intent, String pkg) throws RemoteException {
170             // check native crashes when we have a chance
171             if (checkNativeCrashes()) {
172                 print("NATIVE: new tombstones");
173             }
174             return true;
175         }
176 
177         @Override
activityResuming(String pkg)178         public boolean activityResuming(String pkg) throws RemoteException {
179             // check native crashes when we have a chance
180             if (checkNativeCrashes()) {
181                 print("NATIVE: new tombstones");
182             }
183             return true;
184         }
185 
186         @Override
appCrashed(String processName, int pid, String shortMsg, String longMsg, long timeMillis, String stackTrace)187         public boolean appCrashed(String processName, int pid, String shortMsg, String longMsg,
188                 long timeMillis, String stackTrace) throws RemoteException {
189             if (processName == null) {
190                 print("CRASH: null process name, assuming system");
191             } else {
192                 print("CRASH: " + processName);
193             }
194             return false;
195         }
196 
197         @Override
appEarlyNotResponding(String processName, int pid, String annotation)198         public int appEarlyNotResponding(String processName, int pid, String annotation)
199                 throws RemoteException {
200             // ignore
201             return 0;
202         }
203 
204         @Override
appNotResponding(String processName, int pid, String processStats)205         public int appNotResponding(String processName, int pid, String processStats)
206                 throws RemoteException {
207             print("ANR: " + processName);
208             return -1;
209         }
210 
211         @Override
systemNotResponding(String msg)212         public int systemNotResponding(String msg) throws RemoteException {
213             print("WATCHDOG: " + msg);
214             return -1;
215         }
216     }
217 }
218