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 }