Home | History | Annotate | Download | only in timezone
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.server.timezone;
     18 
     19 import com.android.internal.annotations.VisibleForTesting;
     20 
     21 import android.app.timezone.RulesUpdaterContract;
     22 import android.content.Context;
     23 import android.content.pm.PackageManager;
     24 import android.os.Environment;
     25 import android.os.FileUtils;
     26 import android.os.SystemClock;
     27 import android.provider.TimeZoneRulesDataContract;
     28 import android.util.Slog;
     29 
     30 import java.io.File;
     31 import java.io.IOException;
     32 import java.io.PrintWriter;
     33 import java.time.Clock;
     34 
     35 /**
     36  * Monitors the installed applications associated with time zone updates. If the app packages are
     37  * updated it indicates there <em>might</em> be a time zone rules update to apply so a targeted
     38  * broadcast intent is used to trigger the time zone updater app.
     39  *
     40  * <p>The "update triggering" behavior of this component can be disabled via device configuration.
     41  *
     42  * <p>The package tracker listens for package updates of the time zone "updater app" and "data app".
     43  * It also listens for "reliability" triggers. Reliability triggers are there to ensure that the
     44  * package tracker handles failures reliably and are "idle maintenance" events or something similar.
     45  * Reliability triggers can cause a time zone update check to take place if the current state is
     46  * unclear. For example, it can be unclear after boot or after a failure. If there are repeated
     47  * failures reliability updates are halted until the next boot.
     48  *
     49  * <p>This component keeps persistent track of the most recent app packages checked to avoid
     50  * unnecessary expense from broadcasting intents (which will cause other app processes to spawn).
     51  * The current status is also stored to detect whether the most recently-generated check is
     52  * complete successfully. For example, if the device was interrupted while doing a check and never
     53  * acknowledged a check then a check will be retried the next time a "reliability trigger" event
     54  * happens.
     55  */
     56 // Also made non-final so it can be mocked.
     57 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
     58 public class PackageTracker {
     59     private static final String TAG = "timezone.PackageTracker";
     60 
     61     private final PackageManagerHelper mPackageManagerHelper;
     62     private final PackageTrackerIntentHelper mIntentHelper;
     63     private final ConfigHelper mConfigHelper;
     64     private final PackageStatusStorage mPackageStatusStorage;
     65     private final Clock mElapsedRealtimeClock;
     66 
     67     // False if tracking is disabled.
     68     private boolean mTrackingEnabled;
     69 
     70     // These fields may be null if package tracking is disabled.
     71     private String mUpdateAppPackageName;
     72     private String mDataAppPackageName;
     73 
     74     // The time a triggered check is allowed to take before it is considered overdue.
     75     private int mCheckTimeAllowedMillis;
     76     // The number of failed checks in a row before reliability checks should stop happening.
     77     private long mFailedCheckRetryCount;
     78 
     79     /*
     80      * The minimum delay between a successive reliability triggers / other operations. Should to be
     81      * larger than mCheckTimeAllowedMillis to avoid reliability triggers happening during package
     82      * update checks.
     83      */
     84     private int mDelayBeforeReliabilityCheckMillis;
     85 
     86     // Reliability check state: If a check was triggered but not acknowledged within
     87     // mCheckTimeAllowedMillis then another one can be triggered.
     88     private Long mLastTriggerTimestamp = null;
     89 
     90     // Reliability check state: Whether any checks have been triggered at all.
     91     private boolean mCheckTriggered;
     92 
     93     // Reliability check state: A count of how many failures have occurred consecutively.
     94     private int mCheckFailureCount;
     95 
     96     /** Creates the {@link PackageTracker} for normal use. */
     97     static PackageTracker create(Context context) {
     98         Clock elapsedRealtimeClock = SystemClock.elapsedRealtimeClock();
     99         PackageTrackerHelperImpl helperImpl = new PackageTrackerHelperImpl(context);
    100         File storageDir = FileUtils.createDir(Environment.getDataSystemDirectory(), "timezone");
    101         return new PackageTracker(
    102                 elapsedRealtimeClock /* elapsedRealtimeClock */,
    103                 helperImpl /* configHelper */,
    104                 helperImpl /* packageManagerHelper */,
    105                 new PackageStatusStorage(storageDir),
    106                 new PackageTrackerIntentHelperImpl(context));
    107     }
    108 
    109     // A constructor that can be used by tests to supply mocked / faked dependencies.
    110     PackageTracker(Clock elapsedRealtimeClock, ConfigHelper configHelper,
    111             PackageManagerHelper packageManagerHelper, PackageStatusStorage packageStatusStorage,
    112             PackageTrackerIntentHelper intentHelper) {
    113         mElapsedRealtimeClock = elapsedRealtimeClock;
    114         mConfigHelper = configHelper;
    115         mPackageManagerHelper = packageManagerHelper;
    116         mPackageStatusStorage = packageStatusStorage;
    117         mIntentHelper = intentHelper;
    118     }
    119 
    120     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    121     protected synchronized boolean start() {
    122         mTrackingEnabled = mConfigHelper.isTrackingEnabled();
    123         if (!mTrackingEnabled) {
    124             Slog.i(TAG, "Time zone updater / data package tracking explicitly disabled.");
    125             return false;
    126         }
    127 
    128         mUpdateAppPackageName = mConfigHelper.getUpdateAppPackageName();
    129         mDataAppPackageName = mConfigHelper.getDataAppPackageName();
    130         mCheckTimeAllowedMillis = mConfigHelper.getCheckTimeAllowedMillis();
    131         mFailedCheckRetryCount = mConfigHelper.getFailedCheckRetryCount();
    132         mDelayBeforeReliabilityCheckMillis = mCheckTimeAllowedMillis + (60 * 1000);
    133 
    134         // Validate the device configuration including the application packages.
    135         // The manifest entries in the apps themselves are not validated until use as they can
    136         // change and we don't want to prevent the system server starting due to a bad application.
    137         throwIfDeviceSettingsOrAppsAreBad();
    138 
    139         // Explicitly start in a reliability state where reliability triggering will do something.
    140         mCheckTriggered = false;
    141         mCheckFailureCount = 0;
    142 
    143         // Initialize the storage, as needed.
    144         try {
    145             mPackageStatusStorage.initialize();
    146         } catch (IOException e) {
    147             Slog.w(TAG, "PackageTracker storage could not be initialized.", e);
    148             return false;
    149         }
    150 
    151         // Initialize the intent helper.
    152         mIntentHelper.initialize(mUpdateAppPackageName, mDataAppPackageName, this);
    153 
    154         // Schedule a reliability trigger so we will have at least one after boot. This will allow
    155         // us to catch if a package updated wasn't handled to completion. There's no hurry: it's ok
    156         // to delay for a while before doing this even if idle.
    157         mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    158 
    159         Slog.i(TAG, "Time zone updater / data package tracking enabled");
    160         return true;
    161     }
    162 
    163     /**
    164      * Performs checks that confirm the system image has correctly configured package
    165      * tracking configuration. Only called if package tracking is enabled. Throws an exception if
    166      * the device is configured badly which will prevent the device booting.
    167      */
    168     private void throwIfDeviceSettingsOrAppsAreBad() {
    169         // None of the checks below can be based on application manifest settings, otherwise a bad
    170         // update could leave the device in an unbootable state. See validateDataAppManifest() and
    171         // validateUpdaterAppManifest() for softer errors.
    172 
    173         throwRuntimeExceptionIfNullOrEmpty(
    174                 mUpdateAppPackageName, "Update app package name missing.");
    175         throwRuntimeExceptionIfNullOrEmpty(mDataAppPackageName, "Data app package name missing.");
    176         if (mFailedCheckRetryCount < 1) {
    177             throw logAndThrowRuntimeException("mFailedRetryCount=" + mFailedCheckRetryCount, null);
    178         }
    179         if (mCheckTimeAllowedMillis < 1000) {
    180             throw logAndThrowRuntimeException(
    181                     "mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis, null);
    182         }
    183 
    184         // Validate the updater application package.
    185         try {
    186             if (!mPackageManagerHelper.isPrivilegedApp(mUpdateAppPackageName)) {
    187                 throw logAndThrowRuntimeException(
    188                         "Update app " + mUpdateAppPackageName + " must be a priv-app.", null);
    189             }
    190         } catch (PackageManager.NameNotFoundException e) {
    191             throw logAndThrowRuntimeException("Could not determine update app package details for "
    192                     + mUpdateAppPackageName, e);
    193         }
    194         Slog.d(TAG, "Update app " + mUpdateAppPackageName + " is valid.");
    195 
    196         // Validate the data application package.
    197         try {
    198             if (!mPackageManagerHelper.isPrivilegedApp(mDataAppPackageName)) {
    199                 throw logAndThrowRuntimeException(
    200                         "Data app " + mDataAppPackageName + " must be a priv-app.", null);
    201             }
    202         } catch (PackageManager.NameNotFoundException e) {
    203             throw logAndThrowRuntimeException("Could not determine data app package details for "
    204                     + mDataAppPackageName, e);
    205         }
    206         Slog.d(TAG, "Data app " + mDataAppPackageName + " is valid.");
    207     }
    208 
    209     /**
    210      * Inspects the current in-memory state, installed packages and storage state to determine if an
    211      * update check is needed and then trigger if it is.
    212      *
    213      * @param packageChanged true if this method was called because a known packaged definitely
    214      *     changed, false if the cause is a reliability trigger
    215      */
    216     public synchronized void triggerUpdateIfNeeded(boolean packageChanged) {
    217         if (!mTrackingEnabled) {
    218             throw new IllegalStateException("Unexpected call. Tracking is disabled.");
    219         }
    220 
    221         // Validate the applications' current manifest entries: make sure they are configured as
    222         // they should be. These are not fatal and just means that no update is triggered: we don't
    223         // want to take down the system server if an OEM or Google have pushed a bad update to
    224         // an application.
    225         boolean updaterAppManifestValid = validateUpdaterAppManifest();
    226         boolean dataAppManifestValid = validateDataAppManifest();
    227         if (!updaterAppManifestValid || !dataAppManifestValid) {
    228             Slog.e(TAG, "No update triggered due to invalid application manifest entries."
    229                     + " updaterApp=" + updaterAppManifestValid
    230                     + ", dataApp=" + dataAppManifestValid);
    231 
    232             // There's no point in doing any reliability triggers if the current packages are bad.
    233             mIntentHelper.unscheduleReliabilityTrigger();
    234             return;
    235         }
    236 
    237         if (!packageChanged) {
    238             // This call was made because the device is doing a "reliability" check.
    239             // 4 possible cases:
    240             // 1) No check has previously triggered since restart. We want to trigger in this case.
    241             // 2) A check has previously triggered and it is in progress. We want to trigger if
    242             //    the response is overdue.
    243             // 3) A check has previously triggered and it failed. We want to trigger, but only if
    244             //    we're not in a persistent failure state.
    245             // 4) A check has previously triggered and it succeeded.
    246             //    We don't want to trigger, and want to stop future triggers.
    247 
    248             if (!mCheckTriggered) {
    249                 // Case 1.
    250                 Slog.d(TAG, "triggerUpdateIfNeeded: First reliability trigger.");
    251             } else if (isCheckInProgress()) {
    252                 // Case 2.
    253                 if (!isCheckResponseOverdue()) {
    254                     // A check is in progress but hasn't been given time to succeed.
    255                     Slog.d(TAG,
    256                             "triggerUpdateIfNeeded: checkComplete call is not yet overdue."
    257                                     + " Not triggering.");
    258                     // Don't do any work now but we do schedule a future reliability trigger.
    259                     mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    260                     return;
    261                 }
    262             } else if (mCheckFailureCount > mFailedCheckRetryCount) {
    263                 // Case 3. If the system is in some kind of persistent failure state we don't want
    264                 // to keep checking, so just stop.
    265                 Slog.i(TAG, "triggerUpdateIfNeeded: number of allowed consecutive check failures"
    266                         + " exceeded. Stopping reliability triggers until next reboot or package"
    267                         + " update.");
    268                 mIntentHelper.unscheduleReliabilityTrigger();
    269                 return;
    270             } else if (mCheckFailureCount == 0) {
    271                 // Case 4.
    272                 Slog.i(TAG, "triggerUpdateIfNeeded: No reliability check required. Last check was"
    273                         + " successful.");
    274                 mIntentHelper.unscheduleReliabilityTrigger();
    275                 return;
    276             }
    277         }
    278 
    279         // Read the currently installed data / updater package versions.
    280         PackageVersions currentInstalledVersions = lookupInstalledPackageVersions();
    281         if (currentInstalledVersions == null) {
    282             // This should not happen if the device is configured in a valid way.
    283             Slog.e(TAG, "triggerUpdateIfNeeded: currentInstalledVersions was null");
    284             mIntentHelper.unscheduleReliabilityTrigger();
    285             return;
    286         }
    287 
    288         // Establish the current state using package manager and stored state. Determine if we have
    289         // already successfully checked the installed versions.
    290         PackageStatus packageStatus = mPackageStatusStorage.getPackageStatus();
    291         if (packageStatus == null) {
    292             // This can imply corrupt, uninitialized storage state (e.g. first check ever on a
    293             // device) or after some kind of reset.
    294             Slog.i(TAG, "triggerUpdateIfNeeded: No package status data found. Data check needed.");
    295         } else if (!packageStatus.mVersions.equals(currentInstalledVersions)) {
    296             // The stored package version information differs from the installed version.
    297             // Trigger the check in all cases.
    298             Slog.i(TAG, "triggerUpdateIfNeeded: Stored package versions="
    299                     + packageStatus.mVersions + ", do not match current package versions="
    300                     + currentInstalledVersions + ". Triggering check.");
    301         } else {
    302             Slog.i(TAG, "triggerUpdateIfNeeded: Stored package versions match currently"
    303                     + " installed versions, currentInstalledVersions=" + currentInstalledVersions
    304                     + ", packageStatus.mCheckStatus=" + packageStatus.mCheckStatus);
    305             if (packageStatus.mCheckStatus == PackageStatus.CHECK_COMPLETED_SUCCESS) {
    306                 // The last check succeeded and nothing has changed. Do nothing and disable
    307                 // reliability checks.
    308                 Slog.i(TAG, "triggerUpdateIfNeeded: Prior check succeeded. No need to trigger.");
    309                 mIntentHelper.unscheduleReliabilityTrigger();
    310                 return;
    311             }
    312         }
    313 
    314         // Generate a token to send to the updater app.
    315         CheckToken checkToken =
    316                 mPackageStatusStorage.generateCheckToken(currentInstalledVersions);
    317         if (checkToken == null) {
    318             Slog.w(TAG, "triggerUpdateIfNeeded: Unable to generate check token."
    319                     + " Not sending check request.");
    320             // Trigger again later: perhaps we'll have better luck.
    321             mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    322             return;
    323         }
    324 
    325         // Trigger the update check.
    326         mIntentHelper.sendTriggerUpdateCheck(checkToken);
    327         mCheckTriggered = true;
    328 
    329         // Update the reliability check state in case the update fails.
    330         setCheckInProgress();
    331 
    332         // Schedule a reliability trigger in case the update check doesn't succeed and there is no
    333         // response at all. It will be cancelled if the check is successful in recordCheckResult.
    334         mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    335     }
    336 
    337     /**
    338      * Used to record the result of a check. Can be called even if active package tracking is
    339      * disabled.
    340      */
    341     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    342     protected synchronized void recordCheckResult(CheckToken checkToken, boolean success) {
    343         Slog.i(TAG, "recordOperationResult: checkToken=" + checkToken + " success=" + success);
    344 
    345         // If package tracking is disabled it means no record-keeping is required. However, we do
    346         // want to clear out any stored state to make it clear that the current state is unknown and
    347         // should tracking become enabled again (perhaps through an OTA) we'd need to perform an
    348         // update check.
    349         if (!mTrackingEnabled) {
    350             // This means an updater has spontaneously modified time zone data without having been
    351             // triggered. This can happen if the OEM is handling their own updates, but we don't
    352             // need to do any tracking in this case.
    353 
    354             if (checkToken == null) {
    355                 // This is the expected case if tracking is disabled but an OEM is handling time
    356                 // zone installs using their own mechanism.
    357                 Slog.d(TAG, "recordCheckResult: Tracking is disabled and no token has been"
    358                         + " provided. Resetting tracking state.");
    359             } else {
    360                 // This is unexpected. If tracking is disabled then no check token should have been
    361                 // generated by the package tracker. An updater should never create its own token.
    362                 // This could be a bug in the updater.
    363                 Slog.w(TAG, "recordCheckResult: Tracking is disabled and a token " + checkToken
    364                         + " has been unexpectedly provided. Resetting tracking state.");
    365             }
    366             mPackageStatusStorage.resetCheckState();
    367             return;
    368         }
    369 
    370         if (checkToken == null) {
    371             /*
    372              * If the checkToken is null it suggests an install / uninstall / acknowledgement has
    373              * occurred without a prior trigger (or the client didn't return the token it was given
    374              * for some reason, perhaps a bug).
    375              *
    376              * This shouldn't happen under normal circumstances:
    377              *
    378              * If package tracking is enabled, we assume it is the package tracker responsible for
    379              * triggering updates and a token should have been produced and returned.
    380              *
    381              * If the OEM is handling time zone updates case package tracking should be disabled.
    382              *
    383              * This could happen in tests. The device should recover back to a known state by
    384              * itself rather than be left in an invalid state.
    385              *
    386              * We treat this as putting the device into an unknown state and make sure that
    387              * reliability triggering is enabled so we should recover.
    388              */
    389             Slog.i(TAG, "recordCheckResult: Unexpectedly missing checkToken, resetting"
    390                     + " storage state.");
    391             mPackageStatusStorage.resetCheckState();
    392 
    393             // Schedule a reliability trigger and reset the failure count so we know that the
    394             // next reliability trigger will do something.
    395             mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    396             mCheckFailureCount = 0;
    397         } else {
    398             // This is the expected case when tracking is enabled: a check was triggered and it has
    399             // completed.
    400             boolean recordedCheckCompleteSuccessfully =
    401                     mPackageStatusStorage.markChecked(checkToken, success);
    402             if (recordedCheckCompleteSuccessfully) {
    403                 // If we have recorded the result (whatever it was) we know there is no check in
    404                 // progress.
    405                 setCheckComplete();
    406 
    407                 if (success) {
    408                     // Since the check was successful, no reliability trigger is required until
    409                     // there is a package change.
    410                     mIntentHelper.unscheduleReliabilityTrigger();
    411                     mCheckFailureCount = 0;
    412                 } else {
    413                     // Enable schedule a reliability trigger to check again in future.
    414                     mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    415                     mCheckFailureCount++;
    416                 }
    417             } else {
    418                 // The failure to record the check means an optimistic lock failure and suggests
    419                 // that another check was triggered after the token was generated.
    420                 Slog.i(TAG, "recordCheckResult: could not update token=" + checkToken
    421                         + " with success=" + success + ". Optimistic lock failure");
    422 
    423                 // Schedule a reliability trigger to potentially try again in future.
    424                 mIntentHelper.scheduleReliabilityTrigger(mDelayBeforeReliabilityCheckMillis);
    425                 mCheckFailureCount++;
    426             }
    427         }
    428     }
    429 
    430     /** Access to consecutive failure counts for use in tests. */
    431     @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    432     protected int getCheckFailureCountForTests() {
    433         return mCheckFailureCount;
    434     }
    435 
    436     private void setCheckInProgress() {
    437         mLastTriggerTimestamp = mElapsedRealtimeClock.millis();
    438     }
    439 
    440     private void setCheckComplete() {
    441         mLastTriggerTimestamp = null;
    442     }
    443 
    444     private boolean isCheckInProgress() {
    445         return mLastTriggerTimestamp != null;
    446     }
    447 
    448     private boolean isCheckResponseOverdue() {
    449         if (mLastTriggerTimestamp == null) {
    450             return false;
    451         }
    452         // Risk of overflow, but highly unlikely given the implementation and not problematic.
    453         return mElapsedRealtimeClock.millis() > mLastTriggerTimestamp + mCheckTimeAllowedMillis;
    454     }
    455 
    456     private PackageVersions lookupInstalledPackageVersions() {
    457         long updatePackageVersion;
    458         long dataPackageVersion;
    459         try {
    460             updatePackageVersion =
    461                     mPackageManagerHelper.getInstalledPackageVersion(mUpdateAppPackageName);
    462             dataPackageVersion =
    463                     mPackageManagerHelper.getInstalledPackageVersion(mDataAppPackageName);
    464         } catch (PackageManager.NameNotFoundException e) {
    465             Slog.w(TAG, "lookupInstalledPackageVersions: Unable to resolve installed package"
    466                     + " versions", e);
    467             return null;
    468         }
    469         return new PackageVersions(updatePackageVersion, dataPackageVersion);
    470     }
    471 
    472     private boolean validateDataAppManifest() {
    473         // We only want to talk to a provider that exposed by the known data app package
    474         // so we look up the providers exposed by that app and check the well-known authority is
    475         // there. This prevents the case where *even if* the data app doesn't expose the provider
    476         // required, another app cannot expose one to replace it.
    477         if (!mPackageManagerHelper.contentProviderRegistered(
    478                 TimeZoneRulesDataContract.AUTHORITY, mDataAppPackageName)) {
    479             // Error! Found the package but it didn't expose the correct provider.
    480             Slog.w(TAG, "validateDataAppManifest: Data app " + mDataAppPackageName
    481                     + " does not expose the required provider with authority="
    482                     + TimeZoneRulesDataContract.AUTHORITY);
    483             return false;
    484         }
    485         return true;
    486     }
    487 
    488     private boolean validateUpdaterAppManifest() {
    489         try {
    490             // The updater app is expected to have the UPDATE_TIME_ZONE_RULES permission.
    491             // The updater app is expected to have a receiver for the intent we are going to trigger
    492             // and require the TRIGGER_TIME_ZONE_RULES_CHECK.
    493             if (!mPackageManagerHelper.usesPermission(
    494                     mUpdateAppPackageName,
    495                     RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION)) {
    496                 Slog.w(TAG, "validateUpdaterAppManifest: Updater app " + mDataAppPackageName
    497                         + " does not use permission="
    498                         + RulesUpdaterContract.UPDATE_TIME_ZONE_RULES_PERMISSION);
    499                 return false;
    500             }
    501             if (!mPackageManagerHelper.receiverRegistered(
    502                     RulesUpdaterContract.createUpdaterIntent(mUpdateAppPackageName),
    503                     RulesUpdaterContract.TRIGGER_TIME_ZONE_RULES_CHECK_PERMISSION)) {
    504                 return false;
    505             }
    506 
    507             return true;
    508         } catch (PackageManager.NameNotFoundException e) {
    509             Slog.w(TAG, "validateUpdaterAppManifest: Updater app " + mDataAppPackageName
    510                     + " does not expose the required broadcast receiver.", e);
    511             return false;
    512         }
    513     }
    514 
    515     private static void throwRuntimeExceptionIfNullOrEmpty(String value, String message) {
    516         if (value == null || value.trim().isEmpty()) {
    517             throw logAndThrowRuntimeException(message, null);
    518         }
    519     }
    520 
    521     private static RuntimeException logAndThrowRuntimeException(String message, Throwable cause) {
    522         Slog.wtf(TAG, message, cause);
    523         throw new RuntimeException(message, cause);
    524     }
    525 
    526     public void dump(PrintWriter fout) {
    527         fout.println("PackageTrackerState: " + toString());
    528         mPackageStatusStorage.dump(fout);
    529     }
    530 
    531     @Override
    532     public String toString() {
    533         return "PackageTracker{" +
    534                 "mTrackingEnabled=" + mTrackingEnabled +
    535                 ", mUpdateAppPackageName='" + mUpdateAppPackageName + '\'' +
    536                 ", mDataAppPackageName='" + mDataAppPackageName + '\'' +
    537                 ", mCheckTimeAllowedMillis=" + mCheckTimeAllowedMillis +
    538                 ", mDelayBeforeReliabilityCheckMillis=" + mDelayBeforeReliabilityCheckMillis +
    539                 ", mFailedCheckRetryCount=" + mFailedCheckRetryCount +
    540                 ", mLastTriggerTimestamp=" + mLastTriggerTimestamp +
    541                 ", mCheckTriggered=" + mCheckTriggered +
    542                 ", mCheckFailureCount=" + mCheckFailureCount +
    543                 '}';
    544     }
    545 }
    546