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

Commit b52a4618 authored by Michal Karpinski's avatar Michal Karpinski
Browse files

Moving app process logging from AMS to PMS

Bug: 26796347
Change-Id: I21894c2edb41929f6ecd3880667c53e00acef677
parent 2298bb19
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();
    }
}