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

Commit 06f95f23 authored by Thomas Nguyen's avatar Thomas Nguyen
Browse files

Guard new satellite APIs with feature flag

Bug: 296909779
Test: SMS, MMS, call with live network.
atest SatelliteControllerTest
atest SatelliteManagerTestOnMockService

Change-Id: I797db0df194ec9732db16f3377c0b2e5601c8f23
parent 5722cd9a
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -42905,7 +42905,7 @@ package android.telephony {
    field public static final String KEY_CARRIER_SERVICE_NUMBER_STRING_ARRAY = "carrier_service_number_array";
    field public static final String KEY_CARRIER_SETTINGS_ACTIVITY_COMPONENT_NAME_STRING = "carrier_settings_activity_component_name_string";
    field public static final String KEY_CARRIER_SETTINGS_ENABLE_BOOL = "carrier_settings_enable_bool";
    field public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
    field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE = "carrier_supported_satellite_services_per_provider_bundle";
    field public static final String KEY_CARRIER_SUPPORTS_OPP_DATA_AUTO_PROVISIONING_BOOL = "carrier_supports_opp_data_auto_provisioning_bool";
    field public static final String KEY_CARRIER_SUPPORTS_SS_OVER_UT_BOOL = "carrier_supports_ss_over_ut_bool";
    field public static final String KEY_CARRIER_SUPPORTS_TETHERING_BOOL = "carrier_supports_tethering_bool";
@@ -43073,6 +43073,7 @@ package android.telephony {
    field public static final String KEY_RTT_SUPPORTED_WHILE_ROAMING_BOOL = "rtt_supported_while_roaming_bool";
    field public static final String KEY_RTT_UPGRADE_SUPPORTED_BOOL = "rtt_upgrade_supported_bool";
    field public static final String KEY_RTT_UPGRADE_SUPPORTED_FOR_DOWNGRADED_VT_CALL_BOOL = "rtt_upgrade_supported_for_downgraded_vt_call";
    field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL = "satellite_attach_supported_bool";
    field public static final String KEY_SHOW_4G_FOR_3G_DATA_ICON_BOOL = "show_4g_for_3g_data_icon_bool";
    field public static final String KEY_SHOW_4G_FOR_LTE_DATA_ICON_BOOL = "show_4g_for_lte_data_icon_bool";
    field public static final String KEY_SHOW_APN_SETTING_CDMA_BOOL = "show_apn_setting_cdma_bool";
+6 −0
Original line number Diff line number Diff line
@@ -16731,17 +16731,22 @@ package android.telephony.satellite {
  }
  public final class SatelliteManager {
    method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void addSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void deprovisionSatelliteService(@NonNull String, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @NonNull @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public java.util.Set<java.lang.Integer> getSatelliteAttachRestrictionReasonsForCarrier(int);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void pollPendingSatelliteDatagrams(@NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void provisionSatelliteService(@NonNull String, @NonNull byte[], @Nullable android.os.CancellationSignal, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteDatagram(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteDatagramCallback);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteModemStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteStateCallback);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public int registerForSatelliteProvisionStateChanged(@NonNull java.util.concurrent.Executor, @NonNull android.telephony.satellite.SatelliteProvisionStateCallback);
    method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void removeSatelliteAttachRestrictionForCarrier(int, int, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsDemoModeEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteAttachEnabledForCarrier(int, @NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteCommunicationAllowedForCurrentLocation(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteEnabled(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestIsSatelliteProvisioned(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method public void requestIsSatelliteSupported(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.lang.Boolean,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteAttachEnabledForCarrier(int, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteCapabilities(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<android.telephony.satellite.SatelliteCapabilities,android.telephony.satellite.SatelliteManager.SatelliteException>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestSatelliteEnabled(boolean, boolean, @NonNull java.util.concurrent.Executor, @NonNull java.util.function.Consumer<java.lang.Integer>);
    method @RequiresPermission(android.Manifest.permission.SATELLITE_COMMUNICATION) public void requestTimeForNextSatelliteVisibility(@NonNull java.util.concurrent.Executor, @NonNull android.os.OutcomeReceiver<java.time.Duration,android.telephony.satellite.SatelliteManager.SatelliteException>);
@@ -16768,6 +16773,7 @@ package android.telephony.satellite {
    field public static final int NT_RADIO_TECHNOLOGY_NR_NTN = 2; // 0x2
    field public static final int NT_RADIO_TECHNOLOGY_PROPRIETARY = 4; // 0x4
    field public static final int NT_RADIO_TECHNOLOGY_UNKNOWN = 0; // 0x0
    field @FlaggedApi("com.android.internal.telephony.flags.carrier_enabled_satellite_flag") public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1; // 0x1
    field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_IDLE = 0; // 0x0
    field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_FAILED = 7; // 0x7
    field public static final int SATELLITE_DATAGRAM_TRANSFER_STATE_RECEIVE_NONE = 6; // 0x6
+10 −21
Original line number Diff line number Diff line
@@ -172,28 +172,17 @@
    <integer name="config_satellite_nb_iot_inactivity_timeout_millis">180000</integer>
    <java-symbol type="integer" name="config_satellite_nb_iot_inactivity_timeout_millis" />

    <!-- Telephony config for services supported by satellite providers. The format of each config
         string in the array is as follows: "PLMN_1:service_1,service_2,..."
         where PLMN is the satellite PLMN of a provider and service is an integer with the
         following value:
            1 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VOICE}
            2 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_DATA}
            3 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_SMS}
            4 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_VIDEO}
            5 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_EMERGENCY}
            6 = {@link android.telephony.NetworkRegistrationInfo#SERVICE_TYPE_MMS}
         Example of a config string: "10011:2,3"

         The PLMNs not configured in this array will be ignored and will not be used for satellite
         scanning. -->
    <string-array name="config_satellite_services_supported_by_providers" translatable="false">
    </string-array>
    <java-symbol type="array" name="config_satellite_services_supported_by_providers" />
    <!-- Telephony config for the PLMNs of all satellite providers. This is used by satellite modem
         to identify providers that should be ignored if the carrier config
         carrier_supported_satellite_services_per_provider_bundle does not support them.
         -->
    <string-array name="config_satellite_providers" translatable="false"></string-array>
    <java-symbol type="array" name="config_satellite_providers" />

    <!-- The identifier of the satellite's eSIM profile preloaded on the device. The identifier is
    composed of MCC and MNC of the satellite PLMN with the format "mccmnc". -->
    <string name="config_satellite_esim_identifier" translatable="false"></string>
    <java-symbol type="string" name="config_satellite_esim_identifier" />
    <!-- The identifier of the satellite's SIM profile. The identifier is composed of MCC and MNC
         of the satellite PLMN with the format "mccmnc". -->
    <string name="config_satellite_sim_identifier" translatable="false"></string>
    <java-symbol type="string" name="config_satellite_sim_identifier" />

    <!-- Whether enhanced IWLAN handover check is enabled. If enabled, telephony frameworks
         will not perform handover if the target transport is out of service, or VoPS not
+4 −16
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.telephony;

import android.Manifest;
import android.annotation.CallbackExecutor;
import android.annotation.FlaggedApi;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -52,6 +53,7 @@ import android.telephony.ims.feature.MmTelFeature;
import android.telephony.ims.feature.RcsFeature;

import com.android.internal.telephony.ICarrierConfigLoader;
import com.android.internal.telephony.flags.Flags;
import com.android.telephony.Rlog;

import java.util.List;
@@ -9436,22 +9438,9 @@ public class CarrierConfigManager {
     * </carrier_config>
     * }</pre>
     * <p>
     * If this carrier config is not present, the device overlay config
     * {@code config_satellite_services_supported_by_providers} will be used. If the carrier config
     * is present, the supported services associated with the PLMNs listed in the carrier config
     * will override that of the device overlay config. The supported satellite services will be
     * identified as follows:
     * <ul>
     * <li>For each PLMN that exists only in the carrier provided satellite services, use the
     * carrier provided services as the supported services.</li>
     * <li>For each PLMN that is present only in the device provided satellite services, use the
     * device provided services as the supported services.</li>
     * <li>For each PLMN that is present in both the carrier provided and device provided satellite
     * services, use the carrier provided services as the supported services.</li>
     * </ul>
     * <p>
     * This config is empty by default.
     */
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public static final String KEY_CARRIER_SUPPORTED_SATELLITE_SERVICES_PER_PROVIDER_BUNDLE =
            "carrier_supported_satellite_services_per_provider_bundle";

@@ -9462,9 +9451,8 @@ public class CarrierConfigManager {
     * satellite provider and the carrier before enabling this flag.
     *
     * The default value is false.
     *
     * @hide
     */
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public static final String KEY_SATELLITE_ATTACH_SUPPORTED_BOOL =
            "satellite_attach_supported_bool";

+42 −38
Original line number Diff line number Diff line
@@ -84,30 +84,12 @@ public final class SatelliteManager {
     */
    @Nullable private final Context mContext;

    /**
     * Create a new SatelliteManager object pinned to the given subscription ID.
     * This is needed only to handle carrier specific satellite features.
     * For eg: requestSatelliteAttachEnabledForCarrier and
     *         requestIsSatelliteAttachEnabledForCarrier
     *
     * @return a SatelliteManager that uses the given subId for all satellite activities.
     * @throws IllegalArgumentException if the subscription is invalid.
     * @hide
     */
    public SatelliteManager createForSubscriptionId(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new IllegalArgumentException("Invalid subscription ID");
        }
        return new SatelliteManager(mContext, subId);
    }

    /**
     * Create an instance of the SatelliteManager.
     *
     * @param context The context the SatelliteManager belongs to.
     * @hide
     */

    public SatelliteManager(@Nullable Context context) {
        this(context, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID);
    }
@@ -846,8 +828,8 @@ public final class SatelliteManager {
    /**
     * Satellite communication restricted by geolocation. This can be
     * triggered based upon geofence input provided by carrier to enable or disable satellite.
     * @hide
     */
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public static final int SATELLITE_COMMUNICATION_RESTRICTION_REASON_GEOLOCATION = 1;

    /** @hide */
@@ -1643,26 +1625,28 @@ public final class SatelliteManager {
     * {@code true}.</li>
     * </ul>
     *
     * @param subId The subscription ID of the carrier.
     * @param enableSatellite {@code true} to enable the satellite and {@code false} to disable.
     * @param executor The executor on which the error code listener will be called.
     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
     *
     * @throws SecurityException if the caller doesn't have required permission.
     * @throws IllegalStateException if the Telephony process is not currently available.
     * @hide
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
    public void requestSatelliteAttachEnabledForCarrier(boolean enableSatellite,
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public void requestSatelliteAttachEnabledForCarrier(int subId, boolean enableSatellite,
            @NonNull @CallbackExecutor Executor executor,
            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(resultListener);

        if (enableSatellite) {
            removeSatelliteAttachRestrictionForCarrier(
            removeSatelliteAttachRestrictionForCarrier(subId,
                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
        } else {
            addSatelliteAttachRestrictionForCarrier(
            addSatelliteAttachRestrictionForCarrier(subId,
                    SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER, executor, resultListener);
        }
    }
@@ -1671,6 +1655,7 @@ public final class SatelliteManager {
     * Request to get whether the carrier supported satellite plmn scan and attach by modem is
     * enabled by user.
     *
     * @param subId The subscription ID of the carrier.
     * @param executor The executor on which the callback will be called.
     * @param callback The callback object to which the result will be delivered.
     *                 If the request is successful, {@link OutcomeReceiver#onResult(Object)}
@@ -1681,16 +1666,17 @@ public final class SatelliteManager {
     *
     * @throws SecurityException if the caller doesn't have required permission.
     * @throws IllegalStateException if the Telephony process is not currently available.
     * @hide
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
    public void requestIsSatelliteAttachEnabledForCarrier(
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public void requestIsSatelliteAttachEnabledForCarrier(int subId,
            @NonNull @CallbackExecutor Executor executor,
            @NonNull OutcomeReceiver<Boolean, SatelliteException> callback) {
        Objects.requireNonNull(executor);
        Objects.requireNonNull(callback);

        Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier();
        Set<Integer> restrictionReason = getSatelliteAttachRestrictionReasonsForCarrier(subId);
        executor.execute(() -> callback.onResult(
                !restrictionReason.contains(SATELLITE_COMMUNICATION_RESTRICTION_REASON_USER)));
    }
@@ -1699,19 +1685,25 @@ public final class SatelliteManager {
     * Add a restriction reason for disallowing carrier supported satellite plmn scan and attach
     * by modem.
     *
     * @param subId The subscription ID of the carrier.
     * @param reason Reason for disallowing satellite communication.
     * @param executor The executor on which the error code listener will be called.
     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
     *
     * @throws SecurityException if the caller doesn't have required permission.
     * @throws IllegalStateException if the Telephony process is not currently available.
     * @hide
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
    public void addSatelliteAttachRestrictionForCarrier(
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public void addSatelliteAttachRestrictionForCarrier(int subId,
            @SatelliteCommunicationRestrictionReason int reason,
            @NonNull @CallbackExecutor Executor executor,
            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new IllegalArgumentException("Invalid subscription ID");
        }

        try {
            ITelephony telephony = getITelephony();
            if (telephony != null) {
@@ -1722,7 +1714,7 @@ public final class SatelliteManager {
                                () -> resultListener.accept(result)));
                    }
                };
                telephony.addSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
                telephony.addSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
            } else {
                throw new IllegalStateException("telephony service is null.");
            }
@@ -1736,19 +1728,25 @@ public final class SatelliteManager {
     * Remove a restriction reason for disallowing carrier supported satellite plmn scan and attach
     * by modem.
     *
     * @param subId The subscription ID of the carrier.
     * @param reason Reason for disallowing satellite communication.
     * @param executor The executor on which the error code listener will be called.
     * @param resultListener Listener for the {@link SatelliteError} result of the operation.
     *
     * @throws SecurityException if the caller doesn't have required permission.
     * @throws IllegalStateException if the Telephony process is not currently available.
     * @hide
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
    public void removeSatelliteAttachRestrictionForCarrier(
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    public void removeSatelliteAttachRestrictionForCarrier(int subId,
            @SatelliteCommunicationRestrictionReason int reason,
            @NonNull @CallbackExecutor Executor executor,
            @SatelliteResult @NonNull Consumer<Integer> resultListener) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new IllegalArgumentException("Invalid subscription ID");
        }

        try {
            ITelephony telephony = getITelephony();
            if (telephony != null) {
@@ -1759,7 +1757,7 @@ public final class SatelliteManager {
                                () -> resultListener.accept(result)));
                    }
                };
                telephony.removeSatelliteAttachRestrictionForCarrier(mSubId, reason, errorCallback);
                telephony.removeSatelliteAttachRestrictionForCarrier(subId, reason, errorCallback);
            } else {
                throw new IllegalStateException("telephony service is null.");
            }
@@ -1773,34 +1771,40 @@ public final class SatelliteManager {
     * Get reasons for disallowing satellite attach, as requested by
     * {@link #addSatelliteAttachRestrictionForCarrier(int, Executor, Consumer)}
     *
     * @param subId The subscription ID of the carrier.
     * @return Set of reasons for disallowing satellite communication.
     *
     * @throws SecurityException if the caller doesn't have required permission.
     * @throws IllegalStateException if the Telephony process is not currently available.
     * @hide
     * @throws IllegalArgumentException if the subscription is invalid.
     */
    @RequiresPermission(Manifest.permission.SATELLITE_COMMUNICATION)
    @SatelliteCommunicationRestrictionReason
    public @NonNull Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier() {
    @FlaggedApi(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG)
    @NonNull public Set<Integer> getSatelliteAttachRestrictionReasonsForCarrier(int subId) {
        if (!SubscriptionManager.isValidSubscriptionId(subId)) {
            throw new IllegalArgumentException("Invalid subscription ID");
        }

        try {
            ITelephony telephony = getITelephony();
            if (telephony != null) {
                int[] receivedArray =
                        telephony.getSatelliteAttachRestrictionReasonsForCarrier(mSubId);
                        telephony.getSatelliteAttachRestrictionReasonsForCarrier(subId);
                if (receivedArray.length == 0) {
                    logd("received set is empty, create empty set");
                    logd("receivedArray is empty, create empty set");
                    return new HashSet<>();
                } else {
                    return Arrays.stream(receivedArray).boxed().collect(Collectors.toSet());
                }
            } else {
                throw new IllegalStateException("telephony service is null.");
                throw new IllegalStateException("Telephony service is null.");
            }
        } catch (RemoteException ex) {
            loge("getSatelliteAttachRestrictionReasonsForCarrier() RemoteException: " + ex);
            ex.rethrowFromSystemServer();
        }
        return null;
        return new HashSet<>();
    }

    private static ITelephony getITelephony() {
Loading