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