Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 153f08e8 authored by Neil Fuller's avatar Neil Fuller Committed by Android (Google) Code Review
Browse files

Merge "Changes to support time zone provider status APIs"

parents 314aec32 13c1a5cf
Loading
Loading
Loading
Loading
+67 −37
Original line number Diff line number Diff line
@@ -57,7 +57,7 @@ public final class TimeZoneProviderEvent implements Parcelable {

    /**
     * The provider was uncertain about the time zone. See {@link
     * TimeZoneProviderService#reportUncertain()}
     * TimeZoneProviderService#reportUncertain(TimeZoneProviderStatus)}
     */
    public static final @EventType int EVENT_TYPE_UNCERTAIN = 3;

@@ -66,42 +66,55 @@ public final class TimeZoneProviderEvent implements Parcelable {
    @ElapsedRealtimeLong
    private final long mCreationElapsedMillis;

    // Populated when mType == EVENT_TYPE_SUGGESTION
    @Nullable
    private final TimeZoneProviderSuggestion mSuggestion;

    // Populated when mType == EVENT_TYPE_PERMANENT_FAILURE
    @Nullable
    private final String mFailureCause;

    private TimeZoneProviderEvent(@EventType int type,
    // Populated when mType == EVENT_TYPE_SUGGESTION or EVENT_TYPE_UNCERTAIN
    @Nullable
    private final TimeZoneProviderStatus mTimeZoneProviderStatus;

    private TimeZoneProviderEvent(int type,
            @ElapsedRealtimeLong long creationElapsedMillis,
            @Nullable TimeZoneProviderSuggestion suggestion,
            @Nullable String failureCause) {
            @Nullable String failureCause,
            @Nullable TimeZoneProviderStatus timeZoneProviderStatus) {
        mType = type;
        mCreationElapsedMillis = creationElapsedMillis;
        mSuggestion = suggestion;
        mFailureCause = failureCause;
        mTimeZoneProviderStatus = timeZoneProviderStatus;
    }

    /** Returns a event of type {@link #EVENT_TYPE_SUGGESTION}. */
    /** Returns an event of type {@link #EVENT_TYPE_SUGGESTION}. */
    public static TimeZoneProviderEvent createSuggestionEvent(
            @ElapsedRealtimeLong long creationElapsedMillis,
            @NonNull TimeZoneProviderSuggestion suggestion) {
            @NonNull TimeZoneProviderSuggestion suggestion,
            @NonNull TimeZoneProviderStatus providerStatus) {
        return new TimeZoneProviderEvent(EVENT_TYPE_SUGGESTION, creationElapsedMillis,
                Objects.requireNonNull(suggestion), null);
                Objects.requireNonNull(suggestion), null, Objects.requireNonNull(providerStatus));
    }

    /** Returns a event of type {@link #EVENT_TYPE_UNCERTAIN}. */
    /** Returns an event of type {@link #EVENT_TYPE_UNCERTAIN}. */
    public static TimeZoneProviderEvent createUncertainEvent(
            @ElapsedRealtimeLong long creationElapsedMillis) {
        return new TimeZoneProviderEvent(EVENT_TYPE_UNCERTAIN, creationElapsedMillis, null, null);
            @ElapsedRealtimeLong long creationElapsedMillis,
            @NonNull TimeZoneProviderStatus timeZoneProviderStatus) {

        return new TimeZoneProviderEvent(
                EVENT_TYPE_UNCERTAIN, creationElapsedMillis, null, null,
                Objects.requireNonNull(timeZoneProviderStatus));
    }

    /** Returns a event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */
    /** Returns an event of type {@link #EVENT_TYPE_PERMANENT_FAILURE}. */
    public static TimeZoneProviderEvent createPermanentFailureEvent(
            @ElapsedRealtimeLong long creationElapsedMillis,
            @NonNull String cause) {
        return new TimeZoneProviderEvent(EVENT_TYPE_PERMANENT_FAILURE, creationElapsedMillis, null,
                Objects.requireNonNull(cause));
                Objects.requireNonNull(cause), null);
    }

    /**
@@ -126,7 +139,7 @@ public final class TimeZoneProviderEvent implements Parcelable {
    }

    /**
     * Returns the failure cauese. Populated when {@link #getType()} is {@link
     * Returns the failure cause. Populated when {@link #getType()} is {@link
     * #EVENT_TYPE_PERMANENT_FAILURE}.
     */
    @Nullable
@@ -134,17 +147,27 @@ public final class TimeZoneProviderEvent implements Parcelable {
        return mFailureCause;
    }

    public static final @NonNull Creator<TimeZoneProviderEvent> CREATOR =
            new Creator<TimeZoneProviderEvent>() {
    /**
     * Returns the status of the time zone provider. Populated when {@link #getType()} is {@link
     * #EVENT_TYPE_UNCERTAIN} or {@link #EVENT_TYPE_SUGGESTION}.
     */
    @Nullable
    public TimeZoneProviderStatus getTimeZoneProviderStatus() {
        return mTimeZoneProviderStatus;
    }

    public static final @NonNull Creator<TimeZoneProviderEvent> CREATOR = new Creator<>() {
        @Override
        public TimeZoneProviderEvent createFromParcel(Parcel in) {
            int type = in.readInt();
            long creationElapsedMillis = in.readLong();
                    TimeZoneProviderSuggestion suggestion =
                            in.readParcelable(getClass().getClassLoader(), android.service.timezone.TimeZoneProviderSuggestion.class);
            TimeZoneProviderSuggestion suggestion = in.readParcelable(
                    getClass().getClassLoader(), TimeZoneProviderSuggestion.class);
            String failureCause = in.readString8();
            TimeZoneProviderStatus status = in.readParcelable(
                    getClass().getClassLoader(), TimeZoneProviderStatus.class);
            return new TimeZoneProviderEvent(
                            type, creationElapsedMillis, suggestion, failureCause);
                    type, creationElapsedMillis, suggestion, failureCause, status);
        }

        @Override
@@ -164,6 +187,7 @@ public final class TimeZoneProviderEvent implements Parcelable {
        parcel.writeLong(mCreationElapsedMillis);
        parcel.writeParcelable(mSuggestion, 0);
        parcel.writeString8(mFailureCause);
        parcel.writeParcelable(mTimeZoneProviderStatus, 0);
    }

    @Override
@@ -173,14 +197,17 @@ public final class TimeZoneProviderEvent implements Parcelable {
                + ", mCreationElapsedMillis=" + Duration.ofMillis(mCreationElapsedMillis).toString()
                + ", mSuggestion=" + mSuggestion
                + ", mFailureCause=" + mFailureCause
                + ", mTimeZoneProviderStatus=" + mTimeZoneProviderStatus
                + '}';
    }

    /**
     * Similar to {@link #equals} except this methods checks for equivalence, not equality.
     * i.e. two {@link #EVENT_TYPE_UNCERTAIN} and {@link #EVENT_TYPE_PERMANENT_FAILURE} events are
     * always equivalent, two {@link #EVENT_TYPE_SUGGESTION} events are equivalent if they suggest
     * the same time zones.
     * i.e. two {@link #EVENT_TYPE_SUGGESTION} events are equivalent if they suggest
     * the same time zones and have the same provider status, two {@link #EVENT_TYPE_UNCERTAIN}
     * events are equivalent if they have the same provider status, and {@link
     * #EVENT_TYPE_PERMANENT_FAILURE} events are always equivalent (the nature of the failure is not
     * considered).
     */
    @SuppressWarnings("ReferenceEquality")
    public boolean isEquivalentTo(@Nullable TimeZoneProviderEvent other) {
@@ -191,9 +218,10 @@ public final class TimeZoneProviderEvent implements Parcelable {
            return false;
        }
        if (mType == EVENT_TYPE_SUGGESTION) {
            return mSuggestion.isEquivalentTo(other.getSuggestion());
            return mSuggestion.isEquivalentTo(other.mSuggestion)
                    && Objects.equals(mTimeZoneProviderStatus, other.mTimeZoneProviderStatus);
        }
        return true;
        return Objects.equals(mTimeZoneProviderStatus, other.mTimeZoneProviderStatus);
    }

    @Override
@@ -208,11 +236,13 @@ public final class TimeZoneProviderEvent implements Parcelable {
        return mType == that.mType
                && mCreationElapsedMillis == that.mCreationElapsedMillis
                && Objects.equals(mSuggestion, that.mSuggestion)
                && Objects.equals(mFailureCause, that.mFailureCause);
                && Objects.equals(mFailureCause, that.mFailureCause)
                && Objects.equals(mTimeZoneProviderStatus, that.mTimeZoneProviderStatus);
    }

    @Override
    public int hashCode() {
        return Objects.hash(mType, mCreationElapsedMillis, mSuggestion, mFailureCause);
        return Objects.hash(mType, mCreationElapsedMillis, mSuggestion, mFailureCause,
                mTimeZoneProviderStatus);
    }
}
+31 −2
Original line number Diff line number Diff line
@@ -203,6 +203,20 @@ public abstract class TimeZoneProviderService extends Service {
     * details.
     */
    public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion) {
        reportSuggestion(suggestion, TimeZoneProviderStatus.UNKNOWN);
    }

    /**
     * Indicates a successful time zone detection. See {@link TimeZoneProviderSuggestion} for
     * details.
     *
     * @param providerStatus provider status information that can influence detector service
     *   behavior and/or be reported via the device UI
     *
     * @hide
     */
    public final void reportSuggestion(@NonNull TimeZoneProviderSuggestion suggestion,
            @NonNull TimeZoneProviderStatus providerStatus) {
        Objects.requireNonNull(suggestion);

        mHandler.post(() -> {
@@ -212,7 +226,7 @@ public abstract class TimeZoneProviderService extends Service {
                    try {
                        TimeZoneProviderEvent thisEvent =
                                TimeZoneProviderEvent.createSuggestionEvent(
                                        SystemClock.elapsedRealtime(), suggestion);
                                        SystemClock.elapsedRealtime(), suggestion, providerStatus);
                        if (shouldSendEvent(thisEvent)) {
                            manager.onTimeZoneProviderEvent(thisEvent);
                            mLastEventSent = thisEvent;
@@ -231,6 +245,21 @@ public abstract class TimeZoneProviderService extends Service {
     * to a time zone.
     */
    public final void reportUncertain() {
        reportUncertain(TimeZoneProviderStatus.UNKNOWN);
    }

    /**
     * Indicates the time zone is not known because of an expected runtime state or error.
     *
     * <p>When the status changes then a certain or uncertain report must be made to move the
     * detector service to the new status.
     *
     * @param providerStatus provider status information that can influence detector service
     *   behavior and/or be reported via the device UI
     *
     * @hide
     */
    public final void reportUncertain(@NonNull TimeZoneProviderStatus providerStatus) {
        mHandler.post(() -> {
            synchronized (mLock) {
                ITimeZoneProviderManager manager = mManager;
@@ -238,7 +267,7 @@ public abstract class TimeZoneProviderService extends Service {
                    try {
                        TimeZoneProviderEvent thisEvent =
                                TimeZoneProviderEvent.createUncertainEvent(
                                        SystemClock.elapsedRealtime());
                                        SystemClock.elapsedRealtime(), providerStatus);
                        if (shouldSendEvent(thisEvent)) {
                            manager.onTimeZoneProviderEvent(thisEvent);
                            mLastEventSent = thisEvent;
+22 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022, The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.timezone;

/**
 * @hide
 */
parcelable TimeZoneProviderStatus;
+336 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.service.timezone;

import android.annotation.IntDef;
import android.annotation.NonNull;
import android.os.Parcel;
import android.os.Parcelable;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.Objects;

/**
 * Information about the status of a {@link TimeZoneProviderService}.
 *
 * <p>Not all status properties or status values will apply to all provider implementations.
 * {@code _NOT_APPLICABLE} status can be used to indicate properties that have no meaning for a
 * given implementation.
 *
 * <p>Time zone providers are expected to work in one of two ways:
 * <ol>
 *     <li>Location: Providers will determine location and then map that location to one or more
 *     time zone IDs.</li>
 *     <li>External signals: Providers could use indirect signals like country code
 *     and/or local offset / DST information provided to the device to infer a time zone, e.g.
 *     signals like MCC and NITZ for telephony devices, IP geo location, or DHCP information
 *     (RFC4833). The time zone ID could also be fed directly to the device by an external service.
 *     </li>
 * </ol>
 *
 * <p>The status properties are:
 * <ul>
 *     <li>location detection - for location-based providers, the status of the location detection
 *     mechanism</li>
 *     <li>connectivity - connectivity can influence providers directly, for example if they use
 *     a networked service to map location to time zone ID, or use geo IP, or indirectly for
 *     location detection (e.g. for the network location provider.</li>
 *     <li>time zone resolution - the status related to determining a time zone ID or using a
 *     detected time zone ID. For example, a networked service may be reachable (i.e. connectivity
 *     is working) but the service could return errors, a time zone ID detected may not be usable
 *     for a device because of TZDB version skew, or external indirect signals may available but
 *     do not match the properties of a known time zone ID.</li>
 * </ul>
 *
 * @hide
 */
public final class TimeZoneProviderStatus implements Parcelable {

    /**
     * A status code related to a dependency a provider may have.
     *
     * @hide
     */
    @IntDef(prefix = "DEPENDENCY_STATUS_", value = {
            DEPENDENCY_STATUS_UNKNOWN,
            DEPENDENCY_STATUS_NOT_APPLICABLE,
            DEPENDENCY_STATUS_WORKING,
            DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE,
            DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT,
            DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS,
            DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS,
    })
    @Target(ElementType.TYPE_USE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface DependencyStatus {}

    /** The dependency's status is unknown. */
    public static final @DependencyStatus int DEPENDENCY_STATUS_UNKNOWN = 0;

    /** The dependency is not used by the provider's implementation. */
    public static final @DependencyStatus int DEPENDENCY_STATUS_NOT_APPLICABLE = 1;

    /** The dependency is applicable and working well. */
    public static final @DependencyStatus int DEPENDENCY_STATUS_WORKING = 2;

    /**
     * The dependency is used but is temporarily unavailable, e.g. connectivity has been lost for an
     * unpredictable amount of time.
     *
     * <p>This status is considered normal is may be entered many times a day.
     */
    public static final @DependencyStatus int DEPENDENCY_STATUS_TEMPORARILY_UNAVAILABLE = 3;

    /**
     * The dependency is used by the provider but is blocked by the environment in a way that the
     * provider has detected and is considered likely to persist for some time, e.g. connectivity
     * has been lost due to boarding a plane.
     *
     * <p>This status is considered unusual and could be used by the system as a trigger to try
     * other time zone providers / time zone detection mechanisms. The bar for using this status
     * should therefore be set fairly high to avoid a device bringing up other providers or
     * switching to a different detection mechanism that may provide a different suggestion.
     */
    public static final @DependencyStatus int DEPENDENCY_STATUS_BLOCKED_BY_ENVIRONMENT = 4;

    /**
     * The dependency is used by the provider but is running in a degraded mode due to the user's
     * settings. A user can take action to improve this, e.g. by changing a setting.
     *
     * <p>This status could be used by the system as a trigger to try other time zone
     * providers / time zone detection mechanisms. The user may be informed.
     */
    public static final @DependencyStatus int DEPENDENCY_STATUS_DEGRADED_BY_SETTINGS = 5;

    /**
     * The dependency is used by the provider but is completely blocked by the user's settings.
     * A user can take action to correct this, e.g. by changing a setting.
     *
     * <p>This status could be used by the system as a trigger to try other time zone providers /
     * time zone detection mechanisms. The user may be informed.
     */
    public static final @DependencyStatus int DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS = 6;

    /**
     * A status code related to an operation in a provider's detection algorithm.
     *
     * @hide
     */
    @IntDef(prefix = "OPERATION_STATUS_", value = {
            OPERATION_STATUS_UNKNOWN,
            OPERATION_STATUS_NOT_APPLICABLE,
            OPERATION_STATUS_WORKING,
            OPERATION_STATUS_FAILED,
    })
    @Target(ElementType.TYPE_USE)
    @Retention(RetentionPolicy.SOURCE)
    public @interface OperationStatus {}

    /** The operation's status is unknown. */
    public static final @OperationStatus int OPERATION_STATUS_UNKNOWN = 0;

    /** The operation is not used by the provider's implementation. */
    public static final @OperationStatus int OPERATION_STATUS_NOT_APPLICABLE = 1;

    /** The operation is applicable and working well. */
    public static final @OperationStatus int OPERATION_STATUS_WORKING = 2;

    /** The operation is applicable and failed. */
    public static final @OperationStatus int OPERATION_STATUS_FAILED = 3;

    /**
     * An instance that provides no information about status. Effectively a "null" status.
     */
    @NonNull
    public static final TimeZoneProviderStatus UNKNOWN = new TimeZoneProviderStatus(
            DEPENDENCY_STATUS_UNKNOWN, DEPENDENCY_STATUS_UNKNOWN, OPERATION_STATUS_UNKNOWN);

    private final @DependencyStatus int mLocationDetectionStatus;
    private final @DependencyStatus int mConnectivityStatus;
    private final @OperationStatus int mTimeZoneResolutionStatus;

    private TimeZoneProviderStatus(
            @DependencyStatus int locationDetectionStatus,
            @DependencyStatus int connectivityStatus,
            @OperationStatus int timeZoneResolutionStatus) {
        mLocationDetectionStatus = requireValidDependencyStatus(locationDetectionStatus);
        mConnectivityStatus = requireValidDependencyStatus(connectivityStatus);
        mTimeZoneResolutionStatus = requireValidOperationStatus(timeZoneResolutionStatus);
    }

    /**
     * Returns the status of the location detection dependencies used by the provider (where
     * applicable).
     */
    public @DependencyStatus int getLocationDetectionStatus() {
        return mLocationDetectionStatus;
    }

    /**
     * Returns the status of the connectivity dependencies used by the provider (where applicable).
     */
    public @DependencyStatus int getConnectivityStatus() {
        return mConnectivityStatus;
    }

    /**
     * Returns the status of the time zone resolution operation used by the provider.
     */
    public @OperationStatus int getTimeZoneResolutionStatus() {
        return mTimeZoneResolutionStatus;
    }

    @Override
    public String toString() {
        return "TimeZoneProviderStatus{"
                + "mLocationDetectionStatus=" + mLocationDetectionStatus
                + ", mConnectivityStatus=" + mConnectivityStatus
                + ", mTimeZoneResolutionStatus=" + mTimeZoneResolutionStatus
                + '}';
    }

    public static final @NonNull Creator<TimeZoneProviderStatus> CREATOR = new Creator<>() {
        @Override
        public TimeZoneProviderStatus createFromParcel(Parcel in) {
            @DependencyStatus int locationDetectionStatus = in.readInt();
            @DependencyStatus int connectivityStatus = in.readInt();
            @OperationStatus int timeZoneResolutionStatus = in.readInt();
            return new TimeZoneProviderStatus(
                    locationDetectionStatus, connectivityStatus, timeZoneResolutionStatus);
        }

        @Override
        public TimeZoneProviderStatus[] newArray(int size) {
            return new TimeZoneProviderStatus[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(@NonNull Parcel parcel, int flags) {
        parcel.writeInt(mLocationDetectionStatus);
        parcel.writeInt(mConnectivityStatus);
        parcel.writeInt(mTimeZoneResolutionStatus);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        TimeZoneProviderStatus that = (TimeZoneProviderStatus) o;
        return mLocationDetectionStatus == that.mLocationDetectionStatus
                && mConnectivityStatus == that.mConnectivityStatus
                && mTimeZoneResolutionStatus == that.mTimeZoneResolutionStatus;
    }

    @Override
    public int hashCode() {
        return Objects.hash(
                mLocationDetectionStatus, mConnectivityStatus, mTimeZoneResolutionStatus);
    }

    /** A builder for {@link TimeZoneProviderStatus}. */
    public static final class Builder {

        private @DependencyStatus int mLocationDetectionStatus = DEPENDENCY_STATUS_UNKNOWN;
        private @DependencyStatus int mConnectivityStatus = DEPENDENCY_STATUS_UNKNOWN;
        private @OperationStatus int mTimeZoneResolutionStatus = OPERATION_STATUS_UNKNOWN;

        /**
         * Creates a new builder instance. At creation time all status properties are set to
         * their "UNKNOWN" value.
         */
        public Builder() {
        }

        /**
         * @hide
         */
        public Builder(TimeZoneProviderStatus toCopy) {
            mLocationDetectionStatus = toCopy.mLocationDetectionStatus;
            mConnectivityStatus = toCopy.mConnectivityStatus;
            mTimeZoneResolutionStatus = toCopy.mTimeZoneResolutionStatus;
        }

        /**
         * Sets the status of the provider's location detection dependency (where applicable).
         * See the {@code DEPENDENCY_STATUS_} constants for more information.
         */
        @NonNull
        public Builder setLocationDetectionStatus(@DependencyStatus int locationDetectionStatus) {
            mLocationDetectionStatus = locationDetectionStatus;
            return this;
        }

        /**
         * Sets the status of the provider's connectivity dependency (where applicable).
         * See the {@code DEPENDENCY_STATUS_} constants for more information.
         */
        @NonNull
        public Builder setConnectivityStatus(@DependencyStatus int connectivityStatus) {
            mConnectivityStatus = connectivityStatus;
            return this;
        }

        /**
         * Sets the status of the provider's time zone resolution operation.
         * See the {@code OPERATION_STATUS_} constants for more information.
         */
        @NonNull
        public Builder setTimeZoneResolutionStatus(@OperationStatus int timeZoneResolutionStatus) {
            mTimeZoneResolutionStatus = timeZoneResolutionStatus;
            return this;
        }

        /**
         * Builds a {@link TimeZoneProviderStatus} instance.
         */
        @NonNull
        public TimeZoneProviderStatus build() {
            return new TimeZoneProviderStatus(
                    mLocationDetectionStatus, mConnectivityStatus, mTimeZoneResolutionStatus);
        }
    }

    private @OperationStatus int requireValidOperationStatus(@OperationStatus int operationStatus) {
        if (operationStatus < OPERATION_STATUS_UNKNOWN
                || operationStatus > OPERATION_STATUS_FAILED) {
            throw new IllegalArgumentException(Integer.toString(operationStatus));
        }
        return operationStatus;
    }

    private static @DependencyStatus int requireValidDependencyStatus(
            @DependencyStatus int dependencyStatus) {
        if (dependencyStatus < DEPENDENCY_STATUS_UNKNOWN
                || dependencyStatus > DEPENDENCY_STATUS_BLOCKED_BY_SETTINGS) {
            throw new IllegalArgumentException(Integer.toString(dependencyStatus));
        }
        return dependencyStatus;
    }
}
+83 −19

File changed.

Preview size limit exceeded, changes collapsed.

Loading