diff options
Diffstat (limited to 'src/com/android/packageinstaller/InstallFlowAnalytics.java')
-rw-r--r-- | src/com/android/packageinstaller/InstallFlowAnalytics.java | 608 |
1 files changed, 0 insertions, 608 deletions
diff --git a/src/com/android/packageinstaller/InstallFlowAnalytics.java b/src/com/android/packageinstaller/InstallFlowAnalytics.java deleted file mode 100644 index 4591f31c..00000000 --- a/src/com/android/packageinstaller/InstallFlowAnalytics.java +++ /dev/null @@ -1,608 +0,0 @@ -/* -** -** Copyright 2013, 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.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}. - * - * <p>An instance of this class is created at the beginning of the install flow and gradually filled - * as the user progresses through the flow. When the flow terminates (regardless of the reason), - * {@link #setFlowFinished(byte)} is invoked which reports the installation attempt as an event - * to the Event Log. - */ -public class InstallFlowAnalytics implements Parcelable { - - private static final String TAG = "InstallFlowAnalytics"; - - /** Installation has not yet terminated. */ - static final byte RESULT_NOT_YET_AVAILABLE = -1; - - /** Package successfully installed. */ - static final byte RESULT_SUCCESS = 0; - - /** Installation failed because scheme unsupported. */ - static final byte RESULT_FAILED_UNSUPPORTED_SCHEME = 1; - - /** - * Installation of an APK failed because of a failure to obtain information from the provided - * APK. - */ - static final byte RESULT_FAILED_TO_GET_PACKAGE_INFO = 2; - - /** - * Installation of an already installed package into the current user profile failed because the - * specified package is not installed. - */ - static final byte RESULT_FAILED_PACKAGE_MISSING = 3; - - /** - * Installation failed because installation from unknown sources is prohibited by the Unknown - * Sources setting. - */ - static final byte RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING = 4; - - /** Installation cancelled by the user. */ - static final byte RESULT_CANCELLED_BY_USER = 5; - - /** - * Installation failed due to {@code PackageManager} failure. PackageManager error code is - * provided in {@link #mPackageManagerInstallResult}). - */ - static final byte RESULT_PACKAGE_MANAGER_INSTALL_FAILED = 6; - - /** - * Installation blocked since this feature is not allowed on Android Wear devices yet. - */ - static final byte RESULT_NOT_ALLOWED_ON_WEAR = 7; - - private static final int FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED = 1 << 0; - private static final int FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE = 1 << 1; - private static final int FLAG_VERIFY_APPS_ENABLED = 1 << 2; - private static final int FLAG_APP_VERIFIER_INSTALLED = 1 << 3; - private static final int FLAG_FILE_URI = 1 << 4; - private static final int FLAG_REPLACE = 1 << 5; - private static final int FLAG_SYSTEM_APP = 1 << 6; - private static final int FLAG_PACKAGE_INFO_OBTAINED = 1 << 7; - private static final int FLAG_INSTALL_BUTTON_CLICKED = 1 << 8; - private static final int FLAG_NEW_PERMISSIONS_FOUND = 1 << 9; - private static final int FLAG_PERMISSIONS_DISPLAYED = 1 << 10; - private static final int FLAG_NEW_PERMISSIONS_DISPLAYED = 1 << 11; - private static final int FLAG_ALL_PERMISSIONS_DISPLAYED = 1 << 12; - - /** - * Information about this flow expressed as a collection of flags. See {@code FLAG_...} - * constants. - */ - private int mFlags; - - /** Outcome of the flow. See {@code RESULT_...} constants. */ - private byte mResult = RESULT_NOT_YET_AVAILABLE; - - /** - * Result code returned by {@code PackageManager} to install the package or {@code 0} if - * {@code PackageManager} has not yet been invoked to install the package. - */ - private int mPackageManagerInstallResult; - - /** - * Time instant when the installation request arrived, measured in elapsed realtime - * milliseconds. See {@link SystemClock#elapsedRealtime()}. - */ - private long mStartTimestampMillis; - - /** - * Time instant when the information about the package being installed was obtained, measured in - * elapsed realtime milliseconds. See {@link SystemClock#elapsedRealtime()}. - */ - private long mPackageInfoObtainedTimestampMillis; - - /** - * Time instant when the user clicked the Install button, measured in elapsed realtime - * milliseconds. See {@link SystemClock#elapsedRealtime()}. This field is only valid if the - * Install button has been clicked, as signaled by {@link #FLAG_INSTALL_BUTTON_CLICKED}. - */ - private long mInstallButtonClickTimestampMillis; - - /** - * Time instant when this flow terminated, measured in elapsed realtime milliseconds. See - * {@link SystemClock#elapsedRealtime()}. - */ - 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 - public InstallFlowAnalytics createFromParcel(Parcel in) { - return new InstallFlowAnalytics(in); - } - - @Override - public InstallFlowAnalytics[] newArray(int size) { - return new InstallFlowAnalytics[size]; - } - }; - - public InstallFlowAnalytics() {} - - public InstallFlowAnalytics(Parcel in) { - mFlags = in.readInt(); - mResult = in.readByte(); - mPackageManagerInstallResult = in.readInt(); - mStartTimestampMillis = in.readLong(); - mPackageInfoObtainedTimestampMillis = in.readLong(); - mInstallButtonClickTimestampMillis = in.readLong(); - mEndTimestampMillis = in.readLong(); - mPackageUri = in.readString(); - mLogged = readBoolean(in); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mFlags); - dest.writeByte(mResult); - dest.writeInt(mPackageManagerInstallResult); - dest.writeLong(mStartTimestampMillis); - dest.writeLong(mPackageInfoObtainedTimestampMillis); - dest.writeLong(mInstallButtonClickTimestampMillis); - dest.writeLong(mEndTimestampMillis); - dest.writeString(mPackageUri); - writeBoolean(dest, mLogged); - } - - private static void writeBoolean(Parcel dest, boolean value) { - dest.writeByte((byte) (value ? 1 : 0)); - } - - private static boolean readBoolean(Parcel dest) { - return dest.readByte() != 0; - } - - @Override - public int describeContents() { - 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); - } - - /** Gets whether the Unknown Sources setting is checked. */ - private boolean isInstallsFromUnknownSourcesPermitted() { - return isFlagSet(FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED); - } - - /** Sets whether this install attempt is from an unknown source. */ - void setInstallRequestFromUnknownSource(boolean unknownSource) { - setFlagState(FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE, unknownSource); - } - - /** Gets whether this install attempt is from an unknown source. */ - private boolean isInstallRequestFromUnknownSource() { - return isFlagSet(FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE); - } - - /** Sets whether app verification is enabled. */ - void setVerifyAppsEnabled(boolean enabled) { - setFlagState(FLAG_VERIFY_APPS_ENABLED, enabled); - } - - /** Gets whether app verification is enabled. */ - private boolean isVerifyAppsEnabled() { - return isFlagSet(FLAG_VERIFY_APPS_ENABLED); - } - - /** Sets whether at least one app verifier is installed. */ - void setAppVerifierInstalled(boolean installed) { - setFlagState(FLAG_APP_VERIFIER_INSTALLED, installed); - } - - /** Gets whether at least one app verifier is installed. */ - private boolean isAppVerifierInstalled() { - return isFlagSet(FLAG_APP_VERIFIER_INSTALLED); - } - - /** - * Sets whether an APK file is being installed. - * - * @param fileUri {@code true} if an APK file is being installed, {@code false} if an already - * installed package is being installed to this user profile. - */ - void setFileUri(boolean fileUri) { - 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. - * - * @return {@code true} if an APK file is being installed, {@code false} if an already - * installed package is being installed to this user profile. - */ - private boolean isFileUri() { - return isFlagSet(FLAG_FILE_URI); - } - - /** Sets whether this is an attempt to replace an existing package. */ - void setReplace(boolean replace) { - setFlagState(FLAG_REPLACE, replace); - } - - /** Gets whether this is an attempt to replace an existing package. */ - private boolean isReplace() { - return isFlagSet(FLAG_REPLACE); - } - - /** Sets whether the package being updated is a system package. */ - void setSystemApp(boolean systemApp) { - setFlagState(FLAG_SYSTEM_APP, systemApp); - } - - /** Gets whether the package being updated is a system package. */ - private boolean isSystemApp() { - return isFlagSet(FLAG_SYSTEM_APP); - } - - /** - * Sets whether the package being installed is requesting more permissions than the already - * installed version of the package. - */ - void setNewPermissionsFound(boolean found) { - setFlagState(FLAG_NEW_PERMISSIONS_FOUND, found); - } - - /** - * Gets whether the package being installed is requesting more permissions than the already - * installed version of the package. - */ - private boolean isNewPermissionsFound() { - return isFlagSet(FLAG_NEW_PERMISSIONS_FOUND); - } - - /** Sets whether permissions were displayed to the user. */ - void setPermissionsDisplayed(boolean displayed) { - setFlagState(FLAG_PERMISSIONS_DISPLAYED, displayed); - } - - /** Gets whether permissions were displayed to the user. */ - private boolean isPermissionsDisplayed() { - return isFlagSet(FLAG_PERMISSIONS_DISPLAYED); - } - - /** - * Sets whether new permissions were displayed to the user (if permissions were displayed at - * all). - */ - void setNewPermissionsDisplayed(boolean displayed) { - setFlagState(FLAG_NEW_PERMISSIONS_DISPLAYED, displayed); - } - - /** - * Gets whether new permissions were displayed to the user (if permissions were displayed at - * all). - */ - private boolean isNewPermissionsDisplayed() { - return isFlagSet(FLAG_NEW_PERMISSIONS_DISPLAYED); - } - - /** - * Sets whether all permissions were displayed to the user (if permissions were displayed at - * all). - */ - void setAllPermissionsDisplayed(boolean displayed) { - setFlagState(FLAG_ALL_PERMISSIONS_DISPLAYED, displayed); - } - - /** - * Gets whether all permissions were displayed to the user (if permissions were displayed at - * all). - */ - private boolean isAllPermissionsDisplayed() { - return isFlagSet(FLAG_ALL_PERMISSIONS_DISPLAYED); - } - - /** - * Sets the time instant when the installation request arrived, measured in elapsed realtime - * milliseconds. See {@link SystemClock#elapsedRealtime()}. - */ - void setStartTimestampMillis(long timestampMillis) { - mStartTimestampMillis = timestampMillis; - } - - /** - * Records that the information about the package info has been obtained or that there has been - * a failure to obtain the information. - */ - void setPackageInfoObtained() { - setFlagState(FLAG_PACKAGE_INFO_OBTAINED, true); - mPackageInfoObtainedTimestampMillis = SystemClock.elapsedRealtime(); - } - - /** - * Checks whether the information about the package info has been obtained or that there has - * been a failure to obtain the information. - */ - private boolean isPackageInfoObtained() { - return isFlagSet(FLAG_PACKAGE_INFO_OBTAINED); - } - - /** - * Records that the Install button has been clicked. - */ - void setInstallButtonClicked() { - setFlagState(FLAG_INSTALL_BUTTON_CLICKED, true); - mInstallButtonClickTimestampMillis = SystemClock.elapsedRealtime(); - } - - /** - * Checks whether the Install button has been clicked. - */ - private boolean isInstallButtonClicked() { - return isFlagSet(FLAG_INSTALL_BUTTON_CLICKED); - } - - /** - * Marks this flow as finished due to {@code PackageManager} succeeding or failing to install - * the package and reports this to the Event Log. - */ - void setFlowFinishedWithPackageManagerResult(int packageManagerResult) { - mPackageManagerInstallResult = packageManagerResult; - if (packageManagerResult == PackageManager.INSTALL_SUCCEEDED) { - setFlowFinished( - InstallFlowAnalytics.RESULT_SUCCESS); - } else { - setFlowFinished( - InstallFlowAnalytics.RESULT_PACKAGE_MANAGER_INSTALL_FAILED); - } - } - - /** - * Marks this flow as finished and reports this to the Event Log. - */ - void setFlowFinished(byte result) { - if (mLogged) { - return; - } - mResult = result; - mEndTimestampMillis = SystemClock.elapsedRealtime(); - writeToEventLog(); - } - - private void writeToEventLog() { - byte packageManagerInstallResultByte = 0; - if (mResult == RESULT_PACKAGE_MANAGER_INSTALL_FAILED) { - // PackageManager install error codes are negative, starting from -1 and going to - // -111 (at the moment). We thus store them in negated form. - packageManagerInstallResultByte = clipUnsignedValueToUnsignedByte( - -mPackageManagerInstallResult); - } - - final int resultAndFlags = (mResult & 0xff) - | ((packageManagerInstallResultByte & 0xff) << 8) - | ((mFlags & 0xffff) << 16); - - // Total elapsed time from start to end, in milliseconds. - final int totalElapsedTime = - clipUnsignedLongToUnsignedInt(mEndTimestampMillis - mStartTimestampMillis); - - // Total elapsed time from start till information about the package being installed was - // obtained, in milliseconds. - final int elapsedTimeTillPackageInfoObtained = (isPackageInfoObtained()) - ? clipUnsignedLongToUnsignedInt( - mPackageInfoObtainedTimestampMillis - mStartTimestampMillis) - : 0; - - // Total elapsed time from start till Install button clicked, in milliseconds - // milliseconds. - 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, - 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)) { - Log.v(TAG, "Analytics:" - + "\n\tinstallsFromUnknownSourcesPermitted: " - + isInstallsFromUnknownSourcesPermitted() - + "\n\tinstallRequestFromUnknownSource: " + isInstallRequestFromUnknownSource() - + "\n\tverifyAppsEnabled: " + isVerifyAppsEnabled() - + "\n\tappVerifierInstalled: " + isAppVerifierInstalled() - + "\n\tfileUri: " + isFileUri() - + "\n\treplace: " + isReplace() - + "\n\tsystemApp: " + isSystemApp() - + "\n\tpackageInfoObtained: " + isPackageInfoObtained() - + "\n\tinstallButtonClicked: " + isInstallButtonClicked() - + "\n\tpermissionsDisplayed: " + isPermissionsDisplayed() - + "\n\tnewPermissionsDisplayed: " + isNewPermissionsDisplayed() - + "\n\tallPermissionsDisplayed: " + isAllPermissionsDisplayed() - + "\n\tnewPermissionsFound: " + isNewPermissionsFound() - + "\n\tresult: " + mResult - + "\n\tpackageManagerInstallResult: " + mPackageManagerInstallResult - + "\n\ttotalDuration: " + (mEndTimestampMillis - mStartTimestampMillis) + " ms" - + "\n\ttimeTillPackageInfoObtained: " - + ((isPackageInfoObtained()) - ? ((mPackageInfoObtainedTimestampMillis - mStartTimestampMillis) - + " ms") - : "n/a") - + "\n\ttimeTillInstallButtonClick: " - + ((isInstallButtonClicked()) - ? ((mInstallButtonClickTimestampMillis - mStartTimestampMillis) + " ms") - : "n/a")); - Log.v(TAG, "Wrote to Event Log: 0x" + Long.toString(resultAndFlags & 0xffffffffL, 16) - + ", " + totalElapsedTime - + ", " + elapsedTimeTillPackageInfoObtained - + ", " + elapsedTimeTillInstallButtonClick); - } - } - - private static final byte clipUnsignedValueToUnsignedByte(long value) { - if (value < 0) { - return 0; - } else if (value > 0xff) { - return (byte) 0xff; - } else { - return (byte) value; - } - } - - private static final int clipUnsignedLongToUnsignedInt(long value) { - if (value < 0) { - return 0; - } else if (value > 0xffffffffL) { - return 0xffffffff; - } else { - return (int) value; - } - } - - /** - * Sets or clears the specified flag in the {@link #mFlags} field. - */ - private void setFlagState(int flag, boolean set) { - if (set) { - mFlags |= flag; - } else { - mFlags &= ~flag; - } - } - - /** - * Checks whether the specified flag is set in the {@link #mFlags} field. - */ - 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(); - } -} |