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