• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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