1c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu/*
2c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * Copyright (C) 2016 The Android Open Source Project
3c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu *
4c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * Licensed under the Apache License, Version 2.0 (the "License");
5c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * you may not use this file except in compliance with the License.
6c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * You may obtain a copy of the License at
7c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu *
8c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu *      http://www.apache.org/licenses/LICENSE-2.0
9c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu *
10c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * Unless required by applicable law or agreed to in writing, software
11c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * distributed under the License is distributed on an "AS IS" BASIS,
12c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * See the License for the specific language governing permissions and
14c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * limitations under the License.
15c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu */
16c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
17c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xupackage com.android.server.devicepolicy;
18c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
19c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport android.app.admin.DeviceAdminReceiver;
206235a94ffaed1d82cee2317481c18776f601da1bMichal Karpinskiimport android.app.admin.SecurityLog;
216235a94ffaed1d82cee2317481c18776f601da1bMichal Karpinskiimport android.app.admin.SecurityLog.SecurityEvent;
22d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talaveraimport android.os.SystemClock;
23c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport android.util.Log;
24c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport android.util.Slog;
25c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
26c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport com.android.internal.annotations.GuardedBy;
27c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
28c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport java.io.IOException;
29c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport java.util.ArrayList;
30c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport java.util.List;
31c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport java.util.concurrent.TimeUnit;
32a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinskiimport java.util.concurrent.locks.Lock;
33a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinskiimport java.util.concurrent.locks.ReentrantLock;
34c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
35c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuimport android.os.Process;
36c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
37c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu/**
38c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * A class managing access to the security logs. It maintains an internal buffer of pending
39c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * logs to be retrieved by the device owner. The logs are retrieved from the logd daemon via
40c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * JNI binding, and kept until device owner has retrieved to prevent loss of logs. Access to
41c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * the logs from the device owner is rate-limited, and device owner is notified when the logs
42c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * are ready to be retrieved. This happens every two hours, or when our internal buffer is
43c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu * larger than a certain threshold.
44c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu */
45c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xuclass SecurityLogMonitor implements Runnable {
46c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private final DevicePolicyManagerService mService;
47c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
48a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    private final Lock mLock = new ReentrantLock();
49a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski
50c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    SecurityLogMonitor(DevicePolicyManagerService service) {
51c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        mService = service;
52c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    }
53c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
54d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    private static final boolean DEBUG = false;  // STOPSHIP if true.
55c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private static final String TAG = "SecurityLogMonitor";
56c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    /**
57c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * Each log entry can hold up to 4K bytes (but as of {@link android.os.Build.VERSION_CODES#N}
58c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * it should be less than 100 bytes), setting 1024 entries as the threshold to notify Device
59c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * Owner.
60c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     */
61c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private static final int BUFFER_ENTRIES_NOTIFICATION_LEVEL = 1024;
62c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    /**
63c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * The maximum number of entries we should store before dropping earlier logs, to limit the
64c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * memory usage.
65c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     */
66c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private static final int BUFFER_ENTRIES_MAXIMUM_LEVEL = BUFFER_ENTRIES_NOTIFICATION_LEVEL * 10;
67c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    /**
68c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * How often should Device Owner be notified under normal circumstances.
69c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     */
70c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private static final long RATE_LIMIT_INTERVAL_MILLISECONDS = TimeUnit.HOURS.toMillis(2);
71c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    /**
729cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov     * How often to retry the notification about available logs if it is ignored or missed by DO.
739cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov     */
749cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov    private static final long BROADCAST_RETRY_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(30);
759cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov    /**
76c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * Internally how often should the monitor poll the security logs from logd.
77c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     */
78c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private static final long POLLING_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(1);
794ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    /**
804ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * Overlap between two subsequent log requests, required to avoid losing out of order events.
814ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     */
824ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private static final long OVERLAP_NANOS = TimeUnit.SECONDS.toNanos(3);
834ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
84c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
85a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    @GuardedBy("mLock")
86c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private Thread mMonitorThread = null;
87a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    @GuardedBy("mLock")
884ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private ArrayList<SecurityEvent> mPendingLogs = new ArrayList<>();
89a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    @GuardedBy("mLock")
90c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    private boolean mAllowedToRetrieve = false;
91d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera
92d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    /**
934ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * Last events fetched from log to check for overlap between batches. We can leave it empty if
944ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * we are sure there will be no overlap anymore, e.g. when we get empty batch.
954ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     */
964ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private final ArrayList<SecurityEvent> mLastEvents = new ArrayList<>();
974ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    /** Timestamp of the very last event, -1 means request from the beginning of time. */
984ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private long mLastEventNanos = -1;
994ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
1004ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    /**
101d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * When DO will be allowed to retrieve the log, in milliseconds since boot (as per
1029cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov     * {@link SystemClock#elapsedRealtime()}). After that it will mark the time to retry broadcast.
103d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     */
104d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    @GuardedBy("mLock")
105d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    private long mNextAllowedRetrievalTimeMillis = -1;
106a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    @GuardedBy("mLock")
107d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    private boolean mPaused = false;
108c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
109a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    void start() {
110d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        Slog.i(TAG, "Starting security logging.");
111a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        mLock.lock();
112a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        try {
113a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            if (mMonitorThread == null) {
1144ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                mPendingLogs = new ArrayList<>();
115a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mAllowedToRetrieve = false;
116d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                mNextAllowedRetrievalTimeMillis = -1;
117d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                mPaused = false;
118c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
119a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mMonitorThread = new Thread(this);
120a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mMonitorThread.start();
121a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            }
122a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        } finally {
123a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            mLock.unlock();
124c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        }
125c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    }
126c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
127a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    void stop() {
128d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        Slog.i(TAG, "Stopping security logging.");
129a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        mLock.lock();
130a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        try {
131a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            if (mMonitorThread != null) {
132a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mMonitorThread.interrupt();
133a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                try {
134a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                    mMonitorThread.join(TimeUnit.SECONDS.toMillis(5));
135a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                } catch (InterruptedException e) {
136a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                    Log.e(TAG, "Interrupted while waiting for thread to stop", e);
137a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                }
138a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                // Reset state and clear buffer
1394ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                mPendingLogs = new ArrayList<>();
140a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mAllowedToRetrieve = false;
141d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                mNextAllowedRetrievalTimeMillis = -1;
142d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                mPaused = false;
143a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mMonitorThread = null;
144c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            }
145a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        } finally {
146a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            mLock.unlock();
147c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        }
148c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    }
149c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
150c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    /**
151d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * If logs are being collected, keep collecting them but stop notifying the device owner that
152d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * new logs are available (since they cannot be retrieved).
153d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     */
154d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    void pause() {
155d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        Slog.i(TAG, "Paused.");
156d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera
157d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mLock.lock();
158d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mPaused = true;
159d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mAllowedToRetrieve = false;
160d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mLock.unlock();
161d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    }
162d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera
163d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    /**
164d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * If logs are being collected, start notifying the device owner when logs are ready to be
165d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * retrieved again (if it was paused).
166d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * <p>If logging is enabled and there are logs ready to be retrieved, this method will attempt
167d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * to notify the device owner. Therefore calling identity should be cleared before calling it
168d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * (in case the method is called from a user other than the DO's user).
169d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     */
170d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    void resume() {
171d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mLock.lock();
172d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        try {
173d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            if (!mPaused) {
174d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                Log.d(TAG, "Attempted to resume, but logging is not paused.");
175d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                return;
176d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            }
177d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            mPaused = false;
178d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            mAllowedToRetrieve = false;
179d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        } finally {
180d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            mLock.unlock();
181d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        }
182d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera
183d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        Slog.i(TAG, "Resumed.");
184d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        try {
185d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            notifyDeviceOwnerIfNeeded();
186d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        } catch (InterruptedException e) {
187d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            Log.w(TAG, "Thread interrupted.", e);
188d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        }
189d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    }
190d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera
191d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    /**
192d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     * Discard all collected logs.
193d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera     */
194d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    void discardLogs() {
195d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mLock.lock();
196d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mAllowedToRetrieve = false;
1974ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mPendingLogs = new ArrayList<>();
198d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        mLock.unlock();
199d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        Slog.i(TAG, "Discarded all logs.");
200d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    }
201d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera
202d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera    /**
203c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * Returns the new batch of logs since the last call to this method. Returns null if
204c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     * rate limit is exceeded.
205c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu     */
206a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    List<SecurityEvent> retrieveLogs() {
207a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        mLock.lock();
208a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        try {
209a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            if (mAllowedToRetrieve) {
210a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                mAllowedToRetrieve = false;
211d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
212a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                        + RATE_LIMIT_INTERVAL_MILLISECONDS;
213a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                List<SecurityEvent> result = mPendingLogs;
2144ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                mPendingLogs = new ArrayList<>();
215a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                return result;
216a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            } else {
217a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski                return null;
218a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            }
219a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        } finally {
220a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            mLock.unlock();
221c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        }
222c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    }
223c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
2244ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    /**
2254ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * Requests the next (or the first) batch of events from the log with appropriate timestamp.
2264ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     */
2274ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private void getNextBatch(ArrayList<SecurityEvent> newLogs)
2284ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            throws IOException, InterruptedException {
2294ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (mLastEventNanos < 0) {
2304ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // Non-blocking read that returns all logs immediately.
2314ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            if (DEBUG) Slog.d(TAG, "SecurityLog.readEvents");
2324ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            SecurityLog.readEvents(newLogs);
2334ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        } else {
2344ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // If we have last events from the previous batch, request log events with time overlap
2354ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // with previously retrieved messages to avoid losing events due to reordering in logd.
2364ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            final long startNanos = mLastEvents.isEmpty()
2374ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    ? mLastEventNanos : Math.max(0, mLastEventNanos - OVERLAP_NANOS);
2384ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            if (DEBUG) Slog.d(TAG, "SecurityLog.readEventsSince: " + startNanos);
2394ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // Non-blocking read that returns all logs with timestamps >= startNanos immediately.
2404ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            SecurityLog.readEventsSince(startNanos, newLogs);
2414ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
2424ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
2434ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Sometimes events may be reordered in logd due to simultaneous readers and writers. In
2444ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // this case, we have to sort it to make overlap checking work. This is very unlikely.
2454ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        for (int i = 0; i < newLogs.size() - 1; i++) {
2464ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            if (newLogs.get(i).getTimeNanos() > newLogs.get(i+1).getTimeNanos()) {
2474ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                if (DEBUG) Slog.d(TAG, "Got out of order events, sorting.");
2484ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                // Sort using comparator that compares timestamps.
2494ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                newLogs.sort((e1, e2) -> Long.signum(e1.getTimeNanos() - e2.getTimeNanos()));
2504ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                break;
2514ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            }
2524ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
2534ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
2544ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (DEBUG) Slog.d(TAG, "Got " + newLogs.size() + " new events.");
2554ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    }
2564ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
2574ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    /**
2584ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * Save the last events for overlap checking with the next batch.
2594ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     */
2604ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private void saveLastEvents(ArrayList<SecurityEvent> newLogs) {
2614ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mLastEvents.clear();
2624ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (newLogs.isEmpty()) {
2634ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // This can happen if no events were logged yet or the buffer got cleared. In this case
2644ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // we aren't going to have any overlap next time, leave mLastEvents events empty.
2654ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            return;
2664ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
2674ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
2684ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Save the last timestamp.
2694ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mLastEventNanos = newLogs.get(newLogs.size() - 1).getTimeNanos();
2704ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Position of the earliest event that has to be saved. Start from the penultimate event,
2714ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // going backward.
2724ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        int pos = newLogs.size() - 2;
2734ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        while (pos >= 0 && mLastEventNanos - newLogs.get(pos).getTimeNanos() < OVERLAP_NANOS) {
2744ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            pos--;
2754ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
2764ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // We either run past the start of the list or encountered an event that is too old to keep.
2774ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        pos++;
2784ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mLastEvents.addAll(newLogs.subList(pos, newLogs.size()));
2794ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (DEBUG) Slog.d(TAG, mLastEvents.size() + " events saved for overlap check");
2804ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    }
2814ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
2824ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    /**
2834ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * Merges a new batch into already fetched logs and deals with overlapping and out of order
2844ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     * events.
2854ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov     */
2864ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    @GuardedBy("mLock")
2874ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    private void mergeBatchLocked(final ArrayList<SecurityEvent> newLogs) {
2884ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Reserve capacity so that copying doesn't occur.
2894ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mPendingLogs.ensureCapacity(mPendingLogs.size() + newLogs.size());
2904ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Run through the first events of the batch to check if there is an overlap with previous
2914ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // batch and if so, skip overlapping events. Events are sorted by timestamp, so we can
2924ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // compare it in linear time by advancing two pointers, one for each batch.
2934ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        int curPos = 0;
2944ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        int lastPos = 0;
2954ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // For the first batch mLastEvents will be empty, so no iterations will happen.
2964ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        while (lastPos < mLastEvents.size() && curPos < newLogs.size()) {
2974ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            final SecurityEvent curEvent = newLogs.get(curPos);
2984ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            final long currentNanos = curEvent.getTimeNanos();
2994ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            if (currentNanos > mLastEventNanos) {
3004ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                // We got past the last event of the last batch, no overlap possible anymore.
3014ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                break;
3024ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            }
3034ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            final SecurityEvent lastEvent = mLastEvents.get(lastPos);
3044ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            final long lastNanos = lastEvent.getTimeNanos();
3054ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            if (lastNanos > currentNanos) {
3064ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                // New event older than the last we've seen so far, must be due to reordering.
3074ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                if (DEBUG) Slog.d(TAG, "New event in the overlap: " + currentNanos);
3084ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                mPendingLogs.add(curEvent);
3094ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                curPos++;
3104ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            } else if (lastNanos < currentNanos) {
3114ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                if (DEBUG) Slog.d(TAG, "Event disappeared from the overlap: " + lastNanos);
3124ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                lastPos++;
3134ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            } else {
3144ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                // Two events have the same timestamp, check if they are the same.
3154ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                if (lastEvent.equals(curEvent)) {
3164ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    // Actual overlap, just skip the event.
3174ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    if (DEBUG) Slog.d(TAG, "Skipped dup event with timestamp: " + lastNanos);
3184ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                } else {
3194ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    // Wow, what a coincidence, or probably the clock is too coarse.
3204ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    mPendingLogs.add(curEvent);
3214ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    if (DEBUG) Slog.d(TAG, "Event timestamp collision: " + lastNanos);
3224ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                }
3234ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                lastPos++;
3244ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                curPos++;
3254ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            }
3264ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
3274ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Save the rest of the new batch.
3284ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mPendingLogs.addAll(newLogs.subList(curPos, newLogs.size()));
3294ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
3304ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (mPendingLogs.size() > BUFFER_ENTRIES_MAXIMUM_LEVEL) {
3314ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // Truncate buffer down to half of BUFFER_ENTRIES_MAXIMUM_LEVEL.
3324ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            mPendingLogs = new ArrayList<>(mPendingLogs.subList(
3334ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    mPendingLogs.size() - (BUFFER_ENTRIES_MAXIMUM_LEVEL / 2),
3344ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    mPendingLogs.size()));
3354ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            Slog.i(TAG, "Pending logs buffer full. Discarding old logs.");
3364ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
3374ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (DEBUG) Slog.d(TAG, mPendingLogs.size() + " pending events in the buffer after merging");
3384ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov    }
3394ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
340c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    @Override
341c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    public void run() {
342c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
343c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
3444ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        ArrayList<SecurityEvent> newLogs = new ArrayList<>();
345c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        while (!Thread.currentThread().isInterrupted()) {
346c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            try {
347c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                Thread.sleep(POLLING_INTERVAL_MILLISECONDS);
3484ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                getNextBatch(newLogs);
349c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
3504ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                mLock.lockInterruptibly();
3514ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                try {
3524ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    mergeBatchLocked(newLogs);
3534ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                } finally {
3544ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                    mLock.unlock();
355c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                }
3564ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
3574ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                saveLastEvents(newLogs);
3584ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov                newLogs.clear();
359c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                notifyDeviceOwnerIfNeeded();
360c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            } catch (IOException e) {
361c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                Log.e(TAG, "Failed to read security log", e);
362c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            } catch (InterruptedException e) {
363c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                Log.i(TAG, "Thread interrupted, exiting.", e);
364c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                // We are asked to stop.
365c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                break;
366c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            }
367c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        }
3684ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
3694ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        // Discard previous batch info.
3704ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        mLastEvents.clear();
3714ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        if (mLastEventNanos != -1) {
3724ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // Make sure we don't read old events if logging is re-enabled. Since mLastEvents is
3734ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            // empty, the next request will be done without overlap, so it is enough to add 1 ns.
3744ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov            mLastEventNanos += 1;
3754ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov        }
3764ce59d45a779cea662dbd56e61a98ba2a966b09bPavel Grafov
377d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera        Slog.i(TAG, "MonitorThread exit.");
378c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    }
379c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu
380a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski    private void notifyDeviceOwnerIfNeeded() throws InterruptedException {
3819cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov        boolean allowRetrievalAndNotifyDO = false;
382a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        mLock.lockInterruptibly();
383a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        try {
384d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            if (mPaused) {
385d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera                return;
386d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            }
3879cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov            final int logSize = mPendingLogs.size();
388c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) {
389c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                // Allow DO to retrieve logs if too many pending logs
3909cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                if (!mAllowedToRetrieve) {
3919cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                    allowRetrievalAndNotifyDO = true;
392c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                }
3939cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                if (DEBUG) Slog.d(TAG, "Number of log entries over threshold: " + logSize);
3949cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov            }
3959cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov            if (logSize > 0 && SystemClock.elapsedRealtime() >= mNextAllowedRetrievalTimeMillis) {
3969cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                // Rate limit reset
3979cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                allowRetrievalAndNotifyDO = true;
3989cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                if (DEBUG) Slog.d(TAG, "Timeout reached");
3999cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov            }
4009cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov            if (allowRetrievalAndNotifyDO) {
4019cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                mAllowedToRetrieve = true;
4029cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                // Set the timeout to retry the notification if the DO misses it.
4039cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
4049cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov                        + BROADCAST_RETRY_INTERVAL_MILLISECONDS;
405c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            }
406a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski        } finally {
407a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski            mLock.unlock();
408c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        }
4099cdba2722f86e101b82f9cbc61775ecffa8aac43Pavel Grafov        if (allowRetrievalAndNotifyDO) {
410d36dd15d9bf9f65270b9bee16d6419b96b18bd86Esteban Talavera            Slog.i(TAG, "notify DO");
411c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu            mService.sendDeviceOwnerCommand(DeviceAdminReceiver.ACTION_SECURITY_LOGS_AVAILABLE,
412c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu                    null);
413c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu        }
414c3cd05f8a45ab789aae1cb553df86f94667d595aRubin Xu    }
415a0ea967d111b9f8e01ebbb1ac8d393270e1788b6Michal Karpinski}
416