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.util.Log; 19 import java.io.File; 20 import java.io.FileNotFoundException; 21 import java.util.ArrayList; 22 import java.util.Collections; 23 import java.util.Scanner; 24 25 /** A job that checks for any new tombstones before reporting VIRTUAL_DEVICE_BOOT_COMPLETED. 26 * 27 */ 28 public class TombstoneChecker extends JobBase { 29 private static final String LOG_TAG = "GceTombstoneChecker"; 30 private static final String sSnapshotDir = "/data/tombstones"; 31 private static final String sSnapshotFile = "/ts_snap.txt"; 32 private static final String sTsExceptionMessage = "GceTombstoneChecker internal error. "; 33 private static final String sTsFilePrefix = "tombstone"; 34 private final GceFuture<Boolean> mPassed = new GceFuture<Boolean>("GceTombstoneChecker"); 35 private ArrayList<Record> mPreBootRecords = new ArrayList<Record>(); 36 private ArrayList<Record> mPostBootRecords = new ArrayList<Record>(); 37 38 public TombstoneChecker() { 39 super(LOG_TAG); 40 } 41 42 43 @Override 44 public int execute() { 45 if (mPassed.isDone()) { 46 return 0; 47 } 48 49 try { 50 readPreBootSnapshot(); 51 capturePostBootSnapshot(); 52 if (seenNewTombstones()) { 53 Log.e(LOG_TAG, "Tombstones created during boot. "); 54 for (int i = 0; i < mPostBootRecords.size(); i++) { 55 Log.i(LOG_TAG, mPostBootRecords.get(i).getFileName()); 56 } 57 mPassed.set(new Exception("Tombstones created. ")); 58 } else { 59 mPassed.set(true); 60 } 61 } catch(Exception e) { 62 Log.e(LOG_TAG, sTsExceptionMessage + e); 63 mPassed.set(new Exception(sTsExceptionMessage, e)); 64 } 65 66 return 0; 67 } 68 69 @Override 70 public void onDependencyFailed(Exception e) { 71 mPassed.set(e); 72 } 73 74 public GceFuture<Boolean> getTombstoneResult() { 75 return mPassed; 76 } 77 78 private void capturePostBootSnapshot() throws Exception { 79 File dir = new File(sSnapshotDir); 80 File[] files = dir.listFiles(); 81 82 // In K & L, /data/tombstones directory is not created during boot. So 83 // dir.listFiles() can return null. 84 if (files == null) { 85 return; 86 } 87 88 for (int i = 0; i < files.length; i++) { 89 if (files[i].isFile() && files[i].getName().startsWith(sTsFilePrefix)) { 90 long ctime = files[i].lastModified() / 1000; 91 mPostBootRecords.add(new Record(files[i].getName(), ctime)); 92 } 93 } 94 Collections.sort(mPostBootRecords); 95 96 return; 97 } 98 99 private void readPreBootSnapshot() throws Exception { 100 File file = new File(sSnapshotFile); 101 if (!file.isFile()) { 102 throw new FileNotFoundException(sSnapshotFile); 103 } 104 105 Scanner scanner = new Scanner(file); 106 while (scanner.hasNext()) { 107 String[] fields = scanner.nextLine().split(" "); 108 mPreBootRecords.add(new Record(fields[0], Long.parseLong(fields[1]))); 109 } 110 Collections.sort(mPreBootRecords); 111 112 return; 113 } 114 115 private boolean seenNewTombstones() { 116 return !isEqual(mPreBootRecords, mPostBootRecords); 117 } 118 119 private boolean isEqual(ArrayList<Record> preBoot, ArrayList<Record> postBoot) { 120 postBoot.removeAll(preBoot); 121 if (postBoot.size() != 0) { 122 return false; 123 } 124 125 return true; 126 } 127 128 private class Record implements Comparable<Record> { 129 private String mFilename; 130 private long mCtime; 131 132 public Record(String filename, long ctime) { 133 this.mFilename = filename; 134 this.mCtime = ctime; 135 } 136 137 public String getFileName() { 138 return mFilename; 139 } 140 141 public int compareTo(Record r) { 142 if (this == r) { 143 return 0; 144 } 145 146 return (mFilename.compareTo(r.mFilename)); 147 } 148 149 public boolean equals(Object o) { 150 if (o == null) { 151 return false; 152 } 153 154 if (this == o) { 155 return true; 156 } 157 158 Record r = (Record) o; 159 return (mFilename.equals(r.mFilename) && (mCtime == r.mCtime)); 160 } 161 162 public String toString() { 163 StringBuilder sb = new StringBuilder(); 164 sb.append(mFilename).append(" ").append(String.valueOf(mCtime)); 165 return sb.toString(); 166 } 167 } 168 } 169