Home | History | Annotate | Download | only in packageinstaller
      1 /*
      2 **
      3 ** Copyright 2013, The Android Open Source Project
      4 **
      5 ** Licensed under the Apache License, Version 2.0 (the "License");
      6 ** you may not use this file except in compliance with the License.
      7 ** You may obtain a copy of the License at
      8 **
      9 **     http://www.apache.org/licenses/LICENSE-2.0
     10 **
     11 ** Unless required by applicable law or agreed to in writing, software
     12 ** distributed under the License is distributed on an "AS IS" BASIS,
     13 ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 ** See the License for the specific language governing permissions and
     15 ** limitations under the License.
     16 */
     17 package com.android.packageinstaller;
     18 
     19 import android.content.pm.PackageManager;
     20 import android.os.Parcel;
     21 import android.os.Parcelable;
     22 import android.os.SystemClock;
     23 import android.util.EventLog;
     24 import android.util.Log;
     25 
     26 /**
     27  * Analytics about an attempt to install a package via {@link PackageInstallerActivity}.
     28  *
     29  * <p>An instance of this class is created at the beginning of the install flow and gradually filled
     30  * as the user progresses through the flow. When the flow terminates (regardless of the reason),
     31  * {@link #setFlowFinished(byte)} is invoked which reports the installation attempt as an event
     32  * to the Event Log.
     33  */
     34 public class InstallFlowAnalytics implements Parcelable {
     35 
     36     private static final String TAG = "InstallFlowAnalytics";
     37 
     38     /** Installation has not yet terminated. */
     39     static final byte RESULT_NOT_YET_AVAILABLE = -1;
     40 
     41     /** Package successfully installed. */
     42     static final byte RESULT_SUCCESS = 0;
     43 
     44     /** Installation failed because scheme unsupported. */
     45     static final byte RESULT_FAILED_UNSUPPORTED_SCHEME = 1;
     46 
     47     /**
     48      * Installation of an APK failed because of a failure to obtain information from the provided
     49      * APK.
     50      */
     51     static final byte RESULT_FAILED_TO_GET_PACKAGE_INFO = 2;
     52 
     53     /**
     54      * Installation of an already installed package into the current user profile failed because the
     55      * specified package is not installed.
     56      */
     57     static final byte RESULT_FAILED_PACKAGE_MISSING = 3;
     58 
     59     /**
     60      * Installation failed because installation from unknown sources is prohibited by the Unknown
     61      * Sources setting.
     62      */
     63     static final byte RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING = 4;
     64 
     65     /** Installation cancelled by the user. */
     66     static final byte RESULT_CANCELLED_BY_USER = 5;
     67 
     68     /**
     69      * Installation failed due to {@code PackageManager} failure. PackageManager error code is
     70      * provided in {@link #mPackageManagerInstallResult}).
     71      */
     72     static final byte RESULT_PACKAGE_MANAGER_INSTALL_FAILED = 6;
     73 
     74     private static final int FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED = 1 << 0;
     75     private static final int FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE = 1 << 1;
     76     private static final int FLAG_VERIFY_APPS_ENABLED = 1 << 2;
     77     private static final int FLAG_APP_VERIFIER_INSTALLED = 1 << 3;
     78     private static final int FLAG_FILE_URI = 1 << 4;
     79     private static final int FLAG_REPLACE = 1 << 5;
     80     private static final int FLAG_SYSTEM_APP = 1 << 6;
     81     private static final int FLAG_PACKAGE_INFO_OBTAINED = 1 << 7;
     82     private static final int FLAG_INSTALL_BUTTON_CLICKED = 1 << 8;
     83     private static final int FLAG_NEW_PERMISSIONS_FOUND = 1 << 9;
     84     private static final int FLAG_PERMISSIONS_DISPLAYED = 1 << 10;
     85     private static final int FLAG_NEW_PERMISSIONS_DISPLAYED = 1 << 11;
     86     private static final int FLAG_ALL_PERMISSIONS_DISPLAYED = 1 << 12;
     87 
     88     /**
     89      * Information about this flow expressed as a collection of flags. See {@code FLAG_...}
     90      * constants.
     91      */
     92     private int mFlags;
     93 
     94     /** Outcome of the flow. See {@code RESULT_...} constants. */
     95     private byte mResult = RESULT_NOT_YET_AVAILABLE;
     96 
     97     /**
     98      * Result code returned by {@code PackageManager} to install the package or {@code 0} if
     99      * {@code PackageManager} has not yet been invoked to install the package.
    100      */
    101     private int mPackageManagerInstallResult;
    102 
    103     /**
    104      * Time instant when the installation request arrived, measured in elapsed realtime
    105      * milliseconds. See {@link SystemClock#elapsedRealtime()}.
    106      */
    107     private long mStartTimestampMillis;
    108 
    109     /**
    110      * Time instant when the information about the package being installed was obtained, measured in
    111      * elapsed realtime milliseconds. See {@link SystemClock#elapsedRealtime()}.
    112      */
    113     private long mPackageInfoObtainedTimestampMillis;
    114 
    115     /**
    116      * Time instant when the user clicked the Install button, measured in elapsed realtime
    117      * milliseconds. See {@link SystemClock#elapsedRealtime()}. This field is only valid if the
    118      * Install button has been clicked, as signaled by {@link #FLAG_INSTALL_BUTTON_CLICKED}.
    119      */
    120     private long mInstallButtonClickTimestampMillis;
    121 
    122     /**
    123      * Time instant when this flow terminated, measured in elapsed realtime milliseconds. See
    124      * {@link SystemClock#elapsedRealtime()}.
    125      */
    126     private long mEndTimestampMillis;
    127 
    128     /** Whether this attempt has been logged to the Event Log. */
    129     private boolean mLogged;
    130 
    131     public static final Parcelable.Creator<InstallFlowAnalytics> CREATOR =
    132             new Parcelable.Creator<InstallFlowAnalytics>() {
    133         @Override
    134         public InstallFlowAnalytics createFromParcel(Parcel in) {
    135             return new InstallFlowAnalytics(in);
    136         }
    137 
    138         @Override
    139         public InstallFlowAnalytics[] newArray(int size) {
    140             return new InstallFlowAnalytics[size];
    141         }
    142     };
    143 
    144     public InstallFlowAnalytics() {}
    145 
    146     public InstallFlowAnalytics(Parcel in) {
    147         mFlags = in.readInt();
    148         mResult = in.readByte();
    149         mPackageManagerInstallResult = in.readInt();
    150         mStartTimestampMillis = in.readLong();
    151         mPackageInfoObtainedTimestampMillis = in.readLong();
    152         mInstallButtonClickTimestampMillis = in.readLong();
    153         mEndTimestampMillis = in.readLong();
    154         mLogged = readBoolean(in);
    155     }
    156 
    157     @Override
    158     public void writeToParcel(Parcel dest, int flags) {
    159         dest.writeInt(mFlags);
    160         dest.writeByte(mResult);
    161         dest.writeInt(mPackageManagerInstallResult);
    162         dest.writeLong(mStartTimestampMillis);
    163         dest.writeLong(mPackageInfoObtainedTimestampMillis);
    164         dest.writeLong(mInstallButtonClickTimestampMillis);
    165         dest.writeLong(mEndTimestampMillis);
    166         writeBoolean(dest, mLogged);
    167     }
    168 
    169     private static void writeBoolean(Parcel dest, boolean value) {
    170         dest.writeByte((byte) (value ? 1 : 0));
    171     }
    172 
    173     private static boolean readBoolean(Parcel dest) {
    174         return dest.readByte() != 0;
    175     }
    176 
    177     @Override
    178     public int describeContents() {
    179         return 0;
    180     }
    181 
    182     /** Sets whether the Unknown Sources setting is checked. */
    183     void setInstallsFromUnknownSourcesPermitted(boolean permitted) {
    184         setFlagState(FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED, permitted);
    185     }
    186 
    187     /** Gets whether the Unknown Sources setting is checked. */
    188     private boolean isInstallsFromUnknownSourcesPermitted() {
    189         return isFlagSet(FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED);
    190     }
    191 
    192     /** Sets whether this install attempt is from an unknown source. */
    193     void setInstallRequestFromUnknownSource(boolean unknownSource) {
    194         setFlagState(FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE, unknownSource);
    195     }
    196 
    197     /** Gets whether this install attempt is from an unknown source. */
    198     private boolean isInstallRequestFromUnknownSource() {
    199         return isFlagSet(FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE);
    200     }
    201 
    202     /** Sets whether app verification is enabled. */
    203     void setVerifyAppsEnabled(boolean enabled) {
    204         setFlagState(FLAG_VERIFY_APPS_ENABLED, enabled);
    205     }
    206 
    207     /** Gets whether app verification is enabled. */
    208     private boolean isVerifyAppsEnabled() {
    209         return isFlagSet(FLAG_VERIFY_APPS_ENABLED);
    210     }
    211 
    212     /** Sets whether at least one app verifier is installed. */
    213     void setAppVerifierInstalled(boolean installed) {
    214         setFlagState(FLAG_APP_VERIFIER_INSTALLED, installed);
    215     }
    216 
    217     /** Gets whether at least one app verifier is installed. */
    218     private boolean isAppVerifierInstalled() {
    219         return isFlagSet(FLAG_APP_VERIFIER_INSTALLED);
    220     }
    221 
    222     /**
    223      * Sets whether an APK file is being installed.
    224      *
    225      * @param fileUri {@code true} if an APK file is being installed, {@code false} if an already
    226      *        installed package is being installed to this user profile.
    227      */
    228     void setFileUri(boolean fileUri) {
    229         setFlagState(FLAG_FILE_URI, fileUri);
    230     }
    231 
    232     /**
    233      * Gets whether an APK file is being installed.
    234      *
    235      * @return {@code true} if an APK file is being installed, {@code false} if an already
    236      *         installed package is being installed to this user profile.
    237      */
    238     private boolean isFileUri() {
    239         return isFlagSet(FLAG_FILE_URI);
    240     }
    241 
    242     /** Sets whether this is an attempt to replace an existing package. */
    243     void setReplace(boolean replace) {
    244         setFlagState(FLAG_REPLACE, replace);
    245     }
    246 
    247     /** Gets whether this is an attempt to replace an existing package. */
    248     private boolean isReplace() {
    249         return isFlagSet(FLAG_REPLACE);
    250     }
    251 
    252     /** Sets whether the package being updated is a system package. */
    253     void setSystemApp(boolean systemApp) {
    254         setFlagState(FLAG_SYSTEM_APP, systemApp);
    255     }
    256 
    257     /** Gets whether the package being updated is a system package. */
    258     private boolean isSystemApp() {
    259         return isFlagSet(FLAG_SYSTEM_APP);
    260     }
    261 
    262     /**
    263      * Sets whether the package being installed is requesting more permissions than the already
    264      * installed version of the package.
    265      */
    266     void setNewPermissionsFound(boolean found) {
    267         setFlagState(FLAG_NEW_PERMISSIONS_FOUND, found);
    268     }
    269 
    270     /**
    271      * Gets whether the package being installed is requesting more permissions than the already
    272      * installed version of the package.
    273      */
    274     private boolean isNewPermissionsFound() {
    275         return isFlagSet(FLAG_NEW_PERMISSIONS_FOUND);
    276     }
    277 
    278     /** Sets whether permissions were displayed to the user. */
    279     void setPermissionsDisplayed(boolean displayed) {
    280         setFlagState(FLAG_PERMISSIONS_DISPLAYED, displayed);
    281     }
    282 
    283     /** Gets whether permissions were displayed to the user. */
    284     private boolean isPermissionsDisplayed() {
    285         return isFlagSet(FLAG_PERMISSIONS_DISPLAYED);
    286     }
    287 
    288     /**
    289      * Sets whether new permissions were displayed to the user (if permissions were displayed at
    290      * all).
    291      */
    292     void setNewPermissionsDisplayed(boolean displayed) {
    293         setFlagState(FLAG_NEW_PERMISSIONS_DISPLAYED, displayed);
    294     }
    295 
    296     /**
    297      * Gets whether new permissions were displayed to the user (if permissions were displayed at
    298      * all).
    299      */
    300     private boolean isNewPermissionsDisplayed() {
    301         return isFlagSet(FLAG_NEW_PERMISSIONS_DISPLAYED);
    302     }
    303 
    304     /**
    305      * Sets whether all permissions were displayed to the user (if permissions were displayed at
    306      * all).
    307      */
    308     void setAllPermissionsDisplayed(boolean displayed) {
    309         setFlagState(FLAG_ALL_PERMISSIONS_DISPLAYED, displayed);
    310     }
    311 
    312     /**
    313      * Gets whether all permissions were displayed to the user (if permissions were displayed at
    314      * all).
    315      */
    316     private boolean isAllPermissionsDisplayed() {
    317         return isFlagSet(FLAG_ALL_PERMISSIONS_DISPLAYED);
    318     }
    319 
    320     /**
    321      * Sets the time instant when the installation request arrived, measured in elapsed realtime
    322      * milliseconds. See {@link SystemClock#elapsedRealtime()}.
    323      */
    324     void setStartTimestampMillis(long timestampMillis) {
    325         mStartTimestampMillis = timestampMillis;
    326     }
    327 
    328     /**
    329      * Records that the information about the package info has been obtained or that there has been
    330      * a failure to obtain the information.
    331      */
    332     void setPackageInfoObtained() {
    333         setFlagState(FLAG_PACKAGE_INFO_OBTAINED, true);
    334         mPackageInfoObtainedTimestampMillis = SystemClock.elapsedRealtime();
    335     }
    336 
    337     /**
    338      * Checks whether the information about the package info has been obtained or that there has
    339      * been a failure to obtain the information.
    340      */
    341     private boolean isPackageInfoObtained() {
    342         return isFlagSet(FLAG_PACKAGE_INFO_OBTAINED);
    343     }
    344 
    345     /**
    346      * Records that the Install button has been clicked.
    347      */
    348     void setInstallButtonClicked() {
    349         setFlagState(FLAG_INSTALL_BUTTON_CLICKED, true);
    350         mInstallButtonClickTimestampMillis = SystemClock.elapsedRealtime();
    351     }
    352 
    353     /**
    354      * Checks whether the Install button has been clicked.
    355      */
    356     private boolean isInstallButtonClicked() {
    357         return isFlagSet(FLAG_INSTALL_BUTTON_CLICKED);
    358     }
    359 
    360     /**
    361      * Marks this flow as finished due to {@code PackageManager} succeeding or failing to install
    362      * the package and reports this to the Event Log.
    363      */
    364     void setFlowFinishedWithPackageManagerResult(int packageManagerResult) {
    365         mPackageManagerInstallResult = packageManagerResult;
    366         if (packageManagerResult == PackageManager.INSTALL_SUCCEEDED) {
    367             setFlowFinished(
    368                     InstallFlowAnalytics.RESULT_SUCCESS);
    369         } else {
    370             setFlowFinished(
    371                     InstallFlowAnalytics.RESULT_PACKAGE_MANAGER_INSTALL_FAILED);
    372         }
    373     }
    374 
    375     /**
    376      * Marks this flow as finished and reports this to the Event Log.
    377      */
    378     void setFlowFinished(byte result) {
    379         if (mLogged) {
    380             return;
    381         }
    382         mResult = result;
    383         mEndTimestampMillis = SystemClock.elapsedRealtime();
    384         writeToEventLog();
    385     }
    386 
    387     private void writeToEventLog() {
    388         byte packageManagerInstallResultByte = 0;
    389         if (mResult == RESULT_PACKAGE_MANAGER_INSTALL_FAILED) {
    390             // PackageManager install error codes are negative, starting from -1 and going to
    391             // -111 (at the moment). We thus store them in negated form.
    392             packageManagerInstallResultByte = clipUnsignedValueToUnsignedByte(
    393                     -mPackageManagerInstallResult);
    394         }
    395 
    396         int resultAndFlags = (mResult & 0xff)
    397                 | ((packageManagerInstallResultByte & 0xff) << 8)
    398                 | ((mFlags & 0xffff) << 16);
    399 
    400         // Total elapsed time from start to end, in milliseconds.
    401         int totalElapsedTime =
    402                 clipUnsignedLongToUnsignedInt(mEndTimestampMillis - mStartTimestampMillis);
    403 
    404         // Total elapsed time from start till information about the package being installed was
    405         // obtained, in milliseconds.
    406         int elapsedTimeTillPackageInfoObtained = (isPackageInfoObtained())
    407                 ? clipUnsignedLongToUnsignedInt(
    408                         mPackageInfoObtainedTimestampMillis - mStartTimestampMillis)
    409                 : 0;
    410 
    411         // Total elapsed time from start till Install button clicked, in milliseconds
    412         // milliseconds.
    413         int elapsedTimeTillInstallButtonClick = (isInstallButtonClicked())
    414                 ? clipUnsignedLongToUnsignedInt(
    415                             mInstallButtonClickTimestampMillis - mStartTimestampMillis)
    416                 : 0;
    417 
    418         EventLogTags.writeInstallPackageAttempt(
    419                 resultAndFlags,
    420                 totalElapsedTime,
    421                 elapsedTimeTillPackageInfoObtained,
    422                 elapsedTimeTillInstallButtonClick);
    423         mLogged = true;
    424 
    425         if (Log.isLoggable(TAG, Log.VERBOSE)) {
    426             Log.v(TAG, "Analytics:"
    427                     + "\n\tinstallsFromUnknownSourcesPermitted: "
    428                         + isInstallsFromUnknownSourcesPermitted()
    429                     + "\n\tinstallRequestFromUnknownSource: " + isInstallRequestFromUnknownSource()
    430                     + "\n\tverifyAppsEnabled: " + isVerifyAppsEnabled()
    431                     + "\n\tappVerifierInstalled: " + isAppVerifierInstalled()
    432                     + "\n\tfileUri: " + isFileUri()
    433                     + "\n\treplace: " + isReplace()
    434                     + "\n\tsystemApp: " + isSystemApp()
    435                     + "\n\tpackageInfoObtained: " + isPackageInfoObtained()
    436                     + "\n\tinstallButtonClicked: " + isInstallButtonClicked()
    437                     + "\n\tpermissionsDisplayed: " + isPermissionsDisplayed()
    438                     + "\n\tnewPermissionsDisplayed: " + isNewPermissionsDisplayed()
    439                     + "\n\tallPermissionsDisplayed: " + isAllPermissionsDisplayed()
    440                     + "\n\tnewPermissionsFound: " + isNewPermissionsFound()
    441                     + "\n\tresult: " + mResult
    442                     + "\n\tpackageManagerInstallResult: " + mPackageManagerInstallResult
    443                     + "\n\ttotalDuration: " + (mEndTimestampMillis - mStartTimestampMillis) + " ms"
    444                     + "\n\ttimeTillPackageInfoObtained: "
    445                         + ((isPackageInfoObtained())
    446                             ? ((mPackageInfoObtainedTimestampMillis - mStartTimestampMillis)
    447                                     + " ms")
    448                             : "n/a")
    449                     + "\n\ttimeTillInstallButtonClick: "
    450                         + ((isInstallButtonClicked())
    451                             ? ((mInstallButtonClickTimestampMillis - mStartTimestampMillis) + " ms")
    452                             : "n/a"));
    453             Log.v(TAG, "Wrote to Event Log: 0x" + Long.toString(resultAndFlags & 0xffffffffL, 16)
    454                     + ", " + totalElapsedTime
    455                     + ", " + elapsedTimeTillPackageInfoObtained
    456                     + ", " + elapsedTimeTillInstallButtonClick);
    457         }
    458     }
    459 
    460     private static final byte clipUnsignedValueToUnsignedByte(long value) {
    461         if (value < 0) {
    462             return 0;
    463         } else if (value > 0xff) {
    464             return (byte) 0xff;
    465         } else {
    466             return (byte) value;
    467         }
    468     }
    469 
    470     private static final int clipUnsignedLongToUnsignedInt(long value) {
    471         if (value < 0) {
    472             return 0;
    473         } else if (value > 0xffffffffL) {
    474             return 0xffffffff;
    475         } else {
    476             return (int) value;
    477         }
    478     }
    479 
    480     /**
    481      * Sets or clears the specified flag in the {@link #mFlags} field.
    482      */
    483     private void setFlagState(int flag, boolean set) {
    484         if (set) {
    485             mFlags |= flag;
    486         } else {
    487             mFlags &= ~flag;
    488         }
    489     }
    490 
    491     /**
    492      * Checks whether the specified flag is set in the {@link #mFlags} field.
    493      */
    494     private boolean isFlagSet(int flag) {
    495         return (mFlags & flag) == flag;
    496     }
    497 }