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.Context; 20 import android.content.pm.PackageManager; 21 import android.net.Uri; 22 import android.os.AsyncTask; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.os.SystemClock; 26 import android.provider.Settings; 27 import android.util.EventLog; 28 import android.util.Log; 29 30 import java.io.BufferedInputStream; 31 import java.io.File; 32 import java.io.FileInputStream; 33 import java.io.IOException; 34 import java.io.InputStream; 35 import java.security.MessageDigest; 36 import java.security.NoSuchAlgorithmException; 37 38 import libcore.io.IoUtils; 39 40 /** 41 * Analytics about an attempt to install a package via {@link PackageInstallerActivity}. 42 * 43 * <p>An instance of this class is created at the beginning of the install flow and gradually filled 44 * as the user progresses through the flow. When the flow terminates (regardless of the reason), 45 * {@link #setFlowFinished(byte)} is invoked which reports the installation attempt as an event 46 * to the Event Log. 47 */ 48 public class InstallFlowAnalytics implements Parcelable { 49 50 private static final String TAG = "InstallFlowAnalytics"; 51 52 /** Installation has not yet terminated. */ 53 static final byte RESULT_NOT_YET_AVAILABLE = -1; 54 55 /** Package successfully installed. */ 56 static final byte RESULT_SUCCESS = 0; 57 58 /** Installation failed because scheme unsupported. */ 59 static final byte RESULT_FAILED_UNSUPPORTED_SCHEME = 1; 60 61 /** 62 * Installation of an APK failed because of a failure to obtain information from the provided 63 * APK. 64 */ 65 static final byte RESULT_FAILED_TO_GET_PACKAGE_INFO = 2; 66 67 /** 68 * Installation of an already installed package into the current user profile failed because the 69 * specified package is not installed. 70 */ 71 static final byte RESULT_FAILED_PACKAGE_MISSING = 3; 72 73 /** 74 * Installation failed because installation from unknown sources is prohibited by the Unknown 75 * Sources setting. 76 */ 77 static final byte RESULT_BLOCKED_BY_UNKNOWN_SOURCES_SETTING = 4; 78 79 /** Installation cancelled by the user. */ 80 static final byte RESULT_CANCELLED_BY_USER = 5; 81 82 /** 83 * Installation failed due to {@code PackageManager} failure. PackageManager error code is 84 * provided in {@link #mPackageManagerInstallResult}). 85 */ 86 static final byte RESULT_PACKAGE_MANAGER_INSTALL_FAILED = 6; 87 88 private static final int FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED = 1 << 0; 89 private static final int FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE = 1 << 1; 90 private static final int FLAG_VERIFY_APPS_ENABLED = 1 << 2; 91 private static final int FLAG_APP_VERIFIER_INSTALLED = 1 << 3; 92 private static final int FLAG_FILE_URI = 1 << 4; 93 private static final int FLAG_REPLACE = 1 << 5; 94 private static final int FLAG_SYSTEM_APP = 1 << 6; 95 private static final int FLAG_PACKAGE_INFO_OBTAINED = 1 << 7; 96 private static final int FLAG_INSTALL_BUTTON_CLICKED = 1 << 8; 97 private static final int FLAG_NEW_PERMISSIONS_FOUND = 1 << 9; 98 private static final int FLAG_PERMISSIONS_DISPLAYED = 1 << 10; 99 private static final int FLAG_NEW_PERMISSIONS_DISPLAYED = 1 << 11; 100 private static final int FLAG_ALL_PERMISSIONS_DISPLAYED = 1 << 12; 101 102 /** 103 * Information about this flow expressed as a collection of flags. See {@code FLAG_...} 104 * constants. 105 */ 106 private int mFlags; 107 108 /** Outcome of the flow. See {@code RESULT_...} constants. */ 109 private byte mResult = RESULT_NOT_YET_AVAILABLE; 110 111 /** 112 * Result code returned by {@code PackageManager} to install the package or {@code 0} if 113 * {@code PackageManager} has not yet been invoked to install the package. 114 */ 115 private int mPackageManagerInstallResult; 116 117 /** 118 * Time instant when the installation request arrived, measured in elapsed realtime 119 * milliseconds. See {@link SystemClock#elapsedRealtime()}. 120 */ 121 private long mStartTimestampMillis; 122 123 /** 124 * Time instant when the information about the package being installed was obtained, measured in 125 * elapsed realtime milliseconds. See {@link SystemClock#elapsedRealtime()}. 126 */ 127 private long mPackageInfoObtainedTimestampMillis; 128 129 /** 130 * Time instant when the user clicked the Install button, measured in elapsed realtime 131 * milliseconds. See {@link SystemClock#elapsedRealtime()}. This field is only valid if the 132 * Install button has been clicked, as signaled by {@link #FLAG_INSTALL_BUTTON_CLICKED}. 133 */ 134 private long mInstallButtonClickTimestampMillis; 135 136 /** 137 * Time instant when this flow terminated, measured in elapsed realtime milliseconds. See 138 * {@link SystemClock#elapsedRealtime()}. 139 */ 140 private long mEndTimestampMillis; 141 142 /** URI of the package being installed. */ 143 private String mPackageUri; 144 145 /** Whether this attempt has been logged to the Event Log. */ 146 private boolean mLogged; 147 148 private Context mContext; 149 150 public static final Parcelable.Creator<InstallFlowAnalytics> CREATOR = 151 new Parcelable.Creator<InstallFlowAnalytics>() { 152 @Override 153 public InstallFlowAnalytics createFromParcel(Parcel in) { 154 return new InstallFlowAnalytics(in); 155 } 156 157 @Override 158 public InstallFlowAnalytics[] newArray(int size) { 159 return new InstallFlowAnalytics[size]; 160 } 161 }; 162 163 public InstallFlowAnalytics() {} 164 165 public InstallFlowAnalytics(Parcel in) { 166 mFlags = in.readInt(); 167 mResult = in.readByte(); 168 mPackageManagerInstallResult = in.readInt(); 169 mStartTimestampMillis = in.readLong(); 170 mPackageInfoObtainedTimestampMillis = in.readLong(); 171 mInstallButtonClickTimestampMillis = in.readLong(); 172 mEndTimestampMillis = in.readLong(); 173 mPackageUri = in.readString(); 174 mLogged = readBoolean(in); 175 } 176 177 @Override 178 public void writeToParcel(Parcel dest, int flags) { 179 dest.writeInt(mFlags); 180 dest.writeByte(mResult); 181 dest.writeInt(mPackageManagerInstallResult); 182 dest.writeLong(mStartTimestampMillis); 183 dest.writeLong(mPackageInfoObtainedTimestampMillis); 184 dest.writeLong(mInstallButtonClickTimestampMillis); 185 dest.writeLong(mEndTimestampMillis); 186 dest.writeString(mPackageUri); 187 writeBoolean(dest, mLogged); 188 } 189 190 private static void writeBoolean(Parcel dest, boolean value) { 191 dest.writeByte((byte) (value ? 1 : 0)); 192 } 193 194 private static boolean readBoolean(Parcel dest) { 195 return dest.readByte() != 0; 196 } 197 198 @Override 199 public int describeContents() { 200 return 0; 201 } 202 203 void setContext(Context context) { 204 mContext = context; 205 } 206 207 /** Sets whether the Unknown Sources setting is checked. */ 208 void setInstallsFromUnknownSourcesPermitted(boolean permitted) { 209 setFlagState(FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED, permitted); 210 } 211 212 /** Gets whether the Unknown Sources setting is checked. */ 213 private boolean isInstallsFromUnknownSourcesPermitted() { 214 return isFlagSet(FLAG_INSTALLS_FROM_UNKNOWN_SOURCES_PERMITTED); 215 } 216 217 /** Sets whether this install attempt is from an unknown source. */ 218 void setInstallRequestFromUnknownSource(boolean unknownSource) { 219 setFlagState(FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE, unknownSource); 220 } 221 222 /** Gets whether this install attempt is from an unknown source. */ 223 private boolean isInstallRequestFromUnknownSource() { 224 return isFlagSet(FLAG_INSTALL_REQUEST_FROM_UNKNOWN_SOURCE); 225 } 226 227 /** Sets whether app verification is enabled. */ 228 void setVerifyAppsEnabled(boolean enabled) { 229 setFlagState(FLAG_VERIFY_APPS_ENABLED, enabled); 230 } 231 232 /** Gets whether app verification is enabled. */ 233 private boolean isVerifyAppsEnabled() { 234 return isFlagSet(FLAG_VERIFY_APPS_ENABLED); 235 } 236 237 /** Sets whether at least one app verifier is installed. */ 238 void setAppVerifierInstalled(boolean installed) { 239 setFlagState(FLAG_APP_VERIFIER_INSTALLED, installed); 240 } 241 242 /** Gets whether at least one app verifier is installed. */ 243 private boolean isAppVerifierInstalled() { 244 return isFlagSet(FLAG_APP_VERIFIER_INSTALLED); 245 } 246 247 /** 248 * Sets whether an APK file is being installed. 249 * 250 * @param fileUri {@code true} if an APK file is being installed, {@code false} if an already 251 * installed package is being installed to this user profile. 252 */ 253 void setFileUri(boolean fileUri) { 254 setFlagState(FLAG_FILE_URI, fileUri); 255 } 256 257 /** 258 * Sets the URI of the package being installed. 259 */ 260 void setPackageUri(String packageUri) { 261 mPackageUri = packageUri; 262 } 263 264 /** 265 * Gets whether an APK file is being installed. 266 * 267 * @return {@code true} if an APK file is being installed, {@code false} if an already 268 * installed package is being installed to this user profile. 269 */ 270 private boolean isFileUri() { 271 return isFlagSet(FLAG_FILE_URI); 272 } 273 274 /** Sets whether this is an attempt to replace an existing package. */ 275 void setReplace(boolean replace) { 276 setFlagState(FLAG_REPLACE, replace); 277 } 278 279 /** Gets whether this is an attempt to replace an existing package. */ 280 private boolean isReplace() { 281 return isFlagSet(FLAG_REPLACE); 282 } 283 284 /** Sets whether the package being updated is a system package. */ 285 void setSystemApp(boolean systemApp) { 286 setFlagState(FLAG_SYSTEM_APP, systemApp); 287 } 288 289 /** Gets whether the package being updated is a system package. */ 290 private boolean isSystemApp() { 291 return isFlagSet(FLAG_SYSTEM_APP); 292 } 293 294 /** 295 * Sets whether the package being installed is requesting more permissions than the already 296 * installed version of the package. 297 */ 298 void setNewPermissionsFound(boolean found) { 299 setFlagState(FLAG_NEW_PERMISSIONS_FOUND, found); 300 } 301 302 /** 303 * Gets whether the package being installed is requesting more permissions than the already 304 * installed version of the package. 305 */ 306 private boolean isNewPermissionsFound() { 307 return isFlagSet(FLAG_NEW_PERMISSIONS_FOUND); 308 } 309 310 /** Sets whether permissions were displayed to the user. */ 311 void setPermissionsDisplayed(boolean displayed) { 312 setFlagState(FLAG_PERMISSIONS_DISPLAYED, displayed); 313 } 314 315 /** Gets whether permissions were displayed to the user. */ 316 private boolean isPermissionsDisplayed() { 317 return isFlagSet(FLAG_PERMISSIONS_DISPLAYED); 318 } 319 320 /** 321 * Sets whether new permissions were displayed to the user (if permissions were displayed at 322 * all). 323 */ 324 void setNewPermissionsDisplayed(boolean displayed) { 325 setFlagState(FLAG_NEW_PERMISSIONS_DISPLAYED, displayed); 326 } 327 328 /** 329 * Gets whether new permissions were displayed to the user (if permissions were displayed at 330 * all). 331 */ 332 private boolean isNewPermissionsDisplayed() { 333 return isFlagSet(FLAG_NEW_PERMISSIONS_DISPLAYED); 334 } 335 336 /** 337 * Sets whether all permissions were displayed to the user (if permissions were displayed at 338 * all). 339 */ 340 void setAllPermissionsDisplayed(boolean displayed) { 341 setFlagState(FLAG_ALL_PERMISSIONS_DISPLAYED, displayed); 342 } 343 344 /** 345 * Gets whether all permissions were displayed to the user (if permissions were displayed at 346 * all). 347 */ 348 private boolean isAllPermissionsDisplayed() { 349 return isFlagSet(FLAG_ALL_PERMISSIONS_DISPLAYED); 350 } 351 352 /** 353 * Sets the time instant when the installation request arrived, measured in elapsed realtime 354 * milliseconds. See {@link SystemClock#elapsedRealtime()}. 355 */ 356 void setStartTimestampMillis(long timestampMillis) { 357 mStartTimestampMillis = timestampMillis; 358 } 359 360 /** 361 * Records that the information about the package info has been obtained or that there has been 362 * a failure to obtain the information. 363 */ 364 void setPackageInfoObtained() { 365 setFlagState(FLAG_PACKAGE_INFO_OBTAINED, true); 366 mPackageInfoObtainedTimestampMillis = SystemClock.elapsedRealtime(); 367 } 368 369 /** 370 * Checks whether the information about the package info has been obtained or that there has 371 * been a failure to obtain the information. 372 */ 373 private boolean isPackageInfoObtained() { 374 return isFlagSet(FLAG_PACKAGE_INFO_OBTAINED); 375 } 376 377 /** 378 * Records that the Install button has been clicked. 379 */ 380 void setInstallButtonClicked() { 381 setFlagState(FLAG_INSTALL_BUTTON_CLICKED, true); 382 mInstallButtonClickTimestampMillis = SystemClock.elapsedRealtime(); 383 } 384 385 /** 386 * Checks whether the Install button has been clicked. 387 */ 388 private boolean isInstallButtonClicked() { 389 return isFlagSet(FLAG_INSTALL_BUTTON_CLICKED); 390 } 391 392 /** 393 * Marks this flow as finished due to {@code PackageManager} succeeding or failing to install 394 * the package and reports this to the Event Log. 395 */ 396 void setFlowFinishedWithPackageManagerResult(int packageManagerResult) { 397 mPackageManagerInstallResult = packageManagerResult; 398 if (packageManagerResult == PackageManager.INSTALL_SUCCEEDED) { 399 setFlowFinished( 400 InstallFlowAnalytics.RESULT_SUCCESS); 401 } else { 402 setFlowFinished( 403 InstallFlowAnalytics.RESULT_PACKAGE_MANAGER_INSTALL_FAILED); 404 } 405 } 406 407 /** 408 * Marks this flow as finished and reports this to the Event Log. 409 */ 410 void setFlowFinished(byte result) { 411 if (mLogged) { 412 return; 413 } 414 mResult = result; 415 mEndTimestampMillis = SystemClock.elapsedRealtime(); 416 writeToEventLog(); 417 } 418 419 private void writeToEventLog() { 420 byte packageManagerInstallResultByte = 0; 421 if (mResult == RESULT_PACKAGE_MANAGER_INSTALL_FAILED) { 422 // PackageManager install error codes are negative, starting from -1 and going to 423 // -111 (at the moment). We thus store them in negated form. 424 packageManagerInstallResultByte = clipUnsignedValueToUnsignedByte( 425 -mPackageManagerInstallResult); 426 } 427 428 final int resultAndFlags = (mResult & 0xff) 429 | ((packageManagerInstallResultByte & 0xff) << 8) 430 | ((mFlags & 0xffff) << 16); 431 432 // Total elapsed time from start to end, in milliseconds. 433 final int totalElapsedTime = 434 clipUnsignedLongToUnsignedInt(mEndTimestampMillis - mStartTimestampMillis); 435 436 // Total elapsed time from start till information about the package being installed was 437 // obtained, in milliseconds. 438 final int elapsedTimeTillPackageInfoObtained = (isPackageInfoObtained()) 439 ? clipUnsignedLongToUnsignedInt( 440 mPackageInfoObtainedTimestampMillis - mStartTimestampMillis) 441 : 0; 442 443 // Total elapsed time from start till Install button clicked, in milliseconds 444 // milliseconds. 445 final int elapsedTimeTillInstallButtonClick = (isInstallButtonClicked()) 446 ? clipUnsignedLongToUnsignedInt( 447 mInstallButtonClickTimestampMillis - mStartTimestampMillis) 448 : 0; 449 450 // If this user has consented to app verification, augment the logged event with the hash of 451 // the contents of the APK. 452 if (((mFlags & FLAG_FILE_URI) != 0) 453 && ((mFlags & FLAG_VERIFY_APPS_ENABLED) != 0) 454 && (isUserConsentToVerifyAppsGranted())) { 455 // Log the hash of the APK's contents. 456 // Reading the APK may take a while -- perform in background. 457 AsyncTask.THREAD_POOL_EXECUTOR.execute(new Runnable() { 458 @Override 459 public void run() { 460 byte[] digest = null; 461 try { 462 digest = getPackageContentsDigest(); 463 } catch (IOException e) { 464 Log.w(TAG, "Failed to hash APK contents", e); 465 } finally { 466 String digestHex = (digest != null) 467 ? IntegralToString.bytesToHexString(digest, false) 468 : ""; 469 EventLogTags.writeInstallPackageAttempt( 470 resultAndFlags, 471 totalElapsedTime, 472 elapsedTimeTillPackageInfoObtained, 473 elapsedTimeTillInstallButtonClick, 474 digestHex); 475 } 476 } 477 }); 478 } else { 479 // Do not log the hash of the APK's contents 480 EventLogTags.writeInstallPackageAttempt( 481 resultAndFlags, 482 totalElapsedTime, 483 elapsedTimeTillPackageInfoObtained, 484 elapsedTimeTillInstallButtonClick, 485 ""); 486 } 487 mLogged = true; 488 489 if (Log.isLoggable(TAG, Log.VERBOSE)) { 490 Log.v(TAG, "Analytics:" 491 + "\n\tinstallsFromUnknownSourcesPermitted: " 492 + isInstallsFromUnknownSourcesPermitted() 493 + "\n\tinstallRequestFromUnknownSource: " + isInstallRequestFromUnknownSource() 494 + "\n\tverifyAppsEnabled: " + isVerifyAppsEnabled() 495 + "\n\tappVerifierInstalled: " + isAppVerifierInstalled() 496 + "\n\tfileUri: " + isFileUri() 497 + "\n\treplace: " + isReplace() 498 + "\n\tsystemApp: " + isSystemApp() 499 + "\n\tpackageInfoObtained: " + isPackageInfoObtained() 500 + "\n\tinstallButtonClicked: " + isInstallButtonClicked() 501 + "\n\tpermissionsDisplayed: " + isPermissionsDisplayed() 502 + "\n\tnewPermissionsDisplayed: " + isNewPermissionsDisplayed() 503 + "\n\tallPermissionsDisplayed: " + isAllPermissionsDisplayed() 504 + "\n\tnewPermissionsFound: " + isNewPermissionsFound() 505 + "\n\tresult: " + mResult 506 + "\n\tpackageManagerInstallResult: " + mPackageManagerInstallResult 507 + "\n\ttotalDuration: " + (mEndTimestampMillis - mStartTimestampMillis) + " ms" 508 + "\n\ttimeTillPackageInfoObtained: " 509 + ((isPackageInfoObtained()) 510 ? ((mPackageInfoObtainedTimestampMillis - mStartTimestampMillis) 511 + " ms") 512 : "n/a") 513 + "\n\ttimeTillInstallButtonClick: " 514 + ((isInstallButtonClicked()) 515 ? ((mInstallButtonClickTimestampMillis - mStartTimestampMillis) + " ms") 516 : "n/a")); 517 Log.v(TAG, "Wrote to Event Log: 0x" + Long.toString(resultAndFlags & 0xffffffffL, 16) 518 + ", " + totalElapsedTime 519 + ", " + elapsedTimeTillPackageInfoObtained 520 + ", " + elapsedTimeTillInstallButtonClick); 521 } 522 } 523 524 private static final byte clipUnsignedValueToUnsignedByte(long value) { 525 if (value < 0) { 526 return 0; 527 } else if (value > 0xff) { 528 return (byte) 0xff; 529 } else { 530 return (byte) value; 531 } 532 } 533 534 private static final int clipUnsignedLongToUnsignedInt(long value) { 535 if (value < 0) { 536 return 0; 537 } else if (value > 0xffffffffL) { 538 return 0xffffffff; 539 } else { 540 return (int) value; 541 } 542 } 543 544 /** 545 * Sets or clears the specified flag in the {@link #mFlags} field. 546 */ 547 private void setFlagState(int flag, boolean set) { 548 if (set) { 549 mFlags |= flag; 550 } else { 551 mFlags &= ~flag; 552 } 553 } 554 555 /** 556 * Checks whether the specified flag is set in the {@link #mFlags} field. 557 */ 558 private boolean isFlagSet(int flag) { 559 return (mFlags & flag) == flag; 560 } 561 562 /** 563 * Checks whether the user has consented to app verification. 564 */ 565 private boolean isUserConsentToVerifyAppsGranted() { 566 return Settings.Secure.getInt( 567 mContext.getContentResolver(), 568 Settings.Secure.PACKAGE_VERIFIER_USER_CONSENT, 0) != 0; 569 } 570 571 /** 572 * Gets the digest of the contents of the package being installed. 573 */ 574 private byte[] getPackageContentsDigest() throws IOException { 575 File file = new File(Uri.parse(mPackageUri).getPath()); 576 return getSha256ContentsDigest(file); 577 } 578 579 /** 580 * Gets the SHA-256 digest of the contents of the specified file. 581 */ 582 private static byte[] getSha256ContentsDigest(File file) throws IOException { 583 MessageDigest digest; 584 try { 585 digest = MessageDigest.getInstance("SHA-256"); 586 } catch (NoSuchAlgorithmException e) { 587 throw new RuntimeException("SHA-256 not available", e); 588 } 589 590 byte[] buf = new byte[8192]; 591 InputStream in = null; 592 try { 593 in = new BufferedInputStream(new FileInputStream(file), buf.length); 594 int chunkSize; 595 while ((chunkSize = in.read(buf)) != -1) { 596 digest.update(buf, 0, chunkSize); 597 } 598 } finally { 599 IoUtils.closeQuietly(in); 600 } 601 return digest.digest(); 602 } 603 }