SecurityLogMonitor.java revision c3cd05f8a45ab789aae1cb553df86f94667d595a
1/* 2 * Copyright (C) 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 17package com.android.server.devicepolicy; 18 19import android.app.admin.DeviceAdminReceiver; 20import android.auditing.SecurityLog; 21import android.auditing.SecurityLog.SecurityEvent; 22import android.util.Log; 23import android.util.Slog; 24 25import com.android.internal.annotations.GuardedBy; 26 27import java.io.IOException; 28import java.util.ArrayList; 29import java.util.List; 30import java.util.concurrent.TimeUnit; 31 32import android.os.Process; 33 34/** 35 * A class managing access to the security logs. It maintains an internal buffer of pending 36 * logs to be retrieved by the device owner. The logs are retrieved from the logd daemon via 37 * JNI binding, and kept until device owner has retrieved to prevent loss of logs. Access to 38 * the logs from the device owner is rate-limited, and device owner is notified when the logs 39 * are ready to be retrieved. This happens every two hours, or when our internal buffer is 40 * larger than a certain threshold. 41 */ 42class SecurityLogMonitor implements Runnable { 43 private final DevicePolicyManagerService mService; 44 45 SecurityLogMonitor(DevicePolicyManagerService service) { 46 mService = service; 47 } 48 49 private static final boolean DEBUG = false; 50 private static final String TAG = "SecurityLogMonitor"; 51 /** 52 * Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N} 53 * it should be less than 100 bytes), setting 1024 entries as the threshold to notify Device 54 * Owner. 55 */ 56 private static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024; 57 /** 58 * The maximum number of entries we should store before dropping earlier logs, to limit the 59 * memory usage. 60 */ 61 private static final int BUFFER_ENTRIES_MAXIMUM_LEVEL = BUFFER_ENTRIES_NOTIFICATION_LEVEL * 10; 62 /** 63 * How often should Device Owner be notified under normal circumstances. 64 */ 65 private static final long RATE_LIMIT_INTERVAL_MILLISECONDS = TimeUnit.HOURS.toMillis(2); 66 /** 67 * Internally how often should the monitor poll the security logs from logd. 68 */ 69 private static final long POLLING_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(1); 70 71 @GuardedBy("this") 72 private Thread mMonitorThread = null; 73 @GuardedBy("this") 74 private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<SecurityEvent>(); 75 @GuardedBy("this") 76 private boolean mAllowedToRetrieve = false; 77 // When DO will be allowed to retrieves the log, in milliseconds. 78 @GuardedBy("this") 79 private long mNextAllowedRetrivalTimeMillis = -1; 80 81 synchronized void start() { 82 if (mMonitorThread == null) { 83 mPendingLogs = new ArrayList<SecurityEvent>(); 84 mAllowedToRetrieve = false; 85 mNextAllowedRetrivalTimeMillis = -1; 86 87 mMonitorThread = new Thread(this); 88 mMonitorThread.start(); 89 } 90 } 91 92 synchronized void stop() { 93 if (mMonitorThread != null) { 94 mMonitorThread.interrupt(); 95 try { 96 mMonitorThread.join(TimeUnit.SECONDS.toMillis(5)); 97 } catch (InterruptedException e) { 98 Log.e(TAG, "Interrupted while waiting for thread to stop", e); 99 } 100 mMonitorThread = null; 101 } 102 } 103 104 /** 105 * Returns the new batch of logs since the last call to this method. Returns null if 106 * rate limit is exceeded. 107 */ 108 synchronized List<SecurityEvent> retrieveLogs() { 109 if (mAllowedToRetrieve) { 110 mAllowedToRetrieve = false; 111 mNextAllowedRetrivalTimeMillis = System.currentTimeMillis() 112 + RATE_LIMIT_INTERVAL_MILLISECONDS; 113 List<SecurityEvent> result = mPendingLogs; 114 mPendingLogs = new ArrayList<SecurityEvent>(); 115 return result; 116 } else { 117 return null; 118 } 119 } 120 121 @Override 122 public void run() { 123 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 124 125 ArrayList<SecurityEvent> logs = new ArrayList<SecurityEvent>(); 126 // The timestamp of the latest log entry that has been read, in nanoseconds 127 long lastLogTimestampNanos = -1; 128 while (!Thread.currentThread().isInterrupted()) { 129 try { 130 Thread.sleep(POLLING_INTERVAL_MILLISECONDS); 131 132 if (lastLogTimestampNanos < 0) { 133 // Non-blocking read that returns all logs immediately. 134 if (DEBUG) Slog.d(TAG, "SecurityLog.readEvents"); 135 SecurityLog.readEvents(logs); 136 } else { 137 if (DEBUG) Slog.d(TAG, 138 "SecurityLog.readEventsSince: " + lastLogTimestampNanos); 139 // Non-blocking read that returns all logs >= the timestamp immediately. 140 SecurityLog.readEventsSince(lastLogTimestampNanos + 1, logs); 141 } 142 if (!logs.isEmpty()) { 143 if (DEBUG) Slog.d(TAG, "processing new logs"); 144 synchronized (this) { 145 mPendingLogs.addAll(logs); 146 if (mPendingLogs.size() > BUFFER_ENTRIES_MAXIMUM_LEVEL) { 147 // Truncate buffer down to half of BUFFER_ENTRIES_MAXIMUM_LEVEL 148 mPendingLogs = new ArrayList<SecurityEvent>(mPendingLogs.subList( 149 mPendingLogs.size() - (BUFFER_ENTRIES_MAXIMUM_LEVEL / 2), 150 mPendingLogs.size())); 151 } 152 } 153 lastLogTimestampNanos = logs.get(logs.size() - 1).getTimeNanos(); 154 logs.clear(); 155 } 156 notifyDeviceOwnerIfNeeded(); 157 } catch (IOException e) { 158 Log.e(TAG, "Failed to read security log", e); 159 } catch (InterruptedException e) { 160 Log.i(TAG, "Thread interrupted, exiting.", e); 161 // We are asked to stop. 162 break; 163 } 164 } 165 if (DEBUG) Slog.d(TAG, "MonitorThread exit."); 166 synchronized (this) { 167 // Reset state and clear buffer 168 mPendingLogs = new ArrayList<SecurityEvent>(); 169 mAllowedToRetrieve = false; 170 mNextAllowedRetrivalTimeMillis = -1; 171 } 172 } 173 174 private void notifyDeviceOwnerIfNeeded() { 175 boolean shouldNotifyDO = false; 176 boolean allowToRetrieveNow = false; 177 synchronized (this) { 178 int logSize = mPendingLogs.size(); 179 if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) { 180 // Allow DO to retrieve logs if too many pending logs 181 allowToRetrieveNow = true; 182 } else if (logSize > 0) { 183 if (mNextAllowedRetrivalTimeMillis == -1 || 184 System.currentTimeMillis() >= mNextAllowedRetrivalTimeMillis) { 185 // Rate limit reset 186 allowToRetrieveNow = true; 187 } 188 } 189 shouldNotifyDO = (!mAllowedToRetrieve) && allowToRetrieveNow; 190 mAllowedToRetrieve = allowToRetrieveNow; 191 } 192 if (shouldNotifyDO) { 193 if (DEBUG) Slog.d(TAG, "notify DO"); 194 mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_SECURITY_LOGS_AVAILABLE, 195 null); 196 } 197 } 198}