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