1 /* 2 * Copyright (C) 2017 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 package com.android.google.gce.gceservice; 17 18 import android.os.Handler; 19 import android.util.Log; 20 import java.io.FileOutputStream; 21 import java.io.IOException; 22 import java.io.PrintWriter; 23 import java.text.DateFormat; 24 import java.text.SimpleDateFormat; 25 import java.util.ArrayList; 26 import java.util.Calendar; 27 import java.util.List; 28 29 /** 30 * Report virtual device boot status and other messages to console. 31 * 32 * This class sends messages to kernel log (and serial console) directly by 33 * writing to /dev/kmsg. 34 */ 35 public class EventReporter extends JobBase { 36 private static final String LOG_TAG = "GceEventReporter"; 37 private static final int KLOG_NOTICE = 5; 38 private static final String KLOG_OUTPUT = "/dev/kmsg"; 39 private static final String KLOG_FORMAT = "<%d>%s: %s\n"; 40 private static final String VIRTUAL_DEVICE_BOOT_STARTED = "VIRTUAL_DEVICE_BOOT_STARTED"; 41 private static final String VIRTUAL_DEVICE_BOOT_PENDING = "VIRTUAL_DEVICE_BOOT_PENDING"; 42 private static final String VIRTUAL_DEVICE_BOOT_COMPLETED = "VIRTUAL_DEVICE_BOOT_COMPLETED"; 43 private static final String VIRTUAL_DEVICE_BOOT_FAILED = "VIRTUAL_DEVICE_BOOT_FAILED"; 44 private static final String VIRTUAL_DEVICE_SCREEN_CHANGED = "VIRTUAL_DEVICE_SCREEN_CHANGED"; 45 private FileOutputStream mKmsgStream = null; 46 private PrintWriter mKmsgWriter = null; 47 private List<String> mMessageList = new ArrayList<String>(); 48 49 50 /** Constructor. */ EventReporter()51 public EventReporter() { 52 super(LOG_TAG); 53 54 try { 55 mKmsgStream = new FileOutputStream(KLOG_OUTPUT); 56 mKmsgWriter = new PrintWriter(mKmsgStream); 57 } catch (IOException e) { 58 Log.e(LOG_TAG, "Could not open output stream.", e); 59 } 60 } 61 62 63 /** Report boot failure. 64 * 65 * Send message to kernel log and serial console explaining boot failure. 66 */ 67 @Override onDependencyFailed(Exception e)68 public void onDependencyFailed(Exception e) { 69 reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_FAILED, e.getMessage())); 70 } 71 72 73 /** Report straggling jobs. 74 * 75 * Reports boot pending, if any of the parent jobs is still awaiting completion 76 * and reschedules itself for re-execution. 77 * 78 * If all jobs have completed, reports boot completed and stops. 79 */ 80 @Override onDependencyStraggling(ArrayList<GceFuture<?>> deps)81 public void onDependencyStraggling(ArrayList<GceFuture<?>> deps) { 82 reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_PENDING, 83 GceFuture.toString(deps))); 84 } 85 86 87 /** Report successful boot completion. 88 * 89 * Issue message to serial port confirming successful boot completion and 90 * custom boot completion message, if specified by the user prior to reboot. 91 */ 92 @Override execute()93 public int execute() { 94 // We suspect that something is throttling our messages and preventing 95 // the following message from being logged to bugreport. 96 // The log is present in logcat log (that we collect independently), yet 97 // occasionally most of the GCEService logs never make it to show up on 98 // bug report. 99 // This may or may not prove to be effective. We need to monitor bugreports 100 // for VIRTUAL_DEVICE_BOOT_COMPLETED messages are being dropped. 101 // 102 // Number chosen at random - yet guaranteed to be prime. 103 try { 104 Thread.sleep(937); 105 } catch (InterruptedException e) {} 106 107 reportMessage(VIRTUAL_DEVICE_BOOT_COMPLETED); 108 return 0; 109 } 110 111 reportMessage(String message)112 public void reportMessage(String message) { 113 Log.i(LOG_TAG, message); 114 mKmsgWriter.printf(KLOG_FORMAT, KLOG_NOTICE, LOG_TAG, message); 115 mKmsgWriter.flush(); 116 DateFormat df = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 117 String date = df.format(Calendar.getInstance().getTime()); 118 mMessageList.add("[" + date + "] " + message); 119 } 120 121 reportBootStarted()122 public void reportBootStarted() { 123 reportMessage(VIRTUAL_DEVICE_BOOT_STARTED); 124 } 125 reportScreenChanged(int width, int height, int dpi, int rotation)126 public void reportScreenChanged(int width, int height, int dpi, int rotation) { 127 reportMessage(String.format("%s width=%d height=%d dpi=%d rotation=%d", 128 VIRTUAL_DEVICE_SCREEN_CHANGED, 129 width, height, dpi, rotation)); 130 } 131 132 /** Get the list of reported messages so far. 133 */ getMessageList()134 public List<String> getMessageList() { 135 return mMessageList; 136 } 137 } 138