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 static java.util.concurrent.TimeUnit.MILLISECONDS; 20 import static java.util.concurrent.TimeUnit.NANOSECONDS; 21 22 import android.app.admin.DeviceAdminReceiver; 23 import android.app.admin.IAuditLogEventsCallback; 24 import android.app.admin.SecurityLog; 25 import android.app.admin.SecurityLog.SecurityEvent; 26 import android.app.admin.flags.Flags; 27 import android.os.Handler; 28 import android.os.IBinder; 29 import android.os.Process; 30 import android.os.RemoteException; 31 import android.os.SystemClock; 32 import android.util.Log; 33 import android.util.Slog; 34 import android.util.SparseArray; 35 36 import com.android.internal.annotations.GuardedBy; 37 import com.android.internal.annotations.VisibleForTesting; 38 import com.android.server.utils.Slogf; 39 40 import java.io.IOException; 41 import java.util.ArrayDeque; 42 import java.util.ArrayList; 43 import java.util.Iterator; 44 import java.util.List; 45 import java.util.concurrent.Semaphore; 46 import java.util.concurrent.TimeUnit; 47 import java.util.concurrent.locks.Lock; 48 import java.util.concurrent.locks.ReentrantLock; 49 50 /** 51 * A class managing access to the security logs. It maintains an internal buffer of pending 52 * logs to be retrieved by the device owner. The logs are retrieved from the logd daemon via 53 * JNI binding, and kept until device owner has retrieved to prevent loss of logs. Access to 54 * the logs from the device owner is rate-limited, and device owner is notified when the logs 55 * are ready to be retrieved. This happens every two hours, or when our internal buffer is 56 * larger than a certain threshold. 57 */ 58 class SecurityLogMonitor implements Runnable { 59 private final DevicePolicyManagerService mService; 60 61 private final Lock mLock = new ReentrantLock(); 62 63 private int mEnabledUser; 64 SecurityLogMonitor(DevicePolicyManagerService service, Handler handler)65 SecurityLogMonitor(DevicePolicyManagerService service, Handler handler) { 66 mService = service; 67 mId = 0; 68 mLastForceNanos = System.nanoTime(); 69 mHandler = handler; 70 } 71 72 private static final boolean DEBUG = false; // STOPSHIP if true. 73 private static final String TAG = "SecurityLogMonitor"; 74 /** 75 * Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N} 76 * it should be less than 100 bytes), setting 1024 entries as the threshold to notify Device 77 * Owner. 78 */ 79 @VisibleForTesting static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024; 80 /** 81 * The maximum number of entries we should store before dropping earlier logs, to limit the 82 * memory usage. 83 */ 84 private static final int BUFFER_ENTRIES_MAXIMUM_LEVEL = BUFFER_ENTRIES_NOTIFICATION_LEVEL * 10; 85 /** 86 * Critical log buffer level, 90% of capacity. 87 */ 88 private static final int BUFFER_ENTRIES_CRITICAL_LEVEL = BUFFER_ENTRIES_MAXIMUM_LEVEL * 9 / 10; 89 /** 90 * How often should Device Owner be notified under normal circumstances. 91 */ 92 private static final long RATE_LIMIT_INTERVAL_MS = TimeUnit.HOURS.toMillis(2); 93 /** 94 * How often to retry the notification about available logs if it is ignored or missed by DO. 95 */ 96 private static final long BROADCAST_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(30); 97 /** 98 * Internally how often should the monitor poll the security logs from logd. 99 */ 100 private static final long POLLING_INTERVAL_MS = TimeUnit.MINUTES.toMillis(1); 101 /** 102 * Overlap between two subsequent log requests, required to avoid losing out of order events. 103 */ 104 private static final long OVERLAP_NS = TimeUnit.SECONDS.toNanos(3); 105 106 /** Minimum time between forced fetch attempts. */ 107 private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10); 108 109 /** 110 * Monitor thread is not null iff SecurityLogMonitor is running, i.e. started and not stopped. 111 * Pausing doesn't change it. 112 */ 113 @GuardedBy("mLock") 114 private Thread mMonitorThread = null; 115 @GuardedBy("mLock") 116 private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<>(); 117 @GuardedBy("mLock") 118 private long mId; 119 @GuardedBy("mLock") 120 private boolean mAllowedToRetrieve = false; 121 122 // Whether we have already logged the fact that log buffer reached 90%, to avoid dupes. 123 @GuardedBy("mLock") 124 private boolean mCriticalLevelLogged = false; 125 126 private boolean mLegacyLogEnabled; 127 private boolean mAuditLogEnabled; 128 129 /** 130 * Last events fetched from log to check for overlap between batches. We can leave it empty if 131 * we are sure there will be no overlap anymore, e.g. when we get empty batch. 132 */ 133 private final ArrayList<SecurityEvent> mLastEvents = new ArrayList<>(); 134 /** Timestamp of the very last event, -1 means request from the beginning of time. */ 135 private long mLastEventNanos = -1; 136 137 /** 138 * When DO will be allowed to retrieve the log, in milliseconds since boot (as per 139 * {@link SystemClock#elapsedRealtime()}). After that it will mark the time to retry broadcast. 140 */ 141 @GuardedBy("mLock") 142 private long mNextAllowedRetrievalTimeMillis = -1; 143 @GuardedBy("mLock") 144 private boolean mPaused = false; 145 146 /** Semaphore used to force log fetching on request from adb. */ 147 private final Semaphore mForceSemaphore = new Semaphore(0 /* permits */); 148 149 /** The last timestamp when force fetch was used, to prevent abuse. */ 150 @GuardedBy("mForceSemaphore") 151 private long mLastForceNanos = 0; 152 153 /** 154 * Handler shared with DPMS. 155 */ 156 private final Handler mHandler; 157 158 /** 159 * Oldest events get purged from audit log buffer if total number exceeds this value. 160 */ 161 private static final int MAX_AUDIT_LOG_EVENTS = 10000; 162 /** 163 * Events older than this get purged from audit log buffer. 164 */ 165 private static final long MAX_AUDIT_LOG_EVENT_AGE_NS = TimeUnit.HOURS.toNanos(8); 166 167 /** 168 * Audit log callbacks keyed by UID. The code should maintain the following invariant: all 169 * callbacks in this map have received (or are scheduled to receive) all events in 170 * mAuditLogEventsBuffer. To ensure this, before a callback is put into this map, it must be 171 * scheduled to receive all the events in the buffer, and conversely, before a new chunk of 172 * events is added to the buffer, it must be scheduled to be sent to all callbacks already in 173 * this list. All scheduling should happen on mHandler, so that they aren't reordered, and 174 * while holding the lock. This ensures that no callback misses an event or receives a duplicate 175 * or out of order events. 176 */ 177 @GuardedBy("mLock") 178 private final SparseArray<IAuditLogEventsCallback> mAuditLogCallbacks = new SparseArray<>(); 179 180 /** 181 * Audit log event buffer. It is shrunk automatically whenever either there are too many events 182 * or the oldest one is too old. 183 */ 184 @GuardedBy("mLock") 185 private final ArrayDeque<SecurityEvent> mAuditLogEventBuffer = new ArrayDeque<>(); 186 187 /** 188 * Start security logging. 189 * 190 * @param enabledUser which user logging is enabled on, or USER_ALL to enable logging for all 191 * users on the device. 192 */ start(int enabledUser)193 void start(int enabledUser) { 194 Slog.i(TAG, "Starting security logging for user " + enabledUser); 195 mEnabledUser = enabledUser; 196 mLock.lock(); 197 try { 198 if (mMonitorThread == null) { 199 resetLegacyBufferLocked(); 200 startMonitorThreadLocked(); 201 } else { 202 Slog.i(TAG, "Security log monitor thread is already running"); 203 } 204 } finally { 205 mLock.unlock(); 206 } 207 } 208 stop()209 void stop() { 210 Slog.i(TAG, "Stopping security logging."); 211 mLock.lock(); 212 try { 213 if (mMonitorThread != null) { 214 stopMonitorThreadLocked(); 215 resetLegacyBufferLocked(); 216 } 217 } finally { 218 mLock.unlock(); 219 } 220 } 221 setLoggingParams(int enabledUser, boolean legacyLogEnabled, boolean auditLogEnabled)222 void setLoggingParams(int enabledUser, boolean legacyLogEnabled, boolean auditLogEnabled) { 223 Slogf.i(TAG, "Setting logging params, user = %d -> %d, legacy: %b -> %b, audit %b -> %b", 224 mEnabledUser, enabledUser, mLegacyLogEnabled, legacyLogEnabled, mAuditLogEnabled, 225 auditLogEnabled); 226 mLock.lock(); 227 try { 228 mEnabledUser = enabledUser; 229 if (mMonitorThread == null && (legacyLogEnabled || auditLogEnabled)) { 230 startMonitorThreadLocked(); 231 } else if (mMonitorThread != null && !legacyLogEnabled && !auditLogEnabled) { 232 stopMonitorThreadLocked(); 233 } 234 235 if (mLegacyLogEnabled != legacyLogEnabled) { 236 resetLegacyBufferLocked(); 237 mLegacyLogEnabled = legacyLogEnabled; 238 } 239 240 if (mAuditLogEnabled != auditLogEnabled) { 241 resetAuditBufferLocked(); 242 mAuditLogEnabled = auditLogEnabled; 243 } 244 } finally { 245 mLock.unlock(); 246 } 247 248 } 249 250 @GuardedBy("mLock") startMonitorThreadLocked()251 private void startMonitorThreadLocked() { 252 mId = 0; 253 mPaused = false; 254 mMonitorThread = new Thread(this); 255 mMonitorThread.start(); 256 SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED); 257 Slog.i(TAG, "Security log monitor thread started"); 258 } 259 260 @GuardedBy("mLock") stopMonitorThreadLocked()261 private void stopMonitorThreadLocked() { 262 mMonitorThread.interrupt(); 263 try { 264 mMonitorThread.join(TimeUnit.SECONDS.toMillis(5)); 265 } catch (InterruptedException e) { 266 Log.e(TAG, "Interrupted while waiting for thread to stop", e); 267 } 268 mMonitorThread = null; 269 SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STOPPED); 270 } 271 272 @GuardedBy("mLock") resetLegacyBufferLocked()273 private void resetLegacyBufferLocked() { 274 mPendingLogs = new ArrayList<>(); 275 mCriticalLevelLogged = false; 276 mAllowedToRetrieve = false; 277 mNextAllowedRetrievalTimeMillis = -1; 278 Slog.i(TAG, "Legacy buffer reset."); 279 } 280 281 @GuardedBy("mLock") resetAuditBufferLocked()282 private void resetAuditBufferLocked() { 283 mAuditLogEventBuffer.clear(); 284 mAuditLogCallbacks.clear(); 285 } 286 287 /** 288 * If logs are being collected, keep collecting them but stop notifying the device owner that 289 * new logs are available (since they cannot be retrieved). 290 */ pause()291 void pause() { 292 Slog.i(TAG, "Paused."); 293 294 mLock.lock(); 295 mPaused = true; 296 mAllowedToRetrieve = false; 297 mLock.unlock(); 298 } 299 300 /** 301 * If logs are being collected, start notifying the device owner when logs are ready to be 302 * retrieved again (if it was paused). 303 * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt 304 * to notify the device owner. Therefore calling identity should be cleared before calling it 305 * (in case the method is called from a user other than the DO's user). 306 */ resume()307 void resume() { 308 mLock.lock(); 309 try { 310 if (!mPaused) { 311 Log.d(TAG, "Attempted to resume, but logging is not paused."); 312 return; 313 } 314 mPaused = false; 315 mAllowedToRetrieve = false; 316 } finally { 317 mLock.unlock(); 318 } 319 320 Slog.i(TAG, "Resumed."); 321 try { 322 notifyDeviceOwnerOrProfileOwnerIfNeeded(false /* force */); 323 } catch (InterruptedException e) { 324 Log.w(TAG, "Thread interrupted.", e); 325 } 326 } 327 328 /** 329 * Discard all collected logs. 330 */ discardLogs()331 void discardLogs() { 332 mLock.lock(); 333 mAllowedToRetrieve = false; 334 mPendingLogs = new ArrayList<>(); 335 mCriticalLevelLogged = false; 336 mLock.unlock(); 337 Slog.i(TAG, "Discarded all logs."); 338 } 339 340 /** 341 * Returns the new batch of logs since the last call to this method. Returns null if 342 * rate limit is exceeded. 343 */ retrieveLogs()344 List<SecurityEvent> retrieveLogs() { 345 mLock.lock(); 346 try { 347 if (mAllowedToRetrieve) { 348 mAllowedToRetrieve = false; 349 mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime() 350 + RATE_LIMIT_INTERVAL_MS; 351 List<SecurityEvent> result = mPendingLogs; 352 mPendingLogs = new ArrayList<>(); 353 mCriticalLevelLogged = false; 354 return result; 355 } else { 356 return null; 357 } 358 } finally { 359 mLock.unlock(); 360 } 361 } 362 363 /** 364 * Requests the next (or the first) batch of events from the log with appropriate timestamp. 365 */ getNextBatch(ArrayList<SecurityEvent> newLogs)366 private void getNextBatch(ArrayList<SecurityEvent> newLogs) throws IOException { 367 if (mLastEventNanos < 0) { 368 // Non-blocking read that returns all logs immediately. 369 if (DEBUG) Slog.d(TAG, "SecurityLog.readEvents"); 370 SecurityLog.readEvents(newLogs); 371 } else { 372 // If we have last events from the previous batch, request log events with time overlap 373 // with previously retrieved messages to avoid losing events due to reordering in logd. 374 final long startNanos = mLastEvents.isEmpty() 375 ? mLastEventNanos : Math.max(0, mLastEventNanos - OVERLAP_NS); 376 if (DEBUG) Slog.d(TAG, "SecurityLog.readEventsSince: " + startNanos); 377 // Non-blocking read that returns all logs with timestamps >= startNanos immediately. 378 SecurityLog.readEventsSince(startNanos, newLogs); 379 } 380 381 // Sometimes events may be reordered in logd due to simultaneous readers and writers. In 382 // this case, we have to sort it to make overlap checking work. This is very unlikely. 383 for (int i = 0; i < newLogs.size() - 1; i++) { 384 if (newLogs.get(i).getTimeNanos() > newLogs.get(i+1).getTimeNanos()) { 385 if (DEBUG) Slog.d(TAG, "Got out of order events, sorting."); 386 // Sort using comparator that compares timestamps. 387 newLogs.sort((e1, e2) -> Long.signum(e1.getTimeNanos() - e2.getTimeNanos())); 388 break; 389 } 390 } 391 SecurityLog.redactEvents(newLogs, mEnabledUser); 392 if (DEBUG) Slog.d(TAG, "Got " + newLogs.size() + " new events."); 393 } 394 395 /** 396 * Save the last events for overlap checking with the next batch. 397 */ saveLastEvents(ArrayList<SecurityEvent> newLogs)398 private void saveLastEvents(ArrayList<SecurityEvent> newLogs) { 399 mLastEvents.clear(); 400 if (newLogs.isEmpty()) { 401 // This can happen if no events were logged yet or the buffer got cleared. In this case 402 // we aren't going to have any overlap next time, leave mLastEvents events empty. 403 return; 404 } 405 406 // Save the last timestamp. 407 mLastEventNanos = newLogs.get(newLogs.size() - 1).getTimeNanos(); 408 // Position of the earliest event that has to be saved. Start from the penultimate event, 409 // going backward. 410 int pos = newLogs.size() - 2; 411 while (pos >= 0 && mLastEventNanos - newLogs.get(pos).getTimeNanos() < OVERLAP_NS) { 412 pos--; 413 } 414 // We either run past the start of the list or encountered an event that is too old to keep. 415 pos++; 416 mLastEvents.addAll(newLogs.subList(pos, newLogs.size())); 417 if (DEBUG) Slog.d(TAG, mLastEvents.size() + " events saved for overlap check"); 418 } 419 420 /** 421 * Merges a new batch into already fetched logs and deals with overlapping and out of order 422 * events. 423 */ 424 @GuardedBy("mLock") mergeBatchLocked(final ArrayList<SecurityEvent> newLogs)425 private void mergeBatchLocked(final ArrayList<SecurityEvent> newLogs) { 426 List<SecurityEvent> dedupedLogs = new ArrayList<>(); 427 // Run through the first events of the batch to check if there is an overlap with previous 428 // batch and if so, skip overlapping events. Events are sorted by timestamp, so we can 429 // compare it in linear time by advancing two pointers, one for each batch. 430 int curPos = 0; 431 int lastPos = 0; 432 // For the first batch mLastEvents will be empty, so no iterations will happen. 433 while (lastPos < mLastEvents.size() && curPos < newLogs.size()) { 434 final SecurityEvent curEvent = newLogs.get(curPos); 435 final long currentNanos = curEvent.getTimeNanos(); 436 if (currentNanos > mLastEventNanos) { 437 // We got past the last event of the last batch, no overlap possible anymore. 438 break; 439 } 440 final SecurityEvent lastEvent = mLastEvents.get(lastPos); 441 final long lastNanos = lastEvent.getTimeNanos(); 442 if (lastNanos > currentNanos) { 443 // New event older than the last we've seen so far, must be due to reordering. 444 if (DEBUG) Slog.d(TAG, "New event in the overlap: " + currentNanos); 445 dedupedLogs.add(curEvent); 446 curPos++; 447 } else if (lastNanos < currentNanos) { 448 if (DEBUG) Slog.d(TAG, "Event disappeared from the overlap: " + lastNanos); 449 lastPos++; 450 } else { 451 // Two events have the same timestamp, check if they are the same. 452 if (lastEvent.eventEquals(curEvent)) { 453 // Actual overlap, just skip the event. 454 if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos); 455 } else { 456 // Wow, what a coincidence, or probably the clock is too coarse. 457 dedupedLogs.add(curEvent); 458 if (DEBUG) Slog.d(TAG, "Event timestamp collision: " + lastNanos); 459 } 460 lastPos++; 461 curPos++; 462 } 463 } 464 // Assign an id to the new logs, after the overlap with mLastEvents. 465 dedupedLogs.addAll(newLogs.subList(curPos, newLogs.size())); 466 for (SecurityEvent event : dedupedLogs) { 467 assignLogId(event); 468 } 469 470 if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) { 471 addToLegacyBufferLocked(dedupedLogs); 472 } 473 474 if (Flags.securityLogV2Enabled() && mAuditLogEnabled) { 475 addAuditLogEventsLocked(dedupedLogs); 476 } 477 } 478 479 @GuardedBy("mLock") addToLegacyBufferLocked(List<SecurityEvent> dedupedLogs)480 private void addToLegacyBufferLocked(List<SecurityEvent> dedupedLogs) { 481 // Save the rest of the new batch. 482 mPendingLogs.addAll(dedupedLogs); 483 484 checkCriticalLevel(); 485 486 if (mPendingLogs.size() > BUFFER_ENTRIES_MAXIMUM_LEVEL) { 487 // Truncate buffer down to half of BUFFER_ENTRIES_MAXIMUM_LEVEL. 488 mPendingLogs = new ArrayList<>(mPendingLogs.subList( 489 mPendingLogs.size() - (BUFFER_ENTRIES_MAXIMUM_LEVEL / 2), 490 mPendingLogs.size())); 491 mCriticalLevelLogged = false; 492 Slog.i(TAG, "Pending logs buffer full. Discarding old logs."); 493 } 494 if (DEBUG) { 495 if (mPendingLogs.size() > 0) { 496 Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging," 497 + " with ids " + mPendingLogs.get(0).getId() 498 + " to " + mPendingLogs.get(mPendingLogs.size() - 1).getId()); 499 } else { 500 Slog.d(TAG, "0 pending events in the buffer after merging"); 501 } 502 } 503 } 504 505 @GuardedBy("mLock") checkCriticalLevel()506 private void checkCriticalLevel() { 507 if (!SecurityLog.isLoggingEnabled()) { 508 return; 509 } 510 511 if (mPendingLogs.size() >= BUFFER_ENTRIES_CRITICAL_LEVEL) { 512 if (!mCriticalLevelLogged) { 513 mCriticalLevelLogged = true; 514 SecurityLog.writeEvent(SecurityLog.TAG_LOG_BUFFER_SIZE_CRITICAL); 515 } 516 } 517 } 518 519 @GuardedBy("mLock") assignLogId(SecurityEvent event)520 private void assignLogId(SecurityEvent event) { 521 event.setId(mId); 522 if (mId == Long.MAX_VALUE) { 523 Slog.i(TAG, "Reached maximum id value; wrapping around."); 524 mId = 0; 525 } else { 526 mId++; 527 } 528 } 529 530 @Override run()531 public void run() { 532 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 533 534 final ArrayList<SecurityEvent> newLogs = new ArrayList<>(); 535 while (!Thread.currentThread().isInterrupted()) { 536 try { 537 final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS); 538 539 getNextBatch(newLogs); 540 541 mLock.lockInterruptibly(); 542 try { 543 mergeBatchLocked(newLogs); 544 } finally { 545 mLock.unlock(); 546 } 547 548 saveLastEvents(newLogs); 549 newLogs.clear(); 550 551 if (!Flags.securityLogV2Enabled() || mLegacyLogEnabled) { 552 notifyDeviceOwnerOrProfileOwnerIfNeeded(force); 553 } 554 } catch (IOException e) { 555 Log.e(TAG, "Failed to read security log", e); 556 } catch (InterruptedException e) { 557 Log.i(TAG, "Thread interrupted, exiting.", e); 558 // We are asked to stop. 559 break; 560 } 561 } 562 563 // Discard previous batch info. 564 mLastEvents.clear(); 565 if (mLastEventNanos != -1) { 566 // Make sure we don't read old events if logging is re-enabled. Since mLastEvents is 567 // empty, the next request will be done without overlap, so it is enough to add 1 ns. 568 mLastEventNanos += 1; 569 } 570 571 Slog.i(TAG, "MonitorThread exit."); 572 } 573 notifyDeviceOwnerOrProfileOwnerIfNeeded(boolean force)574 private void notifyDeviceOwnerOrProfileOwnerIfNeeded(boolean force) 575 throws InterruptedException { 576 boolean allowRetrievalAndNotifyDOOrPO = false; 577 mLock.lockInterruptibly(); 578 try { 579 if (mPaused) { 580 return; 581 } 582 final int logSize = mPendingLogs.size(); 583 if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL || (force && logSize > 0)) { 584 // Allow DO to retrieve logs if too many pending logs or if forced. 585 if (!mAllowedToRetrieve) { 586 allowRetrievalAndNotifyDOOrPO = true; 587 } 588 if (DEBUG) Slog.d(TAG, "Number of log entries over threshold: " + logSize); 589 } 590 if (logSize > 0 && SystemClock.elapsedRealtime() >= mNextAllowedRetrievalTimeMillis) { 591 // Rate limit reset 592 allowRetrievalAndNotifyDOOrPO = true; 593 if (DEBUG) Slog.d(TAG, "Timeout reached"); 594 } 595 if (allowRetrievalAndNotifyDOOrPO) { 596 mAllowedToRetrieve = true; 597 // Set the timeout to retry the notification if the DO misses it. 598 mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime() 599 + BROADCAST_RETRY_INTERVAL_MS; 600 } 601 } finally { 602 mLock.unlock(); 603 } 604 if (allowRetrievalAndNotifyDOOrPO) { 605 Slog.i(TAG, "notify DO or PO"); 606 mService.sendDeviceOwnerOrProfileOwnerCommand( 607 DeviceAdminReceiver.ACTION_SECURITY_LOGS_AVAILABLE, null, mEnabledUser); 608 } 609 } 610 611 /** 612 * Forces the logs to be fetched and made available. Returns 0 on success or timeout to wait 613 * before attempting in milliseconds. 614 */ forceLogs()615 public long forceLogs() { 616 final long nowNanos = System.nanoTime(); 617 // We only synchronize with another calls to this function, not with the fetching thread. 618 synchronized (mForceSemaphore) { 619 final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos; 620 if (toWaitNanos > 0) { 621 return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up. 622 } 623 mLastForceNanos = nowNanos; 624 // There is a race condition with the fetching thread below, but if the last permit is 625 // acquired just after we do the check, logs are forced anyway and that's what we need. 626 if (mForceSemaphore.availablePermits() == 0) { 627 mForceSemaphore.release(); 628 } 629 return 0; 630 } 631 } 632 setAuditLogEventsCallback(int uid, IAuditLogEventsCallback callback)633 public void setAuditLogEventsCallback(int uid, IAuditLogEventsCallback callback) { 634 mLock.lock(); 635 try { 636 if (callback == null) { 637 mAuditLogCallbacks.remove(uid); 638 Slogf.i(TAG, "Cleared audit log callback for UID %d", uid); 639 return; 640 } 641 // Create a copy while holding the lock, so that that new events are not added 642 // resulting in duplicates. 643 final List<SecurityEvent> events = new ArrayList<>(mAuditLogEventBuffer); 644 scheduleSendAuditLogs(uid, callback, events); 645 mAuditLogCallbacks.append(uid, callback); 646 } finally { 647 mLock.unlock(); 648 } 649 Slogf.i(TAG, "Set audit log callback for UID %d", uid); 650 } 651 652 @GuardedBy("mLock") addAuditLogEventsLocked(List<SecurityEvent> events)653 private void addAuditLogEventsLocked(List<SecurityEvent> events) { 654 if (mPaused) { 655 // TODO: maybe we need to stash the logs in some temp buffer wile paused so that 656 // they can be accessed after affiliation is fixed. 657 return; 658 } 659 if (!events.isEmpty()) { 660 for (int i = 0; i < mAuditLogCallbacks.size(); i++) { 661 final int uid = mAuditLogCallbacks.keyAt(i); 662 scheduleSendAuditLogs(uid, mAuditLogCallbacks.valueAt(i), events); 663 } 664 } 665 if (DEBUG) { 666 Slogf.d(TAG, "Adding audit %d events to % already present in the buffer", 667 events.size(), mAuditLogEventBuffer.size()); 668 } 669 mAuditLogEventBuffer.addAll(events); 670 trimAuditLogBufferLocked(); 671 if (DEBUG) { 672 Slogf.d(TAG, "Audit event buffer size after trimming: %d", 673 mAuditLogEventBuffer.size()); 674 } 675 } 676 677 @GuardedBy("mLock") trimAuditLogBufferLocked()678 private void trimAuditLogBufferLocked() { 679 long nowNanos = TimeUnit.MILLISECONDS.toNanos(System.currentTimeMillis()); 680 681 final Iterator<SecurityEvent> iterator = mAuditLogEventBuffer.iterator(); 682 while (iterator.hasNext()) { 683 final SecurityEvent event = iterator.next(); 684 if (mAuditLogEventBuffer.size() <= MAX_AUDIT_LOG_EVENTS 685 && nowNanos - event.getTimeNanos() <= MAX_AUDIT_LOG_EVENT_AGE_NS) { 686 break; 687 } 688 689 iterator.remove(); 690 } 691 } 692 scheduleSendAuditLogs( int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events)693 private void scheduleSendAuditLogs( 694 int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events) { 695 if (DEBUG) { 696 Slogf.d(TAG, "Scheduling to send %d audit log events to UID %d", events.size(), uid); 697 } 698 mHandler.post(() -> sendAuditLogs(uid, callback, events)); 699 } 700 sendAuditLogs( int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events)701 private void sendAuditLogs( 702 int uid, IAuditLogEventsCallback callback, List<SecurityEvent> events) { 703 try { 704 final int size = events.size(); 705 if (DEBUG) { 706 Slogf.d(TAG, "Sending %d audit log events to UID %d", size, uid); 707 } 708 callback.onNewAuditLogEvents(events); 709 if (DEBUG) { 710 Slogf.d(TAG, "Sent %d audit log events to UID %d", size, uid); 711 } 712 } catch (RemoteException e) { 713 Slogf.e(TAG, e, "Failed to invoke audit log callback for UID %d", uid); 714 removeAuditLogEventsCallbackIfDead(uid, callback); 715 } 716 } 717 removeAuditLogEventsCallbackIfDead(int uid, IAuditLogEventsCallback callback)718 private void removeAuditLogEventsCallbackIfDead(int uid, IAuditLogEventsCallback callback) { 719 final IBinder binder = callback.asBinder(); 720 if (binder.isBinderAlive()) { 721 Slog.i(TAG, "Callback binder is still alive, not removing."); 722 return; 723 } 724 725 mLock.lock(); 726 try { 727 int index = mAuditLogCallbacks.indexOfKey(uid); 728 if (index < 0) { 729 Slogf.i(TAG, "Callback not registered for UID %d, nothing to remove", uid); 730 return; 731 } 732 733 final IBinder storedBinder = mAuditLogCallbacks.valueAt(index).asBinder(); 734 if (!storedBinder.equals(binder)) { 735 Slogf.i(TAG, "Callback is already replaced for UID %d, not removing", uid); 736 return; 737 } 738 739 Slogf.i(TAG, "Removing callback for UID %d", uid); 740 mAuditLogCallbacks.removeAt(index); 741 } finally { 742 mLock.unlock(); 743 } 744 } 745 } 746