Cache CDM associations

Maintain an up-to-date in-memory cache of the CDM associations for performance

Test: atest CompanionDeviceManagerTest
Change-Id: I7125994c2bad50615b38cf71fc9d15f6c7c60ac0
This commit is contained in:
Eugene Susla
2020-09-14 14:25:59 -07:00
parent 1c5bb26db0
commit 4b178ad36c

View File

@@ -17,7 +17,8 @@
package com.android.server.companion;
import static com.android.internal.util.CollectionUtils.size;
import static com.android.internal.util.CollectionUtils.emptyIfNull;
import static com.android.internal.util.CollectionUtils.forEach;
import static com.android.internal.util.FunctionalUtils.uncheckExceptions;
import static com.android.internal.util.Preconditions.checkArgument;
import static com.android.internal.util.Preconditions.checkNotNull;
@@ -72,6 +73,7 @@ import android.util.Log;
import android.util.Slog;
import android.util.Xml;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.app.IAppOpsService;
import com.android.internal.content.PackageMonitor;
import com.android.internal.infra.AndroidFuture;
@@ -95,8 +97,10 @@ import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
@@ -142,6 +146,9 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private final Object mLock = new Object();
@GuardedBy("mLock")
private @Nullable Set<Association> mCachedAssociations = null;
public CompanionDeviceManagerService(Context context) {
super(context);
mImpl = new CompanionDeviceManagerImpl();
@@ -176,7 +183,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@Override
public void onPackageModified(String packageName) {
int userId = getChangingUserId();
if (!ArrayUtils.isEmpty(readAllAssociations(userId, packageName))) {
if (!ArrayUtils.isEmpty(getAllAssociations(userId, packageName))) {
updateSpecialAccessPermissionForAssociatedPackage(packageName, userId);
}
}
@@ -192,7 +199,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
@Override
public void onUserUnlocking(@NonNull TargetUser user) {
int userHandle = user.getUserIdentifier();
Set<Association> associations = readAllAssociations(userHandle);
Set<Association> associations = getAllAssociations(userHandle);
if (associations == null || associations.isEmpty()) {
return;
}
@@ -222,7 +229,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
try {
Set<Association> associations = readAllAssociations(userId);
Set<Association> associations = getAllAssociations(userId);
if (associations == null) {
continue;
}
@@ -342,7 +349,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
checkUsesFeature(callingPackage, getCallingUserId());
}
return new ArrayList<>(CollectionUtils.map(
readAllAssociations(userId, callingPackage),
getAllAssociations(userId, callingPackage),
a -> a.getDeviceMacAddress()));
}
@@ -353,7 +360,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
+ android.Manifest.permission.MANAGE_COMPANION_DEVICES);
}
return new ArrayList<>(readAllAssociations(userId, null /* packageFilter */));
return new ArrayList<>(getAllAssociations(userId, null /* packageFilter */));
}
//TODO also revoke notification access
@@ -437,14 +444,14 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
return CollectionUtils.any(
readAllAssociations(userId, packageName),
getAllAssociations(userId, packageName),
a -> Objects.equals(a.getDeviceMacAddress(), macAddress));
}
private void checkCanCallNotificationApi(String callingPackage) throws RemoteException {
checkCallerIsSystemOr(callingPackage);
int userId = getCallingUserId();
checkState(!ArrayUtils.isEmpty(readAllAssociations(userId, callingPackage)),
checkState(!ArrayUtils.isEmpty(getAllAssociations(userId, callingPackage)),
"App must have an association before calling this API");
checkUsesFeature(callingPackage, userId);
}
@@ -472,6 +479,21 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
throws RemoteException {
new ShellCmd().exec(this, in, out, err, args, callback, resultReceiver);
}
@Override
public void dump(@NonNull FileDescriptor fd,
@NonNull PrintWriter fout,
@Nullable String[] args) {
fout.append("Companion Device Associations:").append('\n');
synchronized (mLock) {
forEach(mCachedAssociations, a -> {
fout.append(" ")
.append("u").append("" + a.getUserId()).append(": ")
.append(a.getPackageName()).append(" - ")
.append(a.getDeviceMacAddress()).append('\n');
});
}
}
}
private static int getCallingUserId() {
@@ -581,20 +603,37 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
private void updateAssociations(Function<Set<Association>, Set<Association>> update,
int userId) {
final AtomicFile file = getStorageFileForUser(userId);
synchronized (file) {
Set<Association> associations = readAllAssociations(userId);
final Set<Association> old = CollectionUtils.copyOf(associations);
synchronized (mLock) {
final Set<Association> old = getAllAssociations(userId);
Set<Association> associations = new ArraySet<>(old);
associations = update.apply(associations);
if (size(old) == size(associations)) return;
Set<Association> finalAssociations = associations;
Set<String> companionAppPackages = new HashSet<>();
for (Association association : finalAssociations) {
for (Association association : associations) {
companionAppPackages.add(association.getPackageName());
}
file.write((out) -> {
if (DEBUG) {
Slog.i(LOG_TAG, "Updating associations: " + old + " --> " + associations);
}
mCachedAssociations = Collections.unmodifiableSet(associations);
BackgroundThread.getHandler().sendMessage(PooledLambda.obtainMessage(
CompanionDeviceManagerService::persistAssociations,
this, associations, userId));
ActivityTaskManagerInternal atmInternal = LocalServices.getService(
ActivityTaskManagerInternal.class);
atmInternal.setCompanionAppPackages(userId, companionAppPackages);
}
}
private void persistAssociations(Set<Association> associations, int userId) {
if (DEBUG) {
Slog.i(LOG_TAG, "Writing associations to disk: " + associations);
}
final AtomicFile file = getStorageFileForUser(userId);
synchronized (file) {
file.write(out -> {
XmlSerializer xml = Xml.newSerializer();
try {
xml.setOutput(out, StandardCharsets.UTF_8.name());
@@ -602,7 +641,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
xml.startDocument(null, true);
xml.startTag(null, XML_TAG_ASSOCIATIONS);
CollectionUtils.forEach(finalAssociations, association -> {
forEach(associations, association -> {
xml.startTag(null, XML_TAG_ASSOCIATION)
.attribute(null, XML_ATTR_PACKAGE, association.getPackageName())
.attribute(null, XML_ATTR_DEVICE, association.getDeviceMacAddress())
@@ -615,11 +654,7 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
Slog.e(LOG_TAG, "Error while writing associations file", e);
throw ExceptionUtils.propagate(e);
}
});
ActivityTaskManagerInternal atmInternal = LocalServices.getService(
ActivityTaskManagerInternal.class);
atmInternal.setCompanionAppPackages(userId, companionAppPackages);
}
}
@@ -632,12 +667,27 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
}
@Nullable
private Set<Association> readAllAssociations(int userId) {
return readAllAssociations(userId, null);
private Set<Association> getAllAssociations(int userId) {
synchronized (mLock) {
if (mCachedAssociations == null) {
mCachedAssociations = Collections.unmodifiableSet(
emptyIfNull(readAllAssociations(userId)));
if (DEBUG) {
Slog.i(LOG_TAG, "Read associations from disk: " + mCachedAssociations);
}
}
return mCachedAssociations;
}
}
@Nullable
private Set<Association> readAllAssociations(int userId, @Nullable String packageFilter) {
private Set<Association> getAllAssociations(int userId, @Nullable String packageFilter) {
return CollectionUtils.filter(
getAllAssociations(userId),
a -> Objects.equals(packageFilter, a.getPackageName()));
}
private Set<Association> readAllAssociations(int userId) {
final AtomicFile file = getStorageFileForUser(userId);
if (!file.getBaseFile().exists()) return null;
@@ -656,7 +706,6 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
final String deviceAddress = parser.getAttributeValue(null, XML_ATTR_DEVICE);
if (appPackage == null || deviceAddress == null) continue;
if (packageFilter != null && !packageFilter.equals(appPackage)) continue;
result = ArrayUtils.add(result,
new Association(userId, deviceAddress, appPackage));
@@ -684,8 +733,8 @@ public class CompanionDeviceManagerService extends SystemService implements Bind
public int onCommand(String cmd) {
switch (cmd) {
case "list": {
CollectionUtils.forEach(
readAllAssociations(getNextArgInt()),
forEach(
getAllAssociations(getNextArgInt()),
a -> getOutPrintWriter()
.println(a.getPackageName() + " " + a.getDeviceMacAddress()));
} break;