From 8fca480bee00578c1529b1f32ab30af096db82dd Mon Sep 17 00:00:00 2001 From: Alex Klyubin Date: Thu, 10 Oct 2013 11:33:00 -0700 Subject: 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: 10605940 Change-Id: I0d191398ed8d28950c7b5ee0ce02ec077d2c888b --- .../android/packageinstaller/EventLogTags.logtags | 2 +- .../packageinstaller/InstallAppProgress.java | 1 + .../packageinstaller/InstallFlowAnalytics.java | 124 +++++++++++++++++++-- .../packageinstaller/PackageInstallerActivity.java | 4 +- 4 files changed, 119 insertions(+), 12 deletions(-) (limited to 'src') diff --git a/src/com/android/packageinstaller/EventLogTags.logtags b/src/com/android/packageinstaller/EventLogTags.logtags index 01831488..8cbb1ccd 100644 --- a/src/com/android/packageinstaller/EventLogTags.logtags +++ b/src/com/android/packageinstaller/EventLogTags.logtags @@ -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) diff --git a/src/com/android/packageinstaller/InstallAppProgress.java b/src/com/android/packageinstaller/InstallAppProgress.java index 83e4aa7f..c8e4133b 100755 --- a/src/com/android/packageinstaller/InstallAppProgress.java +++ b/src/com/android/packageinstaller/InstallAppProgress.java @@ -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(); diff --git a/src/com/android/packageinstaller/InstallFlowAnalytics.java b/src/com/android/packageinstaller/InstallFlowAnalytics.java index ac8e53ac..2fc6db37 100644 --- a/src/com/android/packageinstaller/InstallFlowAnalytics.java +++ b/src/com/android/packageinstaller/InstallFlowAnalytics.java @@ -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 CREATOR = new Parcelable.Creator() { @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; - EventLogTags.writeInstallPackageAttempt( - resultAndFlags, - totalElapsedTime, - elapsedTimeTillPackageInfoObtained, - elapsedTimeTillInstallButtonClick); + // 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, + 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 diff --git a/src/com/android/packageinstaller/PackageInstallerActivity.java b/src/com/android/packageinstaller/PackageInstallerActivity.java index a08e792d..a11e11eb 100644 --- a/src/com/android/packageinstaller/PackageInstallerActivity.java +++ b/src/com/android/packageinstaller/PackageInstallerActivity.java @@ -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)) { -- cgit v1.2.3