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

Commit ae1b0d88 authored by Bernardo Rufino's avatar Bernardo Rufino
Browse files

Move backup agent operations to Trampoline.

From BMS.

Test: atest BackupManagerServiceTest TrampolineRoboTest TrampolineTest
Bug: 135661048
Change-Id: I7e118631e293d8eb808cf47e857b9102eab60d37
parent 1cc1768e
Loading
Loading
Loading
Loading
+1 −80
Original line number Diff line number Diff line
@@ -18,10 +18,8 @@ package com.android.server.backup;

import static com.android.internal.util.Preconditions.checkNotNull;

import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManagerMonitor;
import android.app.backup.IBackupObserver;
@@ -40,7 +38,6 @@ import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;

import com.android.internal.annotations.VisibleForTesting;
@@ -81,20 +78,6 @@ public class BackupManagerService {
        mServiceUsers = userServices;
    }

    /**
     * If {@code userId} is different from the calling user id, then the caller must hold the
     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
     *
     * @param userId User id on which the backup operation is being requested.
     * @param message A message to include in the exception if it is thrown.
     */
    private void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
        if (Binder.getCallingUserHandle().getIdentifier() != userId) {
            mContext.enforceCallingOrSelfPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
        }
    }

    /**
     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
     * If the user is not registered with the service (either the user is locked or not eligible for
@@ -110,12 +93,7 @@ public class BackupManagerService {
    @VisibleForTesting
    UserBackupManagerService getServiceForUserIfCallerHasPermission(
            @UserIdInt int userId, String caller) {
        enforceCallingPermissionOnUserId(userId, caller);
        UserBackupManagerService userBackupManagerService = mServiceUsers.get(userId);
        if (userBackupManagerService == null) {
            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
        }
        return userBackupManagerService;
        return mTrampoline.getServiceForUserIfCallerHasPermission(userId, caller);
    }

    /*
@@ -125,63 +103,6 @@ public class BackupManagerService {
     */
    // TODO (b/118520567): Stop hardcoding system user when we pass in user id as a parameter

    // ---------------------------------------------
    // BACKUP AGENT OPERATIONS
    // ---------------------------------------------

    /**
     * An app's backup agent calls this method to let the service know that there's new data to
     * backup for their app {@code packageName}. Only used for apps participating in key-value
     * backup.
     */
    public void dataChanged(@UserIdInt int userId, String packageName) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "dataChanged()");

        if (userBackupManagerService != null) {
            userBackupManagerService.dataChanged(packageName);
        }
    }

    /**
     * Callback: a requested backup agent has been instantiated. This should only be called from the
     * {@link ActivityManager}.
     */
    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");

        if (userBackupManagerService != null) {
            userBackupManagerService.agentConnected(packageName, agentBinder);
        }
    }

    /**
     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
     * called from the {@link ActivityManager}.
     */
    public void agentDisconnected(@UserIdInt int userId, String packageName) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");

        if (userBackupManagerService != null) {
            userBackupManagerService.agentDisconnected(packageName);
        }
    }

    /**
     * Used by a currently-active backup agent to notify the service that it has completed its given
     * outstanding asynchronous backup/restore operation.
     */
    public void opComplete(@UserIdInt int userId, int token, long result) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "opComplete()");

        if (userBackupManagerService != null) {
            userBackupManagerService.opComplete(token, result);
        }
    }

    // ---------------------------------------------
    // TRANSPORT OPERATIONS
    // ---------------------------------------------
+95 −4
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import static java.util.Collections.emptySet;
import android.Manifest;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.backup.BackupManager;
import android.app.backup.IBackupManager;
@@ -502,7 +503,7 @@ public class Trampoline extends IBackupManager.Stub {
    @Override
    public void dataChangedForUser(int userId, String packageName) throws RemoteException {
        if (isUserReadyForBackup(userId)) {
            mService.dataChanged(userId, packageName);
            dataChanged(userId, packageName);
        }
    }

@@ -511,6 +512,20 @@ public class Trampoline extends IBackupManager.Stub {
        dataChangedForUser(binderGetCallingUserId(), packageName);
    }

    /**
     * An app's backup agent calls this method to let the service know that there's new data to
     * backup for their app {@code packageName}. Only used for apps participating in key-value
     * backup.
     */
    public void dataChanged(@UserIdInt int userId, String packageName) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "dataChanged()");

        if (userBackupManagerService != null) {
            userBackupManagerService.dataChanged(packageName);
        }
    }

    @Override
    public void initializeTransportsForUser(
            int userId, String[] transportNames, IBackupObserver observer) throws RemoteException {
@@ -537,7 +552,7 @@ public class Trampoline extends IBackupManager.Stub {
    public void agentConnectedForUser(int userId, String packageName, IBinder agent)
            throws RemoteException {
        if (isUserReadyForBackup(userId)) {
            mService.agentConnected(userId, packageName, agent);
            agentConnected(userId, packageName, agent);
        }
    }

@@ -546,10 +561,23 @@ public class Trampoline extends IBackupManager.Stub {
        agentConnectedForUser(binderGetCallingUserId(), packageName, agent);
    }

    /**
     * Callback: a requested backup agent has been instantiated. This should only be called from the
     * {@link ActivityManager}.
     */
    public void agentConnected(@UserIdInt int userId, String packageName, IBinder agentBinder) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "agentConnected()");

        if (userBackupManagerService != null) {
            userBackupManagerService.agentConnected(packageName, agentBinder);
        }
    }

    @Override
    public void agentDisconnectedForUser(int userId, String packageName) throws RemoteException {
        if (isUserReadyForBackup(userId)) {
            mService.agentDisconnected(userId, packageName);
            agentDisconnected(userId, packageName);
        }
    }

@@ -558,6 +586,19 @@ public class Trampoline extends IBackupManager.Stub {
        agentDisconnectedForUser(binderGetCallingUserId(), packageName);
    }

    /**
     * Callback: a backup agent has failed to come up, or has unexpectedly quit. This should only be
     * called from the {@link ActivityManager}.
     */
    public void agentDisconnected(@UserIdInt int userId, String packageName) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "agentDisconnected()");

        if (userBackupManagerService != null) {
            userBackupManagerService.agentDisconnected(packageName);
        }
    }

    @Override
    public void restoreAtInstallForUser(int userId, String packageName, int token)
            throws RemoteException {
@@ -835,7 +876,7 @@ public class Trampoline extends IBackupManager.Stub {
    @Override
    public void opCompleteForUser(int userId, int token, long result) throws RemoteException {
        if (isUserReadyForBackup(userId)) {
            mService.opComplete(userId, token, result);
            opComplete(userId, token, result);
        }
    }

@@ -844,6 +885,19 @@ public class Trampoline extends IBackupManager.Stub {
        opCompleteForUser(binderGetCallingUserId(), token, result);
    }

    /**
     * Used by a currently-active backup agent to notify the service that it has completed its given
     * outstanding asynchronous backup/restore operation.
     */
    public void opComplete(@UserIdInt int userId, int token, long result) {
        UserBackupManagerService userBackupManagerService =
                getServiceForUserIfCallerHasPermission(userId, "opComplete()");

        if (userBackupManagerService != null) {
            userBackupManagerService.opComplete(token, result);
        }
    }

    @Override
    public long getAvailableRestoreTokenForUser(int userId, String packageName) {
        return isUserReadyForBackup(userId) ? mService.getAvailableRestoreToken(userId,
@@ -927,4 +981,41 @@ public class Trampoline extends IBackupManager.Stub {
            mService.endFullBackup(userId);
        }
    }

    /**
     * Returns the {@link UserBackupManagerService} instance for the specified user {@code userId}.
     * If the user is not registered with the service (either the user is locked or not eligible for
     * the backup service) then return {@code null}.
     *
     * @param userId The id of the user to retrieve its instance of {@link
     *     UserBackupManagerService}.
     * @param caller A {@link String} identifying the caller for logging purposes.
     * @throws SecurityException if {@code userId} is different from the calling user id and the
     *     caller does NOT have the android.permission.INTERACT_ACROSS_USERS_FULL permission.
     */
    @Nullable
    @VisibleForTesting
    UserBackupManagerService getServiceForUserIfCallerHasPermission(
            @UserIdInt int userId, String caller) {
        enforceCallingPermissionOnUserId(userId, caller);
        UserBackupManagerService userBackupManagerService = mUserServices.get(userId);
        if (userBackupManagerService == null) {
            Slog.w(TAG, "Called " + caller + " for unknown user: " + userId);
        }
        return userBackupManagerService;
    }

    /**
     * If {@code userId} is different from the calling user id, then the caller must hold the
     * android.permission.INTERACT_ACROSS_USERS_FULL permission.
     *
     * @param userId User id on which the backup operation is being requested.
     * @param message A message to include in the exception if it is thrown.
     */
    void enforceCallingPermissionOnUserId(@UserIdInt int userId, String message) {
        if (Binder.getCallingUserHandle().getIdentifier() != userId) {
            mContext.enforceCallingOrSelfPermission(
                    Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
        }
    }
}
+0 −103
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@ import android.app.backup.IFullBackupRestoreObserver;
import android.app.backup.ISelectBackupTransportCallback;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.UserHandle;
@@ -221,108 +220,6 @@ public class BackupManagerServiceTest {
                backupManagerService.getServiceForUserIfCallerHasPermission(mUserOneId, "test"));
    }

    // ---------------------------------------------
    // Backup agent tests
    // ---------------------------------------------

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);

        backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);

        verify(mUserOneService).dataChanged(TEST_PACKAGE);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);

        backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);

        verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
    }

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
        IBinder agentBinder = mock(IBinder.class);

        backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);

        verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
        IBinder agentBinder = mock(IBinder.class);

        backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);

        verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
    }

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testAgentDisconnected_onRegisteredUser_callsMethodForUser() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);

        backupManagerService.agentDisconnected(mUserOneId, TEST_PACKAGE);

        verify(mUserOneService).agentDisconnected(TEST_PACKAGE);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testAgentDisconnected_onUnknownUser_doesNotPropagateCall() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);

        backupManagerService.agentDisconnected(mUserTwoId, TEST_PACKAGE);

        verify(mUserOneService, never()).agentDisconnected(TEST_PACKAGE);
    }

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);

        backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);

        verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
        registerUser(mUserOneId, mUserOneService);
        BackupManagerService backupManagerService = createService();
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);

        backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);

        verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
    }

    // ---------------------------------------------
    // Transport tests
    // ---------------------------------------------
+103 −0
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL;

import static com.google.common.truth.Truth.assertThat;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
@@ -28,6 +29,7 @@ import static org.robolectric.Shadows.shadowOf;
import android.annotation.UserIdInt;
import android.app.Application;
import android.content.Context;
import android.os.IBinder;
import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
@@ -63,6 +65,8 @@ import org.robolectric.shadows.ShadowContextWrapper;
        })
@Presubmit
public class TrampolineRoboTest {
    private static final String TEST_PACKAGE = "package";

    private Context mContext;
    private ShadowContextWrapper mShadowContext;
    private ShadowUserManager mShadowUserManager;
@@ -161,10 +165,94 @@ public class TrampolineRoboTest {
        assertThat(serviceUsers.size()).isEqualTo(0);
    }

    // ---------------------------------------------
    // Backup agent tests
    // ---------------------------------------------

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testDataChanged_onRegisteredUser_callsMethodForUser() throws Exception {
        Trampoline backupManagerService = createService();
        registerUser(backupManagerService, mUserOneId, mUserOneService);
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);

        backupManagerService.dataChanged(mUserOneId, TEST_PACKAGE);

        verify(mUserOneService).dataChanged(TEST_PACKAGE);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testDataChanged_onUnknownUser_doesNotPropagateCall() throws Exception {
        Trampoline backupManagerService = createService();
        registerUser(backupManagerService, mUserOneId, mUserOneService);
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);

        backupManagerService.dataChanged(mUserTwoId, TEST_PACKAGE);

        verify(mUserOneService, never()).dataChanged(TEST_PACKAGE);
    }

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testAgentConnected_onRegisteredUser_callsMethodForUser() throws Exception {
        Trampoline backupManagerService = createService();
        registerUser(backupManagerService, mUserOneId, mUserOneService);
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);
        IBinder agentBinder = mock(IBinder.class);

        backupManagerService.agentConnected(mUserOneId, TEST_PACKAGE, agentBinder);

        verify(mUserOneService).agentConnected(TEST_PACKAGE, agentBinder);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testAgentConnected_onUnknownUser_doesNotPropagateCall() throws Exception {
        Trampoline backupManagerService = createService();
        registerUser(backupManagerService, mUserOneId, mUserOneService);
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);
        IBinder agentBinder = mock(IBinder.class);

        backupManagerService.agentConnected(mUserTwoId, TEST_PACKAGE, agentBinder);

        verify(mUserOneService, never()).agentConnected(TEST_PACKAGE, agentBinder);
    }

    /** Test that the backup service routes methods correctly to the user that requests it. */
    @Test
    public void testOpComplete_onRegisteredUser_callsMethodForUser() throws Exception {
        Trampoline backupManagerService = createService();
        registerUser(backupManagerService, mUserOneId, mUserOneService);
        setCallerAndGrantInteractUserPermission(mUserOneId, /* shouldGrantPermission */ false);

        backupManagerService.opComplete(mUserOneId, /* token */ 0, /* result */ 0L);

        verify(mUserOneService).opComplete(/* token */ 0, /* result */ 0L);
    }

    /** Test that the backup service does not route methods for non-registered users. */
    @Test
    public void testOpComplete_onUnknownUser_doesNotPropagateCall() throws Exception {
        Trampoline backupManagerService = createService();
        registerUser(backupManagerService, mUserOneId, mUserOneService);
        setCallerAndGrantInteractUserPermission(mUserTwoId, /* shouldGrantPermission */ false);

        backupManagerService.opComplete(mUserTwoId, /* token */ 0, /* result */ 0L);

        verify(mUserOneService, never()).opComplete(/* token */ 0, /* result */ 0L);
    }

    private Trampoline createService() {
        return new Trampoline(mContext);
    }

    private void registerUser(
            Trampoline trampoline, int userId, UserBackupManagerService userBackupManagerService) {
        trampoline.setBackupServiceActive(userId, true);
        trampoline.startServiceForUser(userId, userBackupManagerService);
    }

    private Trampoline createServiceAndRegisterUser(
            int userId, UserBackupManagerService userBackupManagerService) {
        Trampoline backupManagerService = createService();
@@ -172,4 +260,19 @@ public class TrampolineRoboTest {
        backupManagerService.startServiceForUser(userId, userBackupManagerService);
        return backupManagerService;
    }

    /**
     * Sets the calling user to {@code userId} and grants the permission INTERACT_ACROSS_USERS_FULL
     * to the caller if {@code shouldGrantPermission} is {@code true}, else it denies the
     * permission.
     */
    private void setCallerAndGrantInteractUserPermission(
            @UserIdInt int userId, boolean shouldGrantPermission) {
        ShadowBinder.setCallingUserHandle(UserHandle.of(userId));
        if (shouldGrantPermission) {
            mShadowContext.grantPermissions(INTERACT_ACROSS_USERS_FULL);
        } else {
            mShadowContext.denyPermissions(INTERACT_ACROSS_USERS_FULL);
        }
    }
}
+0 −59
Original line number Diff line number Diff line
@@ -482,22 +482,6 @@ public class TrampolineTest {
                TrampolineTestable.sRememberActivatedFiles.get(NON_USER_SYSTEM), true));
    }

    @Test
    public void dataChangedForUser_forwarded() throws Exception {
        mTrampoline.dataChangedForUser(mUserId, PACKAGE_NAME);

        verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
    }

    @Test
    public void dataChanged_forwarded() throws Exception {
        TrampolineTestable.sCallingUserId = mUserId;

        mTrampoline.dataChanged(PACKAGE_NAME);

        verify(mBackupManagerServiceMock).dataChanged(mUserId, PACKAGE_NAME);
    }

    @Test
    public void clearBackupDataForUser_forwarded() throws Exception {

@@ -515,40 +499,6 @@ public class TrampolineTest {
        verify(mBackupManagerServiceMock).clearBackupData(mUserId, TRANSPORT_NAME, PACKAGE_NAME);
    }

    @Test
    public void agentConnectedForUser_forwarded() throws Exception {

        mTrampoline.agentConnectedForUser(mUserId, PACKAGE_NAME, mAgentMock);

        verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
    }

    @Test
    public void agentConnected_forwarded() throws Exception {
        TrampolineTestable.sCallingUserId = mUserId;

        mTrampoline.agentConnected(PACKAGE_NAME, mAgentMock);

        verify(mBackupManagerServiceMock).agentConnected(mUserId, PACKAGE_NAME, mAgentMock);
    }

    @Test
    public void agentDisconnectedForUser_forwarded() throws Exception {

        mTrampoline.agentDisconnectedForUser(mUserId, PACKAGE_NAME);

        verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
    }

    @Test
    public void agentDisconnected_forwarded() throws Exception {
        TrampolineTestable.sCallingUserId = mUserId;

        mTrampoline.agentDisconnected(PACKAGE_NAME);

        verify(mBackupManagerServiceMock).agentDisconnected(mUserId, PACKAGE_NAME);
    }

    @Test
    public void restoreAtInstallForUser_forwarded() throws Exception {

@@ -935,15 +885,6 @@ public class TrampolineTest {
                .beginRestoreSession(mUserId, PACKAGE_NAME, TRANSPORT_NAME);
    }

    @Test
    public void opComplete_forwarded() throws Exception {
        TrampolineTestable.sCallingUserId = mUserId;

        mTrampoline.opComplete(1, 2);

        verify(mBackupManagerServiceMock).opComplete(mUserId, 1, 2);
    }

    @Test
    public void getAvailableRestoreTokenForUser_forwarded() {
        when(mBackupManagerServiceMock.getAvailableRestoreToken(mUserId, PACKAGE_NAME))