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