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