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

Commit 4aba2b1a authored by Joe Antonetti's avatar Joe Antonetti Committed by Android (Google) Code Review
Browse files

Merge "[Handoff][2/N] Decoupled Association Store from Business Logic" into main

parents 91b9c6a7 8a3ce3b4
Loading
Loading
Loading
Loading
+39 −79
Original line number Diff line number Diff line
@@ -19,11 +19,9 @@ package com.android.server.companion.datatransfer.continuity;
import static com.android.server.companion.datatransfer.contextsync.BitmapUtils.renderDrawableToByteArray;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.ActivityTaskManager;
import android.app.TaskStackListener;
import android.companion.AssociationInfo;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageInfo;
@@ -32,7 +30,6 @@ import android.graphics.drawable.Drawable;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.companion.datatransfer.continuity.connectivity.ConnectedAssociationStore;
import com.android.server.companion.datatransfer.continuity.connectivity.TaskContinuityMessenger;
import com.android.server.companion.datatransfer.continuity.messages.ContinuityDeviceConnected;
import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskAddedMessage;
@@ -41,21 +38,16 @@ import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskU
import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskInfo;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Responsible for broadcasting recent tasks on the current device to the user's
 *
 * other devices via {@link CompanionDeviceManager}.
 */
class TaskBroadcaster
    extends TaskStackListener
    implements ConnectedAssociationStore.Observer {
class TaskBroadcaster extends TaskStackListener {

    private static final String TAG = "TaskBroadcaster";

@@ -64,7 +56,7 @@ class TaskBroadcaster
    private final TaskContinuityMessenger mTaskContinuityMessenger;
    private final PackageManager mPackageManager;

    private boolean mIsBroadcasting = false;
    private boolean mIsListeningToActivityTaskManager = false;

    public TaskBroadcaster(
        @NonNull Context context,
@@ -79,56 +71,35 @@ class TaskBroadcaster
        mTaskContinuityMessenger = taskContinuityMessenger;
    }

    void startBroadcasting(){
        if (mIsBroadcasting) {
            Slog.v(TAG, "TaskBroadcaster is already broadcasting");
            return;
        }

        Slog.v(TAG, "Starting broadcasting");
        mTaskContinuityMessenger.getConnectedAssociationStore().addObserver(this);
    public void onDeviceConnected(int id) {
        Slog.v(TAG, "Transport connected for association id: " + id);
        sendDeviceConnectedMessage(id);
        synchronized (this) {
            if (!mIsListeningToActivityTaskManager) {
                mActivityTaskManager.registerTaskStackListener(this);

        mIsBroadcasting = true;
                mIsListeningToActivityTaskManager = true;
            }
        }

    void stopBroadcasting(){
        if (!mIsBroadcasting) {
            Slog.v(TAG, "TaskBroadcaster is not broadcasting");
            return;
    }

        Slog.v(TAG, "Stopping broadcasting");
        mIsBroadcasting = false;
        mTaskContinuityMessenger.getConnectedAssociationStore().removeObserver(this);
    public void onAllDevicesDisconnected() {
        synchronized (this) {
            if (mIsListeningToActivityTaskManager) {
                mActivityTaskManager.unregisterTaskStackListener(this);
                mIsListeningToActivityTaskManager = false;
            }

    @Override
    public void onTransportConnected(AssociationInfo associationInfo) {
        Slog.v(
            TAG,
            "Transport connected for association id: " + associationInfo.getId());
        sendDeviceConnectedMessage(associationInfo.getId());
        }

    @Override
    public void onTransportDisconnected(int associationId) {
        Slog.v(
            TAG,
            "Transport disconnected for association id: " + associationId);
    }

    @Override
    public void onTaskCreated(
        int taskId,
        ComponentName componentName) throws RemoteException {

    public void onTaskCreated(int taskId, ComponentName componentName) throws RemoteException {
        Slog.v(TAG, "onTaskCreated: taskId=" + taskId);
        RunningTaskInfo taskInfo = getRunningTask(taskId);
        if (taskInfo == null) {
            Slog.w(TAG, "Could not find RunningTaskInfo for taskId: " + taskId);
            return;
        }

        ActivityManager.RunningTaskInfo taskInfo = getRunningTask(taskId);

        if (taskInfo != null) {
        RemoteTaskInfo remoteTaskInfo = createRemoteTaskInfo(taskInfo);
        if (remoteTaskInfo == null) {
            Slog.w(TAG, "Could not create RemoteTaskInfo for task: " + taskInfo.taskId);
@@ -137,15 +108,11 @@ class TaskBroadcaster

        RemoteTaskAddedMessage taskAddedMessage = new RemoteTaskAddedMessage(remoteTaskInfo);
        mTaskContinuityMessenger.sendMessage(taskAddedMessage);
        } else {
            Slog.w(TAG, "Could not find RunningTaskInfo for taskId: " + taskId);
        }
    }

    @Override
    public void onTaskRemoved(int taskId) throws RemoteException {
        Slog.v(TAG, "onTaskRemoved: taskId=" + taskId);

        RemoteTaskRemovedMessage taskRemovedMessage = new RemoteTaskRemovedMessage(taskId);
        mTaskContinuityMessenger.sendMessage(taskRemovedMessage);
    }
@@ -170,17 +137,10 @@ class TaskBroadcaster
            "Sending device connected message for association id: "
                + associationId);

        List<ActivityManager.RunningTaskInfo> runningTasks = getRunningTasks();

        List<RemoteTaskInfo> remoteTasks = new ArrayList<>();
        for (ActivityManager.RunningTaskInfo taskInfo : runningTasks) {
            RemoteTaskInfo remoteTaskInfo = createRemoteTaskInfo(taskInfo);
            if (remoteTaskInfo != null) {
                remoteTasks.add(remoteTaskInfo);
            } else {
                Slog.w(TAG, "Could not create RemoteTaskInfo for task: " + taskInfo.taskId);
            }
        }
        List<RemoteTaskInfo> remoteTasks = getRunningTasks().stream()
            .map(this::createRemoteTaskInfo)
            .filter(Objects::nonNull)
            .collect(Collectors.toList());

        ContinuityDeviceConnected deviceConnectedMessage
            = new ContinuityDeviceConnected(remoteTasks);
@@ -188,10 +148,10 @@ class TaskBroadcaster
        mTaskContinuityMessenger.sendMessage(associationId, deviceConnectedMessage);
    }

    private ActivityManager.RunningTaskInfo getRunningTask(int taskId) {
        List<ActivityManager.RunningTaskInfo> runningTasks = getRunningTasks();
    private RunningTaskInfo getRunningTask(int taskId) {
        List<RunningTaskInfo> runningTasks = getRunningTasks();
        if (runningTasks != null) {
            for (ActivityManager.RunningTaskInfo info : runningTasks) {
            for (RunningTaskInfo info : runningTasks) {
                if (info.taskId == taskId) {
                    return info;
                }
@@ -201,11 +161,11 @@ class TaskBroadcaster
        return null;
    }

    private List<ActivityManager.RunningTaskInfo> getRunningTasks() {
    private List<RunningTaskInfo> getRunningTasks() {
        return mActivityTaskManager.getTasks(Integer.MAX_VALUE, true);
    }

    private RemoteTaskInfo createRemoteTaskInfo(ActivityManager.RunningTaskInfo taskInfo) {
    private RemoteTaskInfo createRemoteTaskInfo(RunningTaskInfo taskInfo) {
        PackageInfo packageInfo;
        try {
            packageInfo = mPackageManager.getPackageInfo(
+23 −3
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.server.companion.datatransfer.continuity;

import android.annotation.NonNull;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.companion.datatransfer.continuity.IHandoffRequestCallback;
import android.companion.datatransfer.continuity.ITaskContinuityManager;
@@ -42,6 +43,7 @@ import com.android.server.SystemService;

import java.util.ArrayList;
import java.util.List;
import java.util.Collection;

/**
 * Service to handle task continuity features
@@ -66,8 +68,7 @@ public final class TaskContinuityManagerService

        mTaskContinuityMessenger = new TaskContinuityMessenger(context, this);
        mTaskBroadcaster = new TaskBroadcaster(context, mTaskContinuityMessenger);
        mRemoteTaskStore = new RemoteTaskStore(
            mTaskContinuityMessenger.getConnectedAssociationStore());
        mRemoteTaskStore = new RemoteTaskStore();
        mOutboundHandoffRequestController = new OutboundHandoffRequestController(
            context,
            mTaskContinuityMessenger);
@@ -79,7 +80,6 @@ public final class TaskContinuityManagerService
    public void onStart() {
        mTaskContinuityManagerService = new TaskContinuityManagerServiceImpl();
        mTaskContinuityMessenger.enable();
        mTaskBroadcaster.startBroadcasting();
        publishBinderService(Context.TASK_CONTINUITY_SERVICE, mTaskContinuityManagerService);
    }

@@ -116,6 +116,26 @@ public final class TaskContinuityManagerService
        }
    }

    @Override
    public void onAssociationConnected(@NonNull AssociationInfo associationInfo) {
        mRemoteTaskStore.addDevice(
            associationInfo.getId(),
            associationInfo.getDisplayName().toString());

        mTaskBroadcaster.onDeviceConnected(associationInfo.getId());
    }

    @Override
    public void onAssociationDisconnected(
        int associationId,
        @NonNull Collection<AssociationInfo> connectedAssociations) {

        mRemoteTaskStore.removeDevice(associationId);
        if (connectedAssociations.isEmpty()) {
            mTaskBroadcaster.onAllDevicesDisconnected();
        }
    }

    @Override
    public void onMessageReceived(
        int associationId,
+54 −27
Original line number Diff line number Diff line
@@ -19,9 +19,10 @@ package com.android.server.companion.datatransfer.continuity.connectivity;
import android.annotation.NonNull;
import android.companion.AssociationInfo;
import android.companion.CompanionDeviceManager;
import android.util.Log;
import android.util.Slog;

import com.android.internal.annotations.VisibleForTesting;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
@@ -29,36 +30,67 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import java.util.function.Consumer;
import java.util.Objects;
import javax.annotation.concurrent.GuardedBy;

public class ConnectedAssociationStore {
class ConnectedAssociationStore {

    private static final String TAG = "ConnectedAssociationStore";

    private final CompanionDeviceManager mCompanionDeviceManager;
    private final Listener mListener;
    private final Executor mExecutor;
    private final Map<Integer, AssociationInfo> mConnectedAssociations = new HashMap<>();
    private final List<Observer> mObservers = new ArrayList<>();

    public interface Observer {
        void onTransportConnected(AssociationInfo associationInfo);
        void onTransportDisconnected(int associationId);
    @GuardedBy("this")
    private Consumer<List<AssociationInfo>> mAssociationInfoConsumer;

    interface Listener {
        void onTransportConnected(@NonNull AssociationInfo associationInfo);
        void onTransportDisconnected(
            int associationId,
            @NonNull Collection<AssociationInfo> connectedAssociations);
    }

    public ConnectedAssociationStore(
    ConnectedAssociationStore(
        @NonNull CompanionDeviceManager companionDeviceManager,
        @NonNull Executor executor) {
        @NonNull Executor executor,
        @NonNull Listener listener) {

        Objects.requireNonNull(companionDeviceManager);
        Objects.requireNonNull(executor);
        Objects.requireNonNull(listener);

        mCompanionDeviceManager = companionDeviceManager;
        mCompanionDeviceManager.addOnTransportsChangedListener(
                executor,
                this::onTransportsChanged);
        mListener = listener;
        mExecutor = executor;
   }

    public void addObserver(@NonNull Observer observer) {
        mObservers.add(observer);
    public void enable() {
        synchronized (this) {
            if (mAssociationInfoConsumer != null) {
                Slog.i(TAG, "ConnectedAssociationStore is already enabled.");
                return;
            }
            mAssociationInfoConsumer = this::onTransportsChanged;
            mCompanionDeviceManager.addOnTransportsChangedListener(
                mExecutor,
                mAssociationInfoConsumer);
            Slog.i(TAG, "Enabled ConnectedAssociationStore.");
        }
    }

    public void removeObserver(@NonNull Observer observer) {
        mObservers.remove(observer);
    public void disable() {
        synchronized (this) {
            if (mAssociationInfoConsumer == null) {
                Slog.i(TAG, "ConnectedAssociationStore is already disabled.");
                return;
            }
            mCompanionDeviceManager.removeOnTransportsChangedListener(mAssociationInfoConsumer);
            mAssociationInfoConsumer = null;
            Slog.i(TAG, "Disabled ConnectedAssociationStore.");
        }
    }

    public Collection<AssociationInfo> getConnectedAssociations() {
@@ -69,7 +101,8 @@ public class ConnectedAssociationStore {
        return mConnectedAssociations.get(associationId);
    }

    private void onTransportsChanged(List<AssociationInfo> associationInfos) {
    @VisibleForTesting
    void onTransportsChanged(List<AssociationInfo> associationInfos) {
        Set<Integer> removedAssociations
            = new HashSet<>(mConnectedAssociations.keySet());

@@ -85,27 +118,21 @@ public class ConnectedAssociationStore {
        }

        for (Integer associationId : removedAssociations) {
            Log.i(
            Slog.i(
                TAG,
                "Transport disconnected for association: " + associationId);

            mConnectedAssociations.remove(associationId);

            for (Observer observer : mObservers) {
                observer.onTransportDisconnected(associationId);
            }
            mListener.onTransportDisconnected(associationId, associationInfos);
        }

        for (AssociationInfo associationInfo : addedAssociations) {
            Log.i(
            Slog.i(
                TAG,
                "Transport connected for association: " + associationInfo.getId());

            mConnectedAssociations.put(associationInfo.getId(), associationInfo);

            for (Observer observer : mObservers) {
                observer.onTransportConnected(associationInfo);
            }
            mListener.onTransportConnected(associationInfo);
        }
    }
}
 No newline at end of file
+37 −4
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@ import java.util.Objects;
 * between devices. Internally, it uses the {@link CompanionDeviceManager} to send and receive
 * messages.
 */
public class TaskContinuityMessenger {
public class TaskContinuityMessenger implements ConnectedAssociationStore.Listener {

    private static final String TAG = "TaskContinuityMessenger";

@@ -51,6 +51,10 @@ public class TaskContinuityMessenger {
    private BiConsumer<Integer, byte[]> mIncomingMessageConsumer;

    public interface Listener {
        void onAssociationConnected(@NonNull AssociationInfo associationInfo);
        void onAssociationDisconnected(
            int associationId,
            @NonNull Collection<AssociationInfo> connectedAssociations);
        void onMessageReceived(int associationId, @NonNull TaskContinuityMessage message);
    }

@@ -65,7 +69,8 @@ public class TaskContinuityMessenger {
        mCompanionDeviceManager = context.getSystemService(CompanionDeviceManager.class);
        mConnectedAssociationStore = new ConnectedAssociationStore(
            mCompanionDeviceManager,
            mExecutor);
            mExecutor,
            this);
    }

    public void enable() {
@@ -80,6 +85,8 @@ public class TaskContinuityMessenger {
                MESSAGE_ONEWAY_TASK_CONTINUITY,
                mIncomingMessageConsumer);
        }

        mConnectedAssociationStore.enable();
    }

    public void disable() {
@@ -93,6 +100,8 @@ public class TaskContinuityMessenger {
                mIncomingMessageConsumer);
            mIncomingMessageConsumer = null;
        }

        mConnectedAssociationStore.disable();
    }

    @NonNull
@@ -111,13 +120,18 @@ public class TaskContinuityMessenger {
        int associationId,
        @NonNull TaskContinuityMessage message) {

        Objects.requireNonNull(message);

        return sendMessage(new int[] {associationId}, message);
    }

    public SendMessageResult sendMessage(
        int[] associationIds,
        @NonNull int[] associationIds,
        @NonNull TaskContinuityMessage message) {

        Objects.requireNonNull(associationIds);
        Objects.requireNonNull(message);

        Slog.i(TAG, "Sending message to " + associationIds.length + " associations.");
        byte[] serializedMessage;
        try {
@@ -148,6 +162,8 @@ public class TaskContinuityMessenger {
    }

    public SendMessageResult sendMessage(@NonNull TaskContinuityMessage message) {
        Objects.requireNonNull(message);

        int[] connectedAssociations = mConnectedAssociationStore
            .getConnectedAssociations()
            .stream()
@@ -157,6 +173,23 @@ public class TaskContinuityMessenger {
        return sendMessage(connectedAssociations, message);
    }

    @Override
    public void onTransportConnected(@NonNull AssociationInfo associationInfo) {
        Objects.requireNonNull(associationInfo);

        mListener.onAssociationConnected(associationInfo);
    }

    @Override
    public void onTransportDisconnected(
        int associationId,
        @NonNull Collection<AssociationInfo> connectedAssociations) {

        Objects.requireNonNull(connectedAssociations);

        mListener.onAssociationDisconnected(associationId, connectedAssociations);
    }

    private void onMessageReceived(int associationId, byte[] data) {
        Slog.v(TAG, "Received message from association id: " + associationId);
      try {
+42 −45
Original line number Diff line number Diff line
@@ -15,13 +15,12 @@
 */
package com.android.server.companion.datatransfer.continuity.tasks;

import android.companion.AssociationInfo;
import android.annotation.NonNull;
import android.companion.datatransfer.continuity.IRemoteTaskListener;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Slog;

import com.android.server.companion.datatransfer.continuity.connectivity.ConnectedAssociationStore;
import com.android.server.companion.datatransfer.continuity.messages.RemoteTaskInfo;

import android.companion.datatransfer.continuity.RemoteTask;
@@ -30,21 +29,15 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
public class RemoteTaskStore {

    private static final String TAG = "RemoteTaskStore";

    private final ConnectedAssociationStore mConnectedAssociationStore;
    private final Map<Integer, RemoteDeviceTaskList> mRemoteDeviceTaskLists
        = new HashMap<>();
    private final RemoteCallbackList<IRemoteTaskListener>
        mRemoteTaskListeners = new RemoteCallbackList<>();

    public RemoteTaskStore(ConnectedAssociationStore connectedAssociationStore) {
        mConnectedAssociationStore = connectedAssociationStore;
        mConnectedAssociationStore.addObserver(this);
    }
    private final Map<Integer, RemoteDeviceTaskList> mRemoteDeviceTaskLists = new HashMap<>();
    private final RemoteCallbackList<IRemoteTaskListener> mRemoteTaskListeners
        = new RemoteCallbackList<>();

    /**
     * Sets the task list of the given association id to the given tasks.
@@ -53,9 +46,9 @@ public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
     * @param tasks The list of tasks currently available on the device on first
     * connection.
     */
    public void setTasks(
        int associationId,
        List<RemoteTaskInfo> tasks) {
    public void setTasks(int associationId, @NonNull List<RemoteTaskInfo> tasks) {
        Objects.requireNonNull(tasks);

        synchronized (mRemoteDeviceTaskLists) {
            if (!mRemoteDeviceTaskLists.containsKey(associationId)) {
                Slog.e(
@@ -70,7 +63,9 @@ public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
        }
    }

      public void addTask(int associationId, RemoteTaskInfo taskInfo) {
    public void addTask(int associationId, @NonNull RemoteTaskInfo taskInfo) {
        Objects.requireNonNull(taskInfo);

        synchronized (mRemoteDeviceTaskLists) {
            if (!mRemoteDeviceTaskLists.containsKey(associationId)) {
                Slog.e(
@@ -95,7 +90,9 @@ public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
        }
    }

    public void updateTask(int associationId, RemoteTaskInfo taskInfo) {
    public void updateTask(int associationId, @NonNull RemoteTaskInfo taskInfo) {
        Objects.requireNonNull(taskInfo);

        synchronized (mRemoteDeviceTaskLists) {
            if (!mRemoteDeviceTaskLists.containsKey(associationId)) {
                return;
@@ -111,6 +108,7 @@ public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
     * @return A list of the most recent tasks from all devices in the task
     * store.
     */
    @NonNull
    public List<RemoteTask> getMostRecentTasks() {
        synchronized (mRemoteDeviceTaskLists) {
            List<RemoteTask> mostRecentTasks = new ArrayList<>();
@@ -124,50 +122,53 @@ public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
        }
    }

    public void addListener(IRemoteTaskListener listener) {
    public void addListener(@NonNull IRemoteTaskListener listener) {
        Objects.requireNonNull(listener);

        synchronized (mRemoteTaskListeners) {
            mRemoteTaskListeners.register(listener);
        }
    }

    public void removeListener(IRemoteTaskListener listener) {
    public void removeListener(@NonNull IRemoteTaskListener listener) {
        Objects.requireNonNull(listener);

        synchronized (mRemoteTaskListeners) {
            mRemoteTaskListeners.unregister(listener);
        }
    }

    @Override
    public void onTransportConnected(AssociationInfo associationInfo) {
    public void addDevice(int id, @NonNull String name) {
        Objects.requireNonNull(name);

        synchronized (mRemoteDeviceTaskLists) {
            if (!mRemoteDeviceTaskLists.containsKey(associationInfo.getId())) {
            if (!mRemoteDeviceTaskLists.containsKey(id)) {
                Slog.v(
                    TAG,
                    "Creating new RemoteDeviceTaskList for association: "
                        + associationInfo.getId());
                    "Creating new RemoteDeviceTaskList for association: " + id);

                RemoteDeviceTaskList taskList
                    = new RemoteDeviceTaskList(
                        associationInfo.getId(),
                        associationInfo.getDisplayName().toString(),
                        id,
                        name,
                        this::onMostRecentTaskChanged);

                mRemoteDeviceTaskLists.put(associationInfo.getId(), taskList);
                mRemoteDeviceTaskLists.put(id, taskList);
            } else {
                Slog.v(
                    TAG,
                    "Transport already connected for association: " + associationInfo.getId());
                    "Transport already connected for association: " + id);
            }
        }
    }

    @Override
    public void onTransportDisconnected(int associationId) {
    public void removeDevice(int id) {
        synchronized (mRemoteDeviceTaskLists) {
            Slog.v(
                TAG,
                "Deleting RemoteDeviceTaskList for association: " + associationId);
                "Deleting RemoteDeviceTaskList for association: " + id);

            mRemoteDeviceTaskLists.remove(associationId);
            mRemoteDeviceTaskLists.remove(id);
            notifyListeners();
        }
    }
@@ -179,18 +180,14 @@ public class RemoteTaskStore implements ConnectedAssociationStore.Observer {
    private void notifyListeners() {
       synchronized (mRemoteTaskListeners) {
            List<RemoteTask> remoteTasks = getMostRecentTasks();
            int i = mRemoteTaskListeners.beginBroadcast();
            while (i > 0) {
                i--;
            mRemoteTaskListeners.broadcast(
                (listener) -> {
                    try {
                    mRemoteTaskListeners
                        .getBroadcastItem(i)
                        .onRemoteTasksChanged(remoteTasks);
                        listener.onRemoteTasksChanged(remoteTasks);
                    } catch (RemoteException e) {
                        Slog.e(TAG, "Failed to notify listener: " + e.getMessage());
                    }
            }
            mRemoteTaskListeners.finishBroadcast();
            });
        }
    }
}
 No newline at end of file
Loading