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