Merge "Log app process start from ActivityManagerService"
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index ca33f9f..9949a97 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -1475,6 +1475,7 @@
final ServiceThread mHandlerThread;
final MainHandler mHandler;
final UiHandler mUiHandler;
+ final ProcessStartLogger mProcessStartLogger;
PackageManagerInternal mPackageManagerInt;
@@ -2452,6 +2453,8 @@
mHandler = new MainHandler(mHandlerThread.getLooper());
mUiHandler = new UiHandler();
+ mProcessStartLogger = new ProcessStartLogger();
+
mFgBroadcastQueue = new BroadcastQueue(this, mHandler,
"foreground", BROADCAST_FG_TIMEOUT, false);
mBgBroadcastQueue = new BroadcastQueue(this, mHandler,
@@ -3552,6 +3555,8 @@
app.processName, hostingType,
hostingNameStr != null ? hostingNameStr : "");
+ mProcessStartLogger.logIfNeededLocked(app, startResult);
+
if (app.persistent) {
Watchdog.getInstance().processStarted(app.processName, startResult.pid);
}
@@ -6634,6 +6639,8 @@
}
}, dumpheapFilter);
+ mProcessStartLogger.registerListener(mContext);
+
// Let system services know.
mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
diff --git a/services/core/java/com/android/server/am/ProcessStartLogger.java b/services/core/java/com/android/server/am/ProcessStartLogger.java
new file mode 100644
index 0000000..d2aa966
--- /dev/null
+++ b/services/core/java/com/android/server/am/ProcessStartLogger.java
@@ -0,0 +1,151 @@
+package com.android.server.am;
+
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_AM;
+import static com.android.server.am.ActivityManagerDebugConfig.TAG_WITH_CLASS_NAME;
+
+import android.app.AppGlobals;
+import android.auditing.SecurityLog;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.Process.ProcessStartResult;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+
+/**
+ * A class that logs process start information (including APK hash) to the security log.
+ */
+class ProcessStartLogger {
+ private static final String CLASS_NAME = "ProcessStartLogger";
+ private static final String TAG = TAG_WITH_CLASS_NAME ? CLASS_NAME : TAG_AM;
+
+ final HandlerThread mHandlerProcessLoggingThread;
+ Handler mHandlerProcessLogging;
+ // Should only access in mHandlerProcessLoggingThread
+ final HashMap<String, String> mProcessLoggingApkHashes;
+
+ ProcessStartLogger() {
+ mHandlerProcessLoggingThread = new HandlerThread(CLASS_NAME,
+ Process.THREAD_PRIORITY_BACKGROUND);
+ mProcessLoggingApkHashes = new HashMap();
+ }
+
+ void logIfNeededLocked(ProcessRecord app, ProcessStartResult startResult) {
+ if (!SecurityLog.isLoggingEnabled()) {
+ return;
+ }
+ if (!mHandlerProcessLoggingThread.isAlive()) {
+ mHandlerProcessLoggingThread.start();
+ mHandlerProcessLogging = new Handler(mHandlerProcessLoggingThread.getLooper());
+ }
+ mHandlerProcessLogging.post(new ProcessLoggingRunnable(app, startResult,
+ System.currentTimeMillis()));
+ }
+
+ void registerListener(Context context) {
+ IntentFilter packageChangedFilter = new IntentFilter();
+ packageChangedFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
+ packageChangedFilter.addAction(Intent.ACTION_PACKAGE_CHANGED);
+ context.registerReceiver(new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)
+ || Intent.ACTION_PACKAGE_ADDED.equals(intent.getAction())) {
+ int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
+ getSendingUserId());
+ String packageName = intent.getData().getSchemeSpecificPart();
+ try {
+ ApplicationInfo info = AppGlobals.getPackageManager().getApplicationInfo(
+ packageName, 0, userHandle);
+ invaildateCache(info.sourceDir);
+ } catch (RemoteException e) {
+ }
+ }
+ }
+ }, packageChangedFilter);
+ }
+
+ private void invaildateCache(final String apkPath) {
+ if (mHandlerProcessLogging != null) {
+ mHandlerProcessLogging.post(new Runnable() {
+ @Override
+ public void run() {
+ mProcessLoggingApkHashes.remove(apkPath);
+ }
+ });
+ }
+ }
+
+ private class ProcessLoggingRunnable implements Runnable {
+
+ private final ProcessRecord app;
+ private final Process.ProcessStartResult startResult;
+ private final long startTimestamp;
+
+ public ProcessLoggingRunnable(ProcessRecord app, Process.ProcessStartResult startResult,
+ long startTimestamp){
+ this.app = app;
+ this.startResult = startResult;
+ this.startTimestamp = startTimestamp;
+ }
+
+ @Override
+ public void run() {
+ String apkHash = computeStringHashOfApk(app);
+ SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START,
+ app.processName,
+ startTimestamp,
+ app.uid,
+ startResult.pid,
+ app.info.seinfo,
+ apkHash);
+ }
+
+ private String computeStringHashOfApk(ProcessRecord app){
+ final String apkFile = app.info.sourceDir;
+ if(apkFile == null) {
+ return "No APK";
+ }
+ String apkHash = mProcessLoggingApkHashes.get(apkFile);
+ if (apkHash == null) {
+ try {
+ byte[] hash = computeHashOfApkFile(apkFile);
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < hash.length; i++) {
+ sb.append(String.format("%02x", hash[i]));
+ }
+ apkHash = sb.toString();
+ mProcessLoggingApkHashes.put(apkFile, apkHash);
+ } catch (IOException | NoSuchAlgorithmException e) {
+ Slog.w(TAG, "computeStringHashOfApk() failed", e);
+ }
+ }
+ return apkHash != null ? apkHash : "Failed to count APK hash";
+ }
+
+ private byte[] computeHashOfApkFile(String packageArchiveLocation)
+ throws IOException, NoSuchAlgorithmException {
+ MessageDigest md = MessageDigest.getInstance("SHA-256");
+ FileInputStream input = new FileInputStream(new File(packageArchiveLocation));
+ byte[] buffer = new byte[65536];
+ int size;
+ while((size = input.read(buffer)) > 0) {
+ md.update(buffer, 0, size);
+ }
+ input.close();
+ return md.digest();
+ }
+ }
+}
\ No newline at end of file