Home | History | Annotate | Download | only in devicepolicy
      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 
     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 
     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 
    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      */
    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
    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 
    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