Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 53937849 authored by Michal Karpinski's avatar Michal Karpinski Committed by Android (Google) Code Review
Browse files

Merge "Moving app process logging from AMS to PMS" into nyc-dev

parents 415ebdb8 b52a4618
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -315,6 +315,12 @@ interface IPackageManager {
     */
    int getApplicationEnabledSetting(in String packageName, int userId);

    /**
     * Logs process start information (including APK hash) to the security log.
     */
    void logAppProcessStartIfNeeded(String processName, int uid, String seinfo, String apkFile,
            int pid);

    /**
     * As per {@link android.content.pm.PackageManager#flushPackageRestrictionsAsUser}.
     */
+6 −6
Original line number Diff line number Diff line
@@ -1488,7 +1488,6 @@ public final class ActivityManagerService extends ActivityManagerNative
    final ServiceThread mHandlerThread;
    final MainHandler mHandler;
    final UiHandler mUiHandler;
    final ProcessStartLogger mProcessStartLogger;
    PackageManagerInternal mPackageManagerInt;
@@ -2460,8 +2459,6 @@ public final class ActivityManagerService extends ActivityManagerNative
        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,
@@ -3594,7 +3591,12 @@ public final class ActivityManagerService extends ActivityManagerNative
                    app.processName, hostingType,
                    hostingNameStr != null ? hostingNameStr : "");
            mProcessStartLogger.logIfNeededLocked(app, startResult);
            try {
                AppGlobals.getPackageManager().logAppProcessStartIfNeeded(app.processName, app.uid,
                        app.info.seinfo, app.info.sourceDir, startResult.pid);
            } catch (RemoteException ex) {
                // Ignore
            }
            if (app.persistent) {
                Watchdog.getInstance().processStarted(app.processName, startResult.pid);
@@ -6537,8 +6539,6 @@ public final class ActivityManagerService extends ActivityManagerNative
            }
        }, dumpheapFilter);
        mProcessStartLogger.registerListener(mContext);
        // Let system services know.
        mSystemServiceManager.startBootPhase(SystemService.PHASE_BOOT_COMPLETED);
+0 −151
Original line number Diff line number Diff line
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.app.admin.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
+28 −0
Original line number Diff line number Diff line
@@ -106,6 +106,7 @@ import android.app.ActivityManagerNative;
import android.app.IActivityManager;
import android.app.admin.DevicePolicyManagerInternal;
import android.app.admin.IDevicePolicyManager;
import android.app.admin.SecurityLog;
import android.app.backup.IBackupManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -459,6 +460,8 @@ public class PackageManagerService extends IPackageManager.Stub {
    final PackageHandler mHandler;
    private final ProcessLoggingHandler mProcessLoggingHandler;
    /**
     * Messages for {@link #mHandler} that need to wait for system ready before
     * being dispatched.
@@ -1712,6 +1715,8 @@ public class PackageManagerService extends IPackageManager.Stub {
            // Send installed broadcasts if the install/update is not ephemeral
            if (!isEphemeral(res.pkg)) {
                mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
                // Send added for users that see the package for the first time
                sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
                        extras, 0 /*flags*/, null /*targetPackage*/,
@@ -2096,6 +2101,7 @@ public class PackageManagerService extends IPackageManager.Stub {
                    Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
            mHandlerThread.start();
            mHandler = new PackageHandler(mHandlerThread.getLooper());
            mProcessLoggingHandler = new ProcessLoggingHandler();
            Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);
            File dataDir = Environment.getDataDirectory();
@@ -19458,4 +19464,26 @@ Slog.v(TAG, ":: stepped forward, applying functor at tag " + parser.getName());
            return new ArrayList<>(mPackages.values());
        }
    }
    /**
     * Logs process start information (including base APK hash) to the security log.
     * @hide
     */
    public void logAppProcessStartIfNeeded(String processName, int uid, String seinfo,
            String apkFile, int pid) {
        if (!SecurityLog.isLoggingEnabled()) {
            return;
        }
        Bundle data = new Bundle();
        data.putLong("startTimestamp", System.currentTimeMillis());
        data.putString("processName", processName);
        data.putInt("uid", uid);
        data.putString("seinfo", seinfo);
        data.putString("apkFile", apkFile);
        data.putInt("pid", pid);
        Message msg = mProcessLoggingHandler.obtainMessage(
                ProcessLoggingHandler.LOG_APP_PROCESS_START_MSG);
        msg.setData(data);
        mProcessLoggingHandler.sendMessage(msg);
    }
}
+112 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.server.pm;

import android.app.admin.SecurityLog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;

import com.android.internal.os.BackgroundThread;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import android.util.Slog;

public final class ProcessLoggingHandler extends Handler {

    private static final String TAG = "ProcessLoggingHandler";
    static final int LOG_APP_PROCESS_START_MSG = 1;
    static final int INVALIDATE_BASE_APK_HASH_MSG = 2;

    private final HashMap<String, String> mProcessLoggingBaseApkHashes = new HashMap();

    ProcessLoggingHandler() {
        super(BackgroundThread.getHandler().getLooper());
    }

    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case LOG_APP_PROCESS_START_MSG: {
                Bundle bundle = msg.getData();
                String processName = bundle.getString("processName");
                int uid = bundle.getInt("uid");
                String seinfo = bundle.getString("seinfo");
                String apkFile = bundle.getString("apkFile");
                int pid = bundle.getInt("pid");
                long startTimestamp = bundle.getLong("startTimestamp");
                String apkHash = computeStringHashOfApk(apkFile);
                SecurityLog.writeEvent(SecurityLog.TAG_APP_PROCESS_START, processName,
                        startTimestamp, uid, pid, seinfo, apkHash);
                break;
            }
            case INVALIDATE_BASE_APK_HASH_MSG: {
                Bundle bundle = msg.getData();
                mProcessLoggingBaseApkHashes.remove(bundle.getString("apkFile"));
                break;
            }
        }
    }

    void invalidateProcessLoggingBaseApkHash(String apkPath) {
        Bundle data = new Bundle();
        data.putString("apkFile", apkPath);
        Message msg = obtainMessage(INVALIDATE_BASE_APK_HASH_MSG);
        msg.setData(data);
        sendMessage(msg);
    }

    private String computeStringHashOfApk(String apkFile) {
        if (apkFile == null) {
            return "No APK";
        }
        String apkHash = mProcessLoggingBaseApkHashes.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();
                mProcessLoggingBaseApkHashes.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();
    }
}