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

Commit 2e4190fa authored by Alex Klyubin's avatar Alex Klyubin
Browse files

Log APK hash for package install attempts in the Event Log.

This CL adds a package_digest field to the install_package_attempt
event. The field is populated with the SHA-256 digest of the contents
of the APK iff the user has consented to app verification and app
verification is enabled.

Bug: 11275004
Bug: 10605940

(cherry picked from commit 8fca480b)

Change-Id: I9773925f7e397ada26efac022349dc8e4af01208
parent 37f38236
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -3,4 +3,4 @@
option java_package com.android.packageinstaller

# APK install attempt via PackageInstaller (see InstallFlowAnalytics for format)
90300 install_package_attempt (result_and_flags|1),(total_time|1|3),(time_till_pkg_info_obtained|1|3),(time_till_install_clicked|1|3)
90300 install_package_attempt (result_and_flags|1),(total_time|1|3),(time_till_pkg_info_obtained|1|3),(time_till_install_clicked|1|3),(package_digest|3)
+1 −0
Original line number Diff line number Diff line
@@ -169,6 +169,7 @@ public class InstallAppProgress extends Activity implements View.OnClickListener
        Intent intent = getIntent();
        mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
        mInstallFlowAnalytics = intent.getParcelableExtra(EXTRA_INSTALL_FLOW_ANALYTICS);
        mInstallFlowAnalytics.setContext(this);
        mPackageURI = intent.getData();

        final String scheme = mPackageURI.getScheme();
+115 −9
Original line number Diff line number Diff line
@@ -16,13 +16,27 @@
*/
package com.android.packageinstaller;

import android.content.Context;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.EventLog;
import android.util.Log;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

import libcore.io.IoUtils;

/**
 * Analytics about an attempt to install a package via {@link PackageInstallerActivity}.
 *
@@ -125,9 +139,14 @@ public class InstallFlowAnalytics implements Parcelable {
     */
    private long mEndTimestampMillis;

    /** URI of the package being installed. */
    private String mPackageUri;

    /** Whether this attempt has been logged to the Event Log. */
    private boolean mLogged;

    private Context mContext;

    public static final Parcelable.Creator<InstallFlowAnalytics> CREATOR =
            new Parcelable.Creator<InstallFlowAnalytics>() {
        @Override
@@ -151,6 +170,7 @@ public class InstallFlowAnalytics implements Parcelable {
        mPackageInfoObtainedTimestampMillis = in.readLong();
        mInstallButtonClickTimestampMillis = in.readLong();
        mEndTimestampMillis = in.readLong();
        mPackageUri = in.readString();
        mLogged = readBoolean(in);
    }

@@ -163,6 +183,7 @@ public class InstallFlowAnalytics implements Parcelable {
        dest.writeLong(mPackageInfoObtainedTimestampMillis);
        dest.writeLong(mInstallButtonClickTimestampMillis);
        dest.writeLong(mEndTimestampMillis);
        dest.writeString(mPackageUri);
        writeBoolean(dest, mLogged);
    }

@@ -179,6 +200,10 @@ public class InstallFlowAnalytics implements Parcelable {
        return 0;
    }

    void setContext(Context context) {
        mContext = context;
    }

    /** Sets whether the Unknown Sources setting is checked. */
    void setInstallsFromUnknownSourcesPermitted(boolean permitted) {
        setFlagState(FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED, permitted);
@@ -229,6 +254,13 @@ public class InstallFlowAnalytics implements Parcelable {
        setFlagState(FLAG_FILE_URI, fileUri);
    }

    /**
     * Sets the URI of the package being installed.
     */
    void setPackageUri(String packageUri) {
        mPackageUri = packageUri;
    }

    /**
     * Gets whether an APK file is being installed.
     *
@@ -393,33 +425,65 @@ public class InstallFlowAnalytics implements Parcelable {
                    -mPackageManagerInstallResult);
        }

        int resultAndFlags = (mResult & 0xff)
        final int resultAndFlags = (mResult & 0xff)
                | ((packageManagerInstallResultByte & 0xff) << 8)
                | ((mFlags & 0xffff) << 16);

        // Total elapsed time from start to end, in milliseconds.
        int totalElapsedTime =
        final int totalElapsedTime =
                clipUnsignedLongToUnsignedInt(mEndTimestampMillis - mStartTimestampMillis);

        // Total elapsed time from start till information about the package being installed was
        // obtained, in milliseconds.
        int elapsedTimeTillPackageInfoObtained = (isPackageInfoObtained())
        final int elapsedTimeTillPackageInfoObtained = (isPackageInfoObtained())
                ? clipUnsignedLongToUnsignedInt(
                        mPackageInfoObtainedTimestampMillis - mStartTimestampMillis)
                : 0;

        // Total elapsed time from start till Install button clicked, in milliseconds
        // milliseconds.
        int elapsedTimeTillInstallButtonClick = (isInstallButtonClicked())
        final int elapsedTimeTillInstallButtonClick = (isInstallButtonClicked())
                ? clipUnsignedLongToUnsignedInt(
                            mInstallButtonClickTimestampMillis - mStartTimestampMillis)
                : 0;

        // If this user has consented to app verification, augment the logged event with the hash of
        // the contents of the APK.
        if (((mFlags & FLAG_FILE_URI) != 0)
                && ((mFlags & FLAG_VERIFY_APPS_ENABLED) != 0)
                && (isUserConsentToVerifyAppsGranted())) {
            // Log the hash of the APK's contents.
            // Reading the APK may take a while -- perform in background.
            AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() {
                @Override
                public void run() {
                    byte[] digest = null;
                    try {
                        digest = getPackageContentsDigest();
                    } catch (IOException e) {
                        Log.w(TAG, "Failed to hash APK contents", e);
                    } finally {
                        String digestHex = (digest != null)
                                ? IntegralToString.bytesToHexString(digest, false)
                                : "";
                        EventLogTags.writeInstallPackageAttempt(
                                resultAndFlags,
                                totalElapsedTime,
                                elapsedTimeTillPackageInfoObtained,
                elapsedTimeTillInstallButtonClick);
                                elapsedTimeTillInstallButtonClick,
                                digestHex);
                    }
                }
            });
        } else {
            // Do not log the hash of the APK's contents
            EventLogTags.writeInstallPackageAttempt(
                    resultAndFlags,
                    totalElapsedTime,
                    elapsedTimeTillPackageInfoObtained,
                    elapsedTimeTillInstallButtonClick,
                    "");
        }
        mLogged = true;

        if (Log.isLoggable(TAG, Log.VERBOSE)) {
@@ -494,4 +558,46 @@ public class InstallFlowAnalytics implements Parcelable {
    private boolean isFlagSet(int flag) {
        return (mFlags & flag) == flag;
    }

    /**
     * Checks whether the user has consented to app verification.
     */
    private boolean isUserConsentToVerifyAppsGranted() {
        return Settings.Secure.getInt(
                mContext.getContentResolver(),
                Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT, 0) != 0;
    }

    /**
     * Gets the digest of the contents of the package being installed.
     */
    private byte[] getPackageContentsDigest() throws IOException {
        File file = new File(Uri.parse(mPackageUri).getPath());
        return getSha256ContentsDigest(file);
    }

    /**
     * Gets the SHA-256 digest of the contents of the specified file.
     */
    private static byte[] getSha256ContentsDigest(File file) throws IOException {
        MessageDigest digest;
        try {
            digest = MessageDigest.getInstance("SHA-256");
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("SHA-256 not available", e);
        }

        byte[] buf = new byte[8192];
        InputStream in = null;
        try {
            in = new BufferedInputStream(new FileInputStream(file), buf.length);
            int chunkSize;
            while ((chunkSize = in.read(buf)) != -1) {
                digest.update(buf, 0, chunkSize);
            }
        } finally {
            IoUtils.closeQuietly(in);
        }
        return digest.digest();
    }
}
 No newline at end of file
+2 −2
Original line number Diff line number Diff line
@@ -39,7 +39,6 @@ import android.os.Bundle;
import android.os.SystemClock;
import android.provider.Settings;
import android.support.v4.view.ViewPager;
import android.util.EventLog;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
@@ -51,7 +50,6 @@ import android.widget.TabHost;
import android.widget.TextView;

import java.io.File;
import java.io.Serializable;
import java.util.List;

/*
@@ -422,12 +420,14 @@ public class PackageInstallerActivity extends Activity implements OnCancelListen

        boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
        mInstallFlowAnalytics = new InstallFlowAnalytics();
        mInstallFlowAnalytics.setContext(this);
        mInstallFlowAnalytics.setStartTimestampMillis(SystemClock.elapsedRealtime());
        mInstallFlowAnalytics.setInstallsFromUnknownSourcesPermitted(
                isInstallingUnknownAppsAllowed());
        mInstallFlowAnalytics.setInstallRequestFromUnknownSource(requestFromUnknownSource);
        mInstallFlowAnalytics.setVerifyAppsEnabled(isVerifyAppsEnabled());
        mInstallFlowAnalytics.setAppVerifierInstalled(isAppVerifierInstalled());
        mInstallFlowAnalytics.setPackageUri(mPackageURI.toString());

        final String scheme = mPackageURI.getScheme();
        if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {