summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Guojing Yuan <guojing@google.com> 2025-02-11 19:34:07 +0000
committer Guojing Yuan <guojing@google.com> 2025-02-18 19:29:16 +0000
commit9fc76869556a3cee13c3e25b42cfb0e731c6492c (patch)
tree7ea019e48c68f062a1281ca81414d0b0f92951ac
parent17c69415fd6a9c4e76620a0ab13ece33b168c98f (diff)
[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
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java6
-rw-r--r--services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java6
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationDiskStore.java42
-rw-r--r--services/companion/java/com/android/server/companion/association/AssociationStore.java12
-rw-r--r--services/companion/java/com/android/server/companion/association/DisassociationProcessor.java21
5 files changed, 75 insertions, 12 deletions
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
index 0e2e50589217..8fbee1d42445 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceManagerService.java
@@ -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
diff --git a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
index 280494544e3b..edfe54d21760 100644
--- a/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
+++ b/services/companion/java/com/android/server/companion/CompanionDeviceShellCommand.java
@@ -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;
diff --git a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
index f2d019bde703..ce7dcd0fa1d4 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationDiskStore.java
@@ -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) {
diff --git a/services/companion/java/com/android/server/companion/association/AssociationStore.java b/services/companion/java/com/android/server/companion/association/AssociationStore.java
index 757abd927ac8..f70c434e6b46 100644
--- a/services/companion/java/com/android/server/companion/association/AssociationStore.java
+++ b/services/companion/java/com/android/server/companion/association/AssociationStore.java
@@ -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) {
diff --git a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
index 150e8da5f614..248056f32a4f 100644
--- a/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
+++ b/services/companion/java/com/android/server/companion/association/DisassociationProcessor.java
@@ -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()) {