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 boot status 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 BootReporter extends JobBase { 36 private static final String LOG_TAG = "GceBootReporter"; 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 FileOutputStream mKmsgStream = null; 45 private PrintWriter mKmsgWriter = null; 46 private List<String> mMessageList = new ArrayList<String>(); 47 48 49 /** Constructor. */ 50 public BootReporter() { 51 super(LOG_TAG); 52 53 try { 54 mKmsgStream = new FileOutputStream(KLOG_OUTPUT); 55 mKmsgWriter = new PrintWriter(mKmsgStream); 56 } catch (IOException e) { 57 Log.e(LOG_TAG, "Could not open output stream.", e); 58 } 59 } 60 61 62 /** Report boot failure. 63 * 64 * Send message to kernel log and serial console explaining boot failure. 65 */ 66 @Override 67 public void onDependencyFailed(Exception e) { 68 reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_FAILED, e.getMessage())); 69 } 70 71 72 /** Report straggling jobs. 73 * 74 * Reports boot pending, if any of the parent jobs is still awaiting completion 75 * and reschedules itself for re-execution. 76 * 77 * If all jobs have completed, reports boot completed and stops. 78 */ 79 @Override 80 public void onDependencyStraggling(ArrayList<GceFuture<?>> deps) { 81 reportMessage(String.format("%s: %s", VIRTUAL_DEVICE_BOOT_PENDING, 82 GceFuture.toString(deps))); 83 } 84 85 86 /** Report successful boot completion. 87 * 88 * Issue message to serial port confirming successful boot completion and 89 * custom boot completion message, if specified by the user prior to reboot. 90 */ 91 @Override 92 public int execute() { 93 // We suspect that something is throttling our messages and preventing 94 // the following message from being logged to bugreport. 95 // The log is present in logcat log (that we collect independently), yet 96 // occasionally most of the GCEService logs never make it to show up on 97 // bug report. 98 // This may or may not prove to be effective. We need to monitor bugreports 99 // for VIRTUAL_DEVICE_BOOT_COMPLETED messages are being dropped. 100 // 101 // Number chosen at random - yet guaranteed to be prime. 102 try { 103 Thread.sleep(937); 104 } catch (InterruptedException e) {} 105 106 reportMessage(VIRTUAL_DEVICE_BOOT_COMPLETED); 107 return 0; 108 } 109 110 111 private void reportMessage(String message) { 112 Log.i(LOG_TAG, message); 113 mKmsgWriter.printf(KLOG_FORMAT, KLOG_NOTICE, LOG_TAG, message); 114 mKmsgWriter.flush(); 115 DateFormat df = new SimpleDateFormat("MM-dd HH:mm:ss.SSS"); 116 String date = df.format(Calendar.getInstance().getTime()); 117 mMessageList.add("[" + date + "] " + message); 118 } 119 120 121 public void reportBootStarted() { 122 reportMessage(VIRTUAL_DEVICE_BOOT_STARTED); 123 } 124 125 /** Get the list of reported messages so far. 126 */ 127 public List<String> getMessageList() { 128 return mMessageList; 129 } 130 } 131