| 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(); |
| } |
| } |
| } |