summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmds/dpm/src/com/android/commands/dpm/Dpm.java24
-rw-r--r--core/java/android/app/admin/DevicePolicyManager.java17
-rw-r--r--core/java/android/app/admin/IDevicePolicyManager.aidl1
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java5
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java9
-rw-r--r--services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java70
6 files changed, 107 insertions, 19 deletions
diff --git a/cmds/dpm/src/com/android/commands/dpm/Dpm.java b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
index 47581e10e937..7c1a5557a1e9 100644
--- a/cmds/dpm/src/com/android/commands/dpm/Dpm.java
+++ b/cmds/dpm/src/com/android/commands/dpm/Dpm.java
@@ -46,6 +46,7 @@ public final class Dpm extends BaseCommand {
private static final String COMMAND_SET_PROFILE_OWNER = "set-profile-owner";
private static final String COMMAND_REMOVE_ACTIVE_ADMIN = "remove-active-admin";
private static final String COMMAND_CLEAR_FREEZE_PERIOD_RECORD = "clear-freeze-period-record";
+ private static final String COMMAND_FORCE_SECURITY_LOGS = "force-security-logs";
private IDevicePolicyManager mDevicePolicyManager;
private int mUserId = UserHandle.USER_SYSTEM;
@@ -76,11 +77,15 @@ public final class Dpm extends BaseCommand {
"\n" +
"dpm remove-active-admin: Disables an active admin, the admin must have declared" +
" android:testOnly in the application in its manifest. This will also remove" +
- " device and profile owners\n" +
+ " device and profile owners.\n" +
"\n" +
"dpm " + COMMAND_CLEAR_FREEZE_PERIOD_RECORD + ": clears framework-maintained " +
"record of past freeze periods that the device went through. For use during " +
- "feature development to prevent triggering restriction on setting freeze periods");
+ "feature development to prevent triggering restriction on setting freeze " +
+ "periods.\n" +
+ "\n" +
+ "dpm " + COMMAND_FORCE_SECURITY_LOGS + ": makes all security logs available to " +
+ "the DPC and triggers DeviceAdminReceiver.onSecurityLogsAvailable() if needed.");
}
@Override
@@ -109,11 +114,26 @@ public final class Dpm extends BaseCommand {
case COMMAND_CLEAR_FREEZE_PERIOD_RECORD:
runClearFreezePeriodRecord();
break;
+ case COMMAND_FORCE_SECURITY_LOGS:
+ runForceSecurityLogs();
+ break;
default:
throw new IllegalArgumentException ("unknown command '" + command + "'");
}
}
+ private void runForceSecurityLogs() throws RemoteException, InterruptedException {
+ while (true) {
+ final long toWait = mDevicePolicyManager.forceSecurityLogs();
+ if (toWait == 0) {
+ break;
+ }
+ System.out.println("We have to wait for " + toWait + " milliseconds...");
+ Thread.sleep(toWait);
+ }
+ System.out.println("Success");
+ }
+
private void parseArgs(boolean canHaveName) {
String opt;
while ((opt = nextOption()) != null) {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index e190fd4cf6cd..5d37e5f94dc0 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -7682,6 +7682,7 @@ public class DevicePolicyManager {
throw re.rethrowFromSystemServer();
}
}
+
/**
* Called by a device owner or profile owner of secondary users that is affiliated with the
* device to disable the keyguard altogether.
@@ -8320,6 +8321,22 @@ public class DevicePolicyManager {
}
/**
+ * Forces a batch of security logs to be fetched from logd and makes it available for DPC.
+ * Only callable by ADB. If throttled, returns time to wait in milliseconds, otherwise 0.
+ * @hide
+ */
+ public long forceSecurityLogs() {
+ if (mService == null) {
+ return 0;
+ }
+ try {
+ return mService.forceSecurityLogs();
+ } catch (RemoteException re) {
+ throw re.rethrowFromSystemServer();
+ }
+ }
+
+ /**
* Called by the system to obtain a {@link DevicePolicyManager} whose calls act on the parent
* profile.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 5197de4cef9f..ef990071dbfd 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -349,6 +349,7 @@ interface IDevicePolicyManager {
boolean isSecurityLoggingEnabled(in ComponentName admin);
ParceledListSlice retrieveSecurityLogs(in ComponentName admin);
ParceledListSlice retrievePreRebootSecurityLogs(in ComponentName admin);
+ long forceSecurityLogs();
boolean isUninstallInQueue(String packageName);
void uninstallPackageWithActiveAdmins(String packageName);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
index 9fcf3eedd983..c4e485c1523a 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/BaseIDevicePolicyManager.java
@@ -177,4 +177,9 @@ abstract class BaseIDevicePolicyManager extends IDevicePolicyManager.Stub {
String packageName, int userId) {
return false;
}
+
+ @Override
+ public long forceSecurityLogs() {
+ return 0;
+ }
}
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
index 4c57f7f050d0..4afe1c784701 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/DevicePolicyManagerService.java
@@ -11659,6 +11659,15 @@ public class DevicePolicyManagerService extends BaseIDevicePolicyManager {
return logs != null ? new ParceledListSlice<SecurityEvent>(logs) : null;
}
+ @Override
+ public long forceSecurityLogs() {
+ enforceShell("forceSecurityLogs");
+ if (!mInjector.securityLogGetLoggingEnabledProperty()) {
+ throw new IllegalStateException("logging is not available");
+ }
+ return mSecurityLogMonitor.forceLogs();
+ }
+
private void enforceCanManageDeviceAdmin() {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_DEVICE_ADMINS,
null);
diff --git a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
index 3277adf479ed..fb34913a5606 100644
--- a/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
+++ b/services/devicepolicy/java/com/android/server/devicepolicy/SecurityLogMonitor.java
@@ -16,6 +16,9 @@
package com.android.server.devicepolicy;
+import static java.util.concurrent.TimeUnit.MILLISECONDS;
+import static java.util.concurrent.TimeUnit.NANOSECONDS;
+
import android.app.admin.DeviceAdminReceiver;
import android.app.admin.SecurityLog;
import android.app.admin.SecurityLog.SecurityEvent;
@@ -30,6 +33,7 @@ import com.android.internal.annotations.VisibleForTesting;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -55,6 +59,7 @@ class SecurityLogMonitor implements Runnable {
SecurityLogMonitor(DevicePolicyManagerService service, long id) {
this.mService = service;
this.mId = id;
+ this.mLastForceNanos = System.nanoTime();
}
private static final boolean DEBUG = false; // STOPSHIP if true.
@@ -77,20 +82,22 @@ class SecurityLogMonitor implements Runnable {
/**
* How often should Device Owner be notified under normal circumstances.
*/
- private static final long RATE_LIMIT_INTERVAL_MILLISECONDS = TimeUnit.HOURS.toMillis(2);
+ private static final long RATE_LIMIT_INTERVAL_MS = TimeUnit.HOURS.toMillis(2);
/**
* How often to retry the notification about available logs if it is ignored or missed by DO.
*/
- private static final long BROADCAST_RETRY_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(30);
+ private static final long BROADCAST_RETRY_INTERVAL_MS = TimeUnit.MINUTES.toMillis(30);
/**
* Internally how often should the monitor poll the security logs from logd.
*/
- private static final long POLLING_INTERVAL_MILLISECONDS = TimeUnit.MINUTES.toMillis(1);
+ private static final long POLLING_INTERVAL_MS = TimeUnit.MINUTES.toMillis(1);
/**
* Overlap between two subsequent log requests, required to avoid losing out of order events.
*/
- private static final long OVERLAP_NANOS = TimeUnit.SECONDS.toNanos(3);
+ private static final long OVERLAP_NS = TimeUnit.SECONDS.toNanos(3);
+ /** Minimum time between forced fetch attempts. */
+ private static final long FORCE_FETCH_THROTTLE_NS = TimeUnit.SECONDS.toNanos(10);
@GuardedBy("mLock")
private Thread mMonitorThread = null;
@@ -122,6 +129,13 @@ class SecurityLogMonitor implements Runnable {
@GuardedBy("mLock")
private boolean mPaused = false;
+ /** Semaphore used to force log fetching on request from adb. */
+ private final Semaphore mForceSemaphore = new Semaphore(0 /* permits */);
+
+ /** The last timestamp when force fetch was used, to prevent abuse. */
+ @GuardedBy("mForceSemaphore")
+ private long mLastForceNanos = 0;
+
void start() {
Slog.i(TAG, "Starting security logging.");
SecurityLog.writeEvent(SecurityLog.TAG_LOGGING_STARTED);
@@ -203,7 +217,7 @@ class SecurityLogMonitor implements Runnable {
Slog.i(TAG, "Resumed.");
try {
- notifyDeviceOwnerIfNeeded();
+ notifyDeviceOwnerIfNeeded(false /* force */);
} catch (InterruptedException e) {
Log.w(TAG, "Thread interrupted.", e);
}
@@ -231,7 +245,7 @@ class SecurityLogMonitor implements Runnable {
if (mAllowedToRetrieve) {
mAllowedToRetrieve = false;
mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
- + RATE_LIMIT_INTERVAL_MILLISECONDS;
+ + RATE_LIMIT_INTERVAL_MS;
List<SecurityEvent> result = mPendingLogs;
mPendingLogs = new ArrayList<>();
mCriticalLevelLogged = false;
@@ -247,8 +261,7 @@ class SecurityLogMonitor implements Runnable {
/**
* Requests the next (or the first) batch of events from the log with appropriate timestamp.
*/
- private void getNextBatch(ArrayList<SecurityEvent> newLogs)
- throws IOException, InterruptedException {
+ private void getNextBatch(ArrayList<SecurityEvent> newLogs) throws IOException {
if (mLastEventNanos < 0) {
// Non-blocking read that returns all logs immediately.
if (DEBUG) Slog.d(TAG, "SecurityLog.readEvents");
@@ -257,7 +270,7 @@ class SecurityLogMonitor implements Runnable {
// If we have last events from the previous batch, request log events with time overlap
// with previously retrieved messages to avoid losing events due to reordering in logd.
final long startNanos = mLastEvents.isEmpty()
- ? mLastEventNanos : Math.max(0, mLastEventNanos - OVERLAP_NANOS);
+ ? mLastEventNanos : Math.max(0, mLastEventNanos - OVERLAP_NS);
if (DEBUG) Slog.d(TAG, "SecurityLog.readEventsSince: " + startNanos);
// Non-blocking read that returns all logs with timestamps >= startNanos immediately.
SecurityLog.readEventsSince(startNanos, newLogs);
@@ -293,7 +306,7 @@ class SecurityLogMonitor implements Runnable {
// Position of the earliest event that has to be saved. Start from the penultimate event,
// going backward.
int pos = newLogs.size() - 2;
- while (pos >= 0 && mLastEventNanos - newLogs.get(pos).getTimeNanos() < OVERLAP_NANOS) {
+ while (pos >= 0 && mLastEventNanos - newLogs.get(pos).getTimeNanos() < OVERLAP_NS) {
pos--;
}
// We either run past the start of the list or encountered an event that is too old to keep.
@@ -401,10 +414,11 @@ class SecurityLogMonitor implements Runnable {
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- ArrayList<SecurityEvent> newLogs = new ArrayList<>();
+ final ArrayList<SecurityEvent> newLogs = new ArrayList<>();
while (!Thread.currentThread().isInterrupted()) {
try {
- Thread.sleep(POLLING_INTERVAL_MILLISECONDS);
+ final boolean force = mForceSemaphore.tryAcquire(POLLING_INTERVAL_MS, MILLISECONDS);
+
getNextBatch(newLogs);
mLock.lockInterruptibly();
@@ -416,7 +430,7 @@ class SecurityLogMonitor implements Runnable {
saveLastEvents(newLogs);
newLogs.clear();
- notifyDeviceOwnerIfNeeded();
+ notifyDeviceOwnerIfNeeded(force);
} catch (IOException e) {
Log.e(TAG, "Failed to read security log", e);
} catch (InterruptedException e) {
@@ -437,7 +451,7 @@ class SecurityLogMonitor implements Runnable {
Slog.i(TAG, "MonitorThread exit.");
}
- private void notifyDeviceOwnerIfNeeded() throws InterruptedException {
+ private void notifyDeviceOwnerIfNeeded(boolean force) throws InterruptedException {
boolean allowRetrievalAndNotifyDO = false;
mLock.lockInterruptibly();
try {
@@ -445,8 +459,8 @@ class SecurityLogMonitor implements Runnable {
return;
}
final int logSize = mPendingLogs.size();
- if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL) {
- // Allow DO to retrieve logs if too many pending logs
+ if (logSize >= BUFFER_ENTRIES_NOTIFICATION_LEVEL || (force && logSize > 0)) {
+ // Allow DO to retrieve logs if too many pending logs or if forced.
if (!mAllowedToRetrieve) {
allowRetrievalAndNotifyDO = true;
}
@@ -461,7 +475,7 @@ class SecurityLogMonitor implements Runnable {
mAllowedToRetrieve = true;
// Set the timeout to retry the notification if the DO misses it.
mNextAllowedRetrievalTimeMillis = SystemClock.elapsedRealtime()
- + BROADCAST_RETRY_INTERVAL_MILLISECONDS;
+ + BROADCAST_RETRY_INTERVAL_MS;
}
} finally {
mLock.unlock();
@@ -472,4 +486,26 @@ class SecurityLogMonitor implements Runnable {
null);
}
}
+
+ /**
+ * Forces the logs to be fetched and made available. Returns 0 on success or timeout to wait
+ * before attempting in milliseconds.
+ */
+ public long forceLogs() {
+ final long nowNanos = System.nanoTime();
+ // We only synchronize with another calls to this function, not with the fetching thread.
+ synchronized (mForceSemaphore) {
+ final long toWaitNanos = mLastForceNanos + FORCE_FETCH_THROTTLE_NS - nowNanos;
+ if (toWaitNanos > 0) {
+ return NANOSECONDS.toMillis(toWaitNanos) + 1; // Round up.
+ }
+ mLastForceNanos = nowNanos;
+ // There is a race condition with the fetching thread below, but if the last permit is
+ // acquired just after we do the check, logs are forced anyway and that's what we need.
+ if (mForceSemaphore.availablePermits() == 0) {
+ mForceSemaphore.release();
+ }
+ return 0;
+ }
+ }
}