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

Commit 9fc76869 authored by Guojing Yuan's avatar Guojing Yuan
Browse files

[CDM] Add last removed association to disk and DUMPSYS

Sample output:
Last Removed Association:
  1739904326244 shell Association{mId=3, mUserId=0, mPackageName='testapp', mDeviceMacAddress=aa:bb:cc:dd:ee:ff, mDisplayName='null', mDeviceProfile='null', mSelfManaged=false, mAssociatedDevice=null, mNotifyOnDeviceNearby=false, mRevoked=false, mPending=false, mTimeApprovedMs=Tue Feb 18 10:45:22 PST 2025, mLastTimeConnectedMs=None, mSystemDataSyncFlags=0, mDeviceId='null}

Bug: 381905348
Test: CTS + manual
Flag: EXEMPT bugfix
Change-Id: Ic3dc11ec0e6ecd0e3ab005e243e5e38f211d10a1
parent 17c69415
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -31,6 +31,8 @@ import static android.os.UserHandle.getCallingUserId;

import static com.android.internal.util.CollectionUtils.any;
import static com.android.internal.util.Preconditions.checkState;
import static com.android.server.companion.association.DisassociationProcessor.REASON_API;
import static com.android.server.companion.association.DisassociationProcessor.REASON_PKG_DATA_CLEARED;
import static com.android.server.companion.utils.PackageUtils.enforceUsesCompanionDeviceFeature;
import static com.android.server.companion.utils.PackageUtils.isRestrictedSettingsAllowed;
import static com.android.server.companion.utils.PermissionsUtils.enforceCallerCanManageAssociationsForPackage;
@@ -249,7 +251,7 @@ public class CompanionDeviceManagerService extends SystemService {
                    + packageName + "]. Cleaning up CDM data...");

            for (AssociationInfo association : associationsForPackage) {
                mDisassociationProcessor.disassociate(association.getId());
                mDisassociationProcessor.disassociate(association.getId(), REASON_PKG_DATA_CLEARED);
            }

            mCompanionAppBinder.onPackageChanged(userId);
@@ -416,7 +418,7 @@ public class CompanionDeviceManagerService extends SystemService {

        @Override
        public void disassociate(int associationId) {
            mDisassociationProcessor.disassociate(associationId);
            mDisassociationProcessor.disassociate(associationId, REASON_API);
        }

        @Override
+4 −2
Original line number Diff line number Diff line
@@ -18,6 +18,8 @@ package com.android.server.companion;

import static android.companion.CompanionDeviceManager.MESSAGE_REQUEST_CONTEXT_SYNC;

import static com.android.server.companion.association.DisassociationProcessor.REASON_SHELL;

import android.companion.AssociationInfo;
import android.companion.ContextSyncMessage;
import android.companion.Flags;
@@ -121,7 +123,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
                    if (association == null) {
                        out.println("Association doesn't exist.");
                    } else {
                        mDisassociationProcessor.disassociate(association.getId());
                        mDisassociationProcessor.disassociate(association.getId(), REASON_SHELL);
                    }
                }
                break;
@@ -131,7 +133,7 @@ class CompanionDeviceShellCommand extends ShellCommand {
                    final List<AssociationInfo> userAssociations =
                            mAssociationStore.getAssociationsByUser(userId);
                    for (AssociationInfo association : userAssociations) {
                        mDisassociationProcessor.disassociate(association.getId());
                        mDisassociationProcessor.disassociate(association.getId(), REASON_SHELL);
                    }
                }
                break;
+42 −0
Original line number Diff line number Diff line
@@ -58,6 +58,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
@@ -164,6 +165,7 @@ public final class AssociationDiskStore {

    private static final String FILE_NAME_LEGACY = "companion_device_manager_associations.xml";
    private static final String FILE_NAME = "companion_device_manager.xml";
    private static final String FILE_NAME_LAST_REMOVED_ASSOCIATION = "last_removed_association.txt";

    private static final String XML_TAG_STATE = "state";
    private static final String XML_TAG_ASSOCIATIONS = "associations";
@@ -268,6 +270,46 @@ public final class AssociationDiskStore {
        }
    }

    /**
     * Read the last removed association from disk.
     */
    public String readLastRemovedAssociation(@UserIdInt int userId) {
        final AtomicFile file = createStorageFileForUser(
                userId, FILE_NAME_LAST_REMOVED_ASSOCIATION);
        StringBuilder sb = new StringBuilder();
        int c;
        try (FileInputStream fis = file.openRead()) {
            while ((c = fis.read()) != -1) {
                sb.append((char) c);
            }
            fis.close();
            return sb.toString();
        } catch (FileNotFoundException e) {
            Slog.e(TAG, "File " + file + " for user=" + userId + " doesn't exist.");
            return null;
        } catch (IOException e) {
            Slog.e(TAG, "Can't read file " + file + " for user=" + userId);
            return null;
        }
    }

    /**
     * Write the last removed association to disk.
     */
    public void writeLastRemovedAssociation(AssociationInfo association, String reason) {
        Slog.i(TAG, "Writing last removed association=" + association.getId() + " to disk...");

        final AtomicFile file = createStorageFileForUser(
                association.getUserId(), FILE_NAME_LAST_REMOVED_ASSOCIATION);
        writeToFileSafely(file, out -> {
            out.write(String.valueOf(System.currentTimeMillis()).getBytes());
            out.write(' ');
            out.write(reason.getBytes());
            out.write(' ');
            out.write(association.toString().getBytes());
        });
    }

    @NonNull
    private static Associations readAssociationsFromFile(@UserIdInt int userId,
            @NonNull AtomicFile file, @NonNull String rootTag) {
+11 −1
Original line number Diff line number Diff line
@@ -276,7 +276,7 @@ public class AssociationStore {
    /**
     * Remove an association.
     */
    public void removeAssociation(int id) {
    public void removeAssociation(int id, String reason) {
        Slog.i(TAG, "Removing association id=[" + id + "]...");

        final AssociationInfo association;
@@ -291,6 +291,8 @@ public class AssociationStore {

            writeCacheToDisk(association.getUserId());

            mDiskStore.writeLastRemovedAssociation(association, reason);

            Slog.i(TAG, "Done removing association.");
        }

@@ -525,6 +527,14 @@ public class AssociationStore {
                out.append("  ").append(a.toString()).append('\n');
            }
        }

        out.append("Last Removed Association:\n");
        for (UserInfo user : mUserManager.getAliveUsers()) {
            String lastRemovedAssociation = mDiskStore.readLastRemovedAssociation(user.id);
            if (lastRemovedAssociation != null) {
                out.append("  ").append(lastRemovedAssociation).append('\n');
            }
        }
    }

    private void broadcastChange(@ChangeType int changeType, AssociationInfo association) {
+14 −7
Original line number Diff line number Diff line
@@ -47,6 +47,13 @@ import com.android.server.companion.transport.CompanionTransportManager;
@SuppressLint("LongLogTag")
public class DisassociationProcessor {

    public static final String REASON_REVOKED = "revoked";
    public static final String REASON_SELF_IDLE = "self-idle";
    public static final String REASON_SHELL = "shell";
    public static final String REASON_LEGACY = "legacy";
    public static final String REASON_API = "api";
    public static final String REASON_PKG_DATA_CLEARED = "pkg-data-cleared";

    private static final String TAG = "CDM_DisassociationProcessor";

    private static final String SYS_PROP_DEBUG_REMOVAL_TIME_WINDOW =
@@ -94,7 +101,7 @@ public class DisassociationProcessor {
     * Disassociate an association by id.
     */
    // TODO: also revoke notification access
    public void disassociate(int id) {
    public void disassociate(int id, String reason) {
        Slog.i(TAG, "Disassociating id=[" + id + "]...");

        final AssociationInfo association = mAssociationStore.getAssociationWithCallerChecks(id);
@@ -126,7 +133,7 @@ public class DisassociationProcessor {

        // Association cleanup.
        mSystemDataTransferRequestStore.removeRequestsByAssociationId(userId, id);
        mAssociationStore.removeAssociation(association.getId());
        mAssociationStore.removeAssociation(association.getId(), reason);

        // If role is not in use by other associations, revoke the role.
        // Do not need to remove the system role since it was pre-granted by the system.
@@ -151,7 +158,7 @@ public class DisassociationProcessor {
    }

    /**
     * @deprecated Use {@link #disassociate(int)} instead.
     * @deprecated Use {@link #disassociate(int, String)} instead.
     */
    @Deprecated
    public void disassociate(int userId, String packageName, String macAddress) {
@@ -165,7 +172,7 @@ public class DisassociationProcessor {

        mAssociationStore.getAssociationWithCallerChecks(association.getId());

        disassociate(association.getId());
        disassociate(association.getId(), REASON_LEGACY);
    }

    @SuppressLint("MissingPermission")
@@ -223,7 +230,7 @@ public class DisassociationProcessor {

            Slog.i(TAG, "Removing inactive self-managed association=[" + association.toShortString()
                    + "].");
            disassociate(id);
            disassociate(id, REASON_SELF_IDLE);
        }
    }

@@ -234,7 +241,7 @@ public class DisassociationProcessor {
     *
     * Lastly remove the role holder for the revoked associations for the same packages.
     *
     * @see #disassociate(int)
     * @see #disassociate(int, String)
     */
    private class OnPackageVisibilityChangeListener implements
            ActivityManager.OnUidImportanceListener {
@@ -260,7 +267,7 @@ public class DisassociationProcessor {
            int userId = UserHandle.getUserId(uid);
            for (AssociationInfo association : mAssociationStore.getRevokedAssociations(userId,
                    packageName)) {
                disassociate(association.getId());
                disassociate(association.getId(), REASON_REVOKED);
            }

            if (mAssociationStore.getRevokedAssociations().isEmpty()) {