Loading core/api/test-current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -909,6 +909,14 @@ package android.companion { method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long); } public final class AssociationRequest implements android.os.Parcelable { method public boolean isSkipRoleGrant(); } public static final class AssociationRequest.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public android.companion.AssociationRequest.Builder setSkipRoleGrant(boolean); } public final class CompanionDeviceManager { method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean); } Loading core/java/android/companion/AssociationRequest.java +53 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.companion; import static android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES; import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED; import static com.android.internal.util.CollectionUtils.emptyIfNull; Loading @@ -28,7 +29,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.drawable.Icon; import android.os.Build; Loading Loading @@ -213,6 +217,11 @@ public final class AssociationRequest implements Parcelable { */ private final boolean mForceConfirmation; /** * Whether to skip the role grant, permission checks and consent dialog. */ private final boolean mSkipRoleGrant; /** * The app package name of the application the association will belong to. * Populated by the system. Loading Loading @@ -283,6 +292,7 @@ public final class AssociationRequest implements Parcelable { @Nullable CharSequence displayName, boolean selfManaged, boolean forceConfirmation, boolean skipRoleGrant, @Nullable Icon deviceIcon) { mSingleDevice = singleDevice; mDeviceFilters = requireNonNull(deviceFilters); Loading @@ -290,6 +300,7 @@ public final class AssociationRequest implements Parcelable { mDisplayName = displayName; mSelfManaged = selfManaged; mForceConfirmation = forceConfirmation; mSkipRoleGrant = skipRoleGrant; mCreationTime = System.currentTimeMillis(); mDeviceIcon = deviceIcon; } Loading Loading @@ -332,6 +343,18 @@ public final class AssociationRequest implements Parcelable { return mForceConfirmation; } /** * Whether to skip the role grant, permission checks and consent dialog. * * @see Builder#setSkipRoleGrant(boolean) * @hide */ @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi public boolean isSkipRoleGrant() { return mSkipRoleGrant; } /** * Whether only a single device should match the provided filter. * Loading Loading @@ -407,6 +430,7 @@ public final class AssociationRequest implements Parcelable { private CharSequence mDisplayName; private boolean mSelfManaged = false; private boolean mForceConfirmation = false; private boolean mSkipRoleGrant = false; private Icon mDeviceIcon = null; public Builder() {} Loading Loading @@ -493,6 +517,27 @@ public final class AssociationRequest implements Parcelable { return this; } /** * Do not attempt to grant the role corresponding to the device profile. * * <p>This will skip the permission checks and consent dialog but will not fail if the * role cannot be granted.</p> * * <p>Requires that the device not to have secure lock screen and that there no locked SIM * card. See {@link KeyguardManager#isKeyguardSecure()}</p> * * @hide */ @RequiresPermission(ASSOCIATE_COMPANION_DEVICES) @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi @NonNull public Builder setSkipRoleGrant(boolean skipRoleGrant) { checkNotUsed(); mSkipRoleGrant = skipRoleGrant; return this; } /** * Set the device icon for the self-managed device and to display the icon in the * self-managed association dialog. Loading Loading @@ -521,7 +566,8 @@ public final class AssociationRequest implements Parcelable { + "provide the display name of the device"); } return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters), mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mDeviceIcon); mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mSkipRoleGrant, mDeviceIcon); } } Loading Loading @@ -597,6 +643,7 @@ public final class AssociationRequest implements Parcelable { + ", associatedDevice = " + mAssociatedDevice + ", selfManaged = " + mSelfManaged + ", forceConfirmation = " + mForceConfirmation + ", skipRoleGrant = " + mSkipRoleGrant + ", packageName = " + mPackageName + ", userId = " + mUserId + ", deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription Loading @@ -617,6 +664,7 @@ public final class AssociationRequest implements Parcelable { && Objects.equals(mAssociatedDevice, that.mAssociatedDevice) && mSelfManaged == that.mSelfManaged && mForceConfirmation == that.mForceConfirmation && mSkipRoleGrant == that.mSkipRoleGrant && Objects.equals(mPackageName, that.mPackageName) && mUserId == that.mUserId && Objects.equals(mDeviceProfilePrivilegesDescription, Loading @@ -637,6 +685,7 @@ public final class AssociationRequest implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mAssociatedDevice); _hash = 31 * _hash + Boolean.hashCode(mSelfManaged); _hash = 31 * _hash + Boolean.hashCode(mForceConfirmation); _hash = 31 * _hash + Boolean.hashCode(mSkipRoleGrant); _hash = 31 * _hash + Objects.hashCode(mPackageName); _hash = 31 * _hash + mUserId; _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); Loading @@ -659,6 +708,7 @@ public final class AssociationRequest implements Parcelable { if (mAssociatedDevice != null) flg |= 0x40; if (mPackageName != null) flg |= 0x80; if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100; if (mSkipRoleGrant) flg |= 0x200; dest.writeInt(flg); dest.writeParcelableList(mDeviceFilters, flags); Loading Loading @@ -692,6 +742,7 @@ public final class AssociationRequest implements Parcelable { boolean selfManaged = (flg & 0x2) != 0; boolean forceConfirmation = (flg & 0x4) != 0; boolean skipPrompt = (flg & 0x8) != 0; boolean skipRoleGrant = (flg & 0x200) != 0; List<DeviceFilter<?>> deviceFilters = new ArrayList<>(); in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class<android.companion.DeviceFilter<?>>) (Class<?>) Loading @@ -714,6 +765,7 @@ public final class AssociationRequest implements Parcelable { this.mAssociatedDevice = associatedDevice; this.mSelfManaged = selfManaged; this.mForceConfirmation = forceConfirmation; this.mSkipRoleGrant = skipRoleGrant; this.mPackageName = packageName; this.mUserId = userId; com.android.internal.util.AnnotationValidations.validate( Loading services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +26 −3 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ecm.EnhancedConfirmationManager; Loading Loading @@ -302,9 +303,18 @@ public class CompanionDeviceManagerService extends SystemService { enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, "create associations"); if (request.isSkipRoleGrant()) { checkCallerCanSkipRoleGrant(); mAssociationRequestsProcessor.createAssociation(userId, packageName, /* macAddress= */ null, request.getDisplayName(), request.getDeviceProfile(), /* associatedDevice= */ null, request.isSelfManaged(), callback, /* resultReceiver= */ null, request.getDeviceIcon(), /* skipRoleGrant= */ true); } else { mAssociationRequestsProcessor.processNewAssociationRequest( request, packageName, userId, callback); } } @Override public PendingIntent buildAssociationCancellationIntent(String packageName, Loading Loading @@ -669,7 +679,7 @@ public class CompanionDeviceManagerService extends SystemService { final MacAddress macAddressObj = MacAddress.fromString(macAddress); mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddressObj, null, null, null, false, null, null, null); null, null, null, false, null, null, null, false); } private void checkCanCallNotificationApi(String callingPackage, int userId) { Loading @@ -684,6 +694,19 @@ public class CompanionDeviceManagerService extends SystemService { "App must have an association before calling this API"); } private void checkCallerCanSkipRoleGrant() { final KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class); if (keyguardManager != null && keyguardManager.isKeyguardSecure()) { throw new SecurityException("Skipping CDM role grant requires insecure keyguard."); } if (getContext().checkCallingPermission(ASSOCIATE_COMPANION_DEVICES) != PERMISSION_GRANTED) { throw new SecurityException( "Skipping CDM role grant requires ASSOCIATE_COMPANION_DEVICES permission."); } } @Override public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) { final AssociationInfo association = Loading services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +3 −2 Original line number Diff line number Diff line Loading @@ -106,8 +106,9 @@ class CompanionDeviceShellCommand extends ShellCommand { boolean selfManaged = getNextBooleanArg(); final MacAddress macAddress = MacAddress.fromString(address); mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress, deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged, /* callback */ null, /* resultReceiver */ null, /* deviceIcon */ null); deviceProfile, deviceProfile, /* associatedDevice= */ null, selfManaged, /* callback= */ null, /* resultReceiver= */ null, /* deviceIcon= */ null, /* skipRoleGrant= */ false); } break; Loading services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java +15 −5 Original line number Diff line number Diff line Loading @@ -282,8 +282,8 @@ public class AssociationRequestsProcessor { Binder.withCleanCallingIdentity(() -> { createAssociation(userId, packageName, macAddress, request.getDisplayName(), request.getDeviceProfile(), request.getAssociatedDevice(), request.isSelfManaged(), callback, resultReceiver, request.getDeviceIcon()); request.isSelfManaged(), callback, resultReceiver, request.getDeviceIcon(), /* skipRoleGrant= */ false); }); } Loading @@ -294,7 +294,8 @@ public class AssociationRequestsProcessor { @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, boolean selfManaged, @Nullable IAssociationRequestCallback callback, @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon) { @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon, boolean skipRoleGrant) { final int id = mAssociationStore.getNextId(); final long timestamp = System.currentTimeMillis(); Loading @@ -303,9 +304,18 @@ public class AssociationRequestsProcessor { selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false, /* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, deviceIcon, /* deviceId */ null); if (skipRoleGrant) { Slog.i(TAG, "Created association for " + association.getDeviceProfile() + " and userId=" + association.getUserId() + ", packageName=" + association.getPackageName() + " without granting role"); mAssociationStore.addAssociation(association); sendCallbackAndFinish(association, callback, resultReceiver); } else { // Add role holder for association (if specified) and add new association to store. maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver); } } /** * Grant a role if specified and add an association to store. Loading Loading
core/api/test-current.txt +8 −0 Original line number Diff line number Diff line Loading @@ -909,6 +909,14 @@ package android.companion { method @NonNull public android.companion.AssociationInfo.Builder setTimeApproved(long); } public final class AssociationRequest implements android.os.Parcelable { method public boolean isSkipRoleGrant(); } public static final class AssociationRequest.Builder { method @NonNull @RequiresPermission(android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES) public android.companion.AssociationRequest.Builder setSkipRoleGrant(boolean); } public final class CompanionDeviceManager { method @RequiresPermission("android.permission.MANAGE_COMPANION_DEVICES") public void enableSecureTransport(boolean); } Loading
core/java/android/companion/AssociationRequest.java +53 −1 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package android.companion; import static android.Manifest.permission.ASSOCIATE_COMPANION_DEVICES; import static android.Manifest.permission.REQUEST_COMPANION_SELF_MANAGED; import static com.android.internal.util.CollectionUtils.emptyIfNull; Loading @@ -28,7 +29,10 @@ import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.StringDef; import android.annotation.SuppressLint; import android.annotation.TestApi; import android.annotation.UserIdInt; import android.app.KeyguardManager; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.drawable.Icon; import android.os.Build; Loading Loading @@ -213,6 +217,11 @@ public final class AssociationRequest implements Parcelable { */ private final boolean mForceConfirmation; /** * Whether to skip the role grant, permission checks and consent dialog. */ private final boolean mSkipRoleGrant; /** * The app package name of the application the association will belong to. * Populated by the system. Loading Loading @@ -283,6 +292,7 @@ public final class AssociationRequest implements Parcelable { @Nullable CharSequence displayName, boolean selfManaged, boolean forceConfirmation, boolean skipRoleGrant, @Nullable Icon deviceIcon) { mSingleDevice = singleDevice; mDeviceFilters = requireNonNull(deviceFilters); Loading @@ -290,6 +300,7 @@ public final class AssociationRequest implements Parcelable { mDisplayName = displayName; mSelfManaged = selfManaged; mForceConfirmation = forceConfirmation; mSkipRoleGrant = skipRoleGrant; mCreationTime = System.currentTimeMillis(); mDeviceIcon = deviceIcon; } Loading Loading @@ -332,6 +343,18 @@ public final class AssociationRequest implements Parcelable { return mForceConfirmation; } /** * Whether to skip the role grant, permission checks and consent dialog. * * @see Builder#setSkipRoleGrant(boolean) * @hide */ @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi public boolean isSkipRoleGrant() { return mSkipRoleGrant; } /** * Whether only a single device should match the provided filter. * Loading Loading @@ -407,6 +430,7 @@ public final class AssociationRequest implements Parcelable { private CharSequence mDisplayName; private boolean mSelfManaged = false; private boolean mForceConfirmation = false; private boolean mSkipRoleGrant = false; private Icon mDeviceIcon = null; public Builder() {} Loading Loading @@ -493,6 +517,27 @@ public final class AssociationRequest implements Parcelable { return this; } /** * Do not attempt to grant the role corresponding to the device profile. * * <p>This will skip the permission checks and consent dialog but will not fail if the * role cannot be granted.</p> * * <p>Requires that the device not to have secure lock screen and that there no locked SIM * card. See {@link KeyguardManager#isKeyguardSecure()}</p> * * @hide */ @RequiresPermission(ASSOCIATE_COMPANION_DEVICES) @SuppressLint("UnflaggedApi") // @TestApi without associated feature. @TestApi @NonNull public Builder setSkipRoleGrant(boolean skipRoleGrant) { checkNotUsed(); mSkipRoleGrant = skipRoleGrant; return this; } /** * Set the device icon for the self-managed device and to display the icon in the * self-managed association dialog. Loading Loading @@ -521,7 +566,8 @@ public final class AssociationRequest implements Parcelable { + "provide the display name of the device"); } return new AssociationRequest(mSingleDevice, emptyIfNull(mDeviceFilters), mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mDeviceIcon); mDeviceProfile, mDisplayName, mSelfManaged, mForceConfirmation, mSkipRoleGrant, mDeviceIcon); } } Loading Loading @@ -597,6 +643,7 @@ public final class AssociationRequest implements Parcelable { + ", associatedDevice = " + mAssociatedDevice + ", selfManaged = " + mSelfManaged + ", forceConfirmation = " + mForceConfirmation + ", skipRoleGrant = " + mSkipRoleGrant + ", packageName = " + mPackageName + ", userId = " + mUserId + ", deviceProfilePrivilegesDescription = " + mDeviceProfilePrivilegesDescription Loading @@ -617,6 +664,7 @@ public final class AssociationRequest implements Parcelable { && Objects.equals(mAssociatedDevice, that.mAssociatedDevice) && mSelfManaged == that.mSelfManaged && mForceConfirmation == that.mForceConfirmation && mSkipRoleGrant == that.mSkipRoleGrant && Objects.equals(mPackageName, that.mPackageName) && mUserId == that.mUserId && Objects.equals(mDeviceProfilePrivilegesDescription, Loading @@ -637,6 +685,7 @@ public final class AssociationRequest implements Parcelable { _hash = 31 * _hash + Objects.hashCode(mAssociatedDevice); _hash = 31 * _hash + Boolean.hashCode(mSelfManaged); _hash = 31 * _hash + Boolean.hashCode(mForceConfirmation); _hash = 31 * _hash + Boolean.hashCode(mSkipRoleGrant); _hash = 31 * _hash + Objects.hashCode(mPackageName); _hash = 31 * _hash + mUserId; _hash = 31 * _hash + Objects.hashCode(mDeviceProfilePrivilegesDescription); Loading @@ -659,6 +708,7 @@ public final class AssociationRequest implements Parcelable { if (mAssociatedDevice != null) flg |= 0x40; if (mPackageName != null) flg |= 0x80; if (mDeviceProfilePrivilegesDescription != null) flg |= 0x100; if (mSkipRoleGrant) flg |= 0x200; dest.writeInt(flg); dest.writeParcelableList(mDeviceFilters, flags); Loading Loading @@ -692,6 +742,7 @@ public final class AssociationRequest implements Parcelable { boolean selfManaged = (flg & 0x2) != 0; boolean forceConfirmation = (flg & 0x4) != 0; boolean skipPrompt = (flg & 0x8) != 0; boolean skipRoleGrant = (flg & 0x200) != 0; List<DeviceFilter<?>> deviceFilters = new ArrayList<>(); in.readParcelableList(deviceFilters, DeviceFilter.class.getClassLoader(), (Class<android.companion.DeviceFilter<?>>) (Class<?>) Loading @@ -714,6 +765,7 @@ public final class AssociationRequest implements Parcelable { this.mAssociatedDevice = associatedDevice; this.mSelfManaged = selfManaged; this.mForceConfirmation = forceConfirmation; this.mSkipRoleGrant = skipRoleGrant; this.mPackageName = packageName; this.mUserId = userId; com.android.internal.util.AnnotationValidations.validate( Loading
services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java +26 −3 Original line number Diff line number Diff line Loading @@ -47,6 +47,7 @@ import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.KeyguardManager; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.ecm.EnhancedConfirmationManager; Loading Loading @@ -302,9 +303,18 @@ public class CompanionDeviceManagerService extends SystemService { enforceCallerCanManageAssociationsForPackage(getContext(), userId, packageName, "create associations"); if (request.isSkipRoleGrant()) { checkCallerCanSkipRoleGrant(); mAssociationRequestsProcessor.createAssociation(userId, packageName, /* macAddress= */ null, request.getDisplayName(), request.getDeviceProfile(), /* associatedDevice= */ null, request.isSelfManaged(), callback, /* resultReceiver= */ null, request.getDeviceIcon(), /* skipRoleGrant= */ true); } else { mAssociationRequestsProcessor.processNewAssociationRequest( request, packageName, userId, callback); } } @Override public PendingIntent buildAssociationCancellationIntent(String packageName, Loading Loading @@ -669,7 +679,7 @@ public class CompanionDeviceManagerService extends SystemService { final MacAddress macAddressObj = MacAddress.fromString(macAddress); mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddressObj, null, null, null, false, null, null, null); null, null, null, false, null, null, null, false); } private void checkCanCallNotificationApi(String callingPackage, int userId) { Loading @@ -684,6 +694,19 @@ public class CompanionDeviceManagerService extends SystemService { "App must have an association before calling this API"); } private void checkCallerCanSkipRoleGrant() { final KeyguardManager keyguardManager = getContext().getSystemService(KeyguardManager.class); if (keyguardManager != null && keyguardManager.isKeyguardSecure()) { throw new SecurityException("Skipping CDM role grant requires insecure keyguard."); } if (getContext().checkCallingPermission(ASSOCIATE_COMPANION_DEVICES) != PERMISSION_GRANTED) { throw new SecurityException( "Skipping CDM role grant requires ASSOCIATE_COMPANION_DEVICES permission."); } } @Override public boolean canPairWithoutPrompt(String packageName, String macAddress, int userId) { final AssociationInfo association = Loading
services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java +3 −2 Original line number Diff line number Diff line Loading @@ -106,8 +106,9 @@ class CompanionDeviceShellCommand extends ShellCommand { boolean selfManaged = getNextBooleanArg(); final MacAddress macAddress = MacAddress.fromString(address); mAssociationRequestsProcessor.createAssociation(userId, packageName, macAddress, deviceProfile, deviceProfile, /* associatedDevice */ null, selfManaged, /* callback */ null, /* resultReceiver */ null, /* deviceIcon */ null); deviceProfile, deviceProfile, /* associatedDevice= */ null, selfManaged, /* callback= */ null, /* resultReceiver= */ null, /* deviceIcon= */ null, /* skipRoleGrant= */ false); } break; Loading
services/companion/java/com/android/server/companion/association/AssociationRequestsProcessor.java +15 −5 Original line number Diff line number Diff line Loading @@ -282,8 +282,8 @@ public class AssociationRequestsProcessor { Binder.withCleanCallingIdentity(() -> { createAssociation(userId, packageName, macAddress, request.getDisplayName(), request.getDeviceProfile(), request.getAssociatedDevice(), request.isSelfManaged(), callback, resultReceiver, request.getDeviceIcon()); request.isSelfManaged(), callback, resultReceiver, request.getDeviceIcon(), /* skipRoleGrant= */ false); }); } Loading @@ -294,7 +294,8 @@ public class AssociationRequestsProcessor { @Nullable MacAddress macAddress, @Nullable CharSequence displayName, @Nullable String deviceProfile, @Nullable AssociatedDevice associatedDevice, boolean selfManaged, @Nullable IAssociationRequestCallback callback, @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon) { @Nullable ResultReceiver resultReceiver, @Nullable Icon deviceIcon, boolean skipRoleGrant) { final int id = mAssociationStore.getNextId(); final long timestamp = System.currentTimeMillis(); Loading @@ -303,9 +304,18 @@ public class AssociationRequestsProcessor { selfManaged, /* notifyOnDeviceNearby */ false, /* revoked */ false, /* pending */ false, timestamp, Long.MAX_VALUE, /* systemDataSyncFlags */ 0, deviceIcon, /* deviceId */ null); if (skipRoleGrant) { Slog.i(TAG, "Created association for " + association.getDeviceProfile() + " and userId=" + association.getUserId() + ", packageName=" + association.getPackageName() + " without granting role"); mAssociationStore.addAssociation(association); sendCallbackAndFinish(association, callback, resultReceiver); } else { // Add role holder for association (if specified) and add new association to store. maybeGrantRoleAndStoreAssociation(association, callback, resultReceiver); } } /** * Grant a role if specified and add an association to store. Loading