Merge "Add association ID parameter to createVirtualDevice"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9e1398c24d
@@ -22,5 +22,16 @@ package android.companion.virtual;
|
||||
* @hide
|
||||
*/
|
||||
interface IVirtualDevice {
|
||||
|
||||
/**
|
||||
* Returns the association ID for this virtual device.
|
||||
*
|
||||
* @see AssociationInfo#getId()
|
||||
*/
|
||||
int getAssociationId();
|
||||
|
||||
/**
|
||||
* Closes the virtual device and frees all associated resources.
|
||||
*/
|
||||
void close();
|
||||
}
|
||||
|
||||
@@ -25,5 +25,14 @@ import android.companion.virtual.IVirtualDevice;
|
||||
*/
|
||||
interface IVirtualDeviceManager {
|
||||
|
||||
IVirtualDevice createVirtualDevice();
|
||||
/**
|
||||
* Creates a virtual device that can be used to create virtual displays and stream contents.
|
||||
*
|
||||
* @param token The binder token created by the caller of this API.
|
||||
* @param packageName The package name of the caller. Implementation of this method must verify
|
||||
* that this belongs to the calling UID.
|
||||
* @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
|
||||
* CDM. Virtual devices must have a corresponding association with CDM in order to be created.
|
||||
*/
|
||||
IVirtualDevice createVirtualDevice(in IBinder token, String packageName, int associationId);
|
||||
}
|
||||
|
||||
@@ -21,7 +21,9 @@ import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.SystemApi;
|
||||
import android.annotation.SystemService;
|
||||
import android.companion.AssociationInfo;
|
||||
import android.content.Context;
|
||||
import android.os.Binder;
|
||||
import android.os.RemoteException;
|
||||
|
||||
/**
|
||||
@@ -49,14 +51,18 @@ public final class VirtualDeviceManager {
|
||||
/**
|
||||
* Creates a virtual device.
|
||||
*
|
||||
* @param associationId The association ID as returned by {@link AssociationInfo#getId()} from
|
||||
* Companion Device Manager. Virtual devices must have a corresponding association with CDM in
|
||||
* order to be created.
|
||||
* @hide
|
||||
*/
|
||||
@RequiresPermission(android.Manifest.permission.CREATE_VIRTUAL_DEVICE)
|
||||
@Nullable
|
||||
public VirtualDevice createVirtualDevice() {
|
||||
// TODO(b/194949534): Add CDM association ID here and unhide this API
|
||||
public VirtualDevice createVirtualDevice(int associationId) {
|
||||
// TODO(b/194949534): Unhide this API
|
||||
try {
|
||||
IVirtualDevice virtualDevice = mService.createVirtualDevice();
|
||||
IVirtualDevice virtualDevice = mService.createVirtualDevice(
|
||||
new Binder(), mContext.getPackageName(), associationId);
|
||||
return new VirtualDevice(mContext, virtualDevice);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (C) 2021 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.companion.virtual;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Slog;
|
||||
|
||||
/**
|
||||
* Utility methods for checking permissions required for VirtualDeviceManager operations.
|
||||
*/
|
||||
class PermissionUtils {
|
||||
|
||||
private static final String LOG_TAG = "VDM.PermissionUtils";
|
||||
|
||||
/**
|
||||
* Verifies whether the calling package name matches the calling app uid.
|
||||
*
|
||||
* @param context the context
|
||||
* @param callingPackage the calling application package name
|
||||
* @param callingUid the calling application uid
|
||||
* @return {@code true} if the package name matches the calling app uid, {@code false} otherwise
|
||||
*/
|
||||
public static boolean validatePackageName(Context context, String callingPackage,
|
||||
int callingUid) {
|
||||
try {
|
||||
int packageUid = context.getPackageManager().getPackageUid(callingPackage, 0);
|
||||
if (packageUid != callingUid) {
|
||||
Slog.e(LOG_TAG, "validatePackageName: App with package name " + callingPackage
|
||||
+ " is UID " + packageUid + " but caller is " + callingUid);
|
||||
return false;
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Slog.e(LOG_TAG, "validatePackageName: App with package name " + callingPackage
|
||||
+ " does not exist");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -19,13 +19,18 @@ package com.android.server.companion.virtual;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.companion.AssociationInfo;
|
||||
import android.companion.CompanionDeviceManager;
|
||||
import android.companion.CompanionDeviceManager.OnAssociationsChangedListener;
|
||||
import android.companion.virtual.IVirtualDevice;
|
||||
import android.companion.virtual.IVirtualDeviceManager;
|
||||
import android.content.Context;
|
||||
import android.os.IBinder;
|
||||
import android.os.Parcel;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ExceptionUtils;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.android.internal.annotations.GuardedBy;
|
||||
import com.android.internal.util.DumpUtils;
|
||||
@@ -33,7 +38,8 @@ import com.android.server.SystemService;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
|
||||
/** @hide */
|
||||
@@ -42,9 +48,30 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
|
||||
private static final boolean DEBUG = false;
|
||||
private static final String LOG_TAG = "VirtualDeviceManagerService";
|
||||
private final Object mVirtualDeviceManagerLock = new Object();
|
||||
private final VirtualDeviceManagerImpl mImpl;
|
||||
@GuardedBy("mVirtualDevices")
|
||||
private final ArrayList<VirtualDeviceImpl> mVirtualDevices = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Mapping from CDM association IDs to virtual devices. Only one virtual device is allowed for
|
||||
* each CDM associated device.
|
||||
*/
|
||||
@GuardedBy("mVirtualDeviceManagerLock")
|
||||
private final SparseArray<VirtualDeviceImpl> mVirtualDevices = new SparseArray<>();
|
||||
|
||||
/**
|
||||
* Mapping from user ID to CDM associations. The associations come from
|
||||
* {@link CompanionDeviceManager#getAllAssociations()}, which contains associations across all
|
||||
* packages.
|
||||
*/
|
||||
private final ConcurrentHashMap<Integer, List<AssociationInfo>> mAllAssociations =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Mapping from user ID to its change listener. The listeners are added when the user is
|
||||
* started and removed when the user stops.
|
||||
*/
|
||||
private final SparseArray<OnAssociationsChangedListener> mOnAssociationsChangedListeners =
|
||||
new SparseArray<>();
|
||||
|
||||
public VirtualDeviceManagerService(Context context) {
|
||||
super(context);
|
||||
@@ -56,30 +83,116 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
publishBinderService(Context.VIRTUAL_DEVICE_SERVICE, mImpl);
|
||||
}
|
||||
|
||||
private class VirtualDeviceImpl extends IVirtualDevice.Stub {
|
||||
@Override
|
||||
public void onUserStarting(@NonNull TargetUser user) {
|
||||
super.onUserStarting(user);
|
||||
synchronized (mVirtualDeviceManagerLock) {
|
||||
final CompanionDeviceManager cdm = getContext()
|
||||
.createContextAsUser(user.getUserHandle(), 0)
|
||||
.getSystemService(CompanionDeviceManager.class);
|
||||
final int userId = user.getUserIdentifier();
|
||||
mAllAssociations.put(userId, cdm.getAllAssociations());
|
||||
OnAssociationsChangedListener listener =
|
||||
associations -> mAllAssociations.put(userId, associations);
|
||||
mOnAssociationsChangedListeners.put(userId, listener);
|
||||
cdm.addOnAssociationsChangedListener(Runnable::run, listener);
|
||||
}
|
||||
}
|
||||
|
||||
private VirtualDeviceImpl() {}
|
||||
@Override
|
||||
public void onUserStopping(@NonNull TargetUser user) {
|
||||
super.onUserStopping(user);
|
||||
synchronized (mVirtualDeviceManagerLock) {
|
||||
int userId = user.getUserIdentifier();
|
||||
mAllAssociations.remove(userId);
|
||||
final CompanionDeviceManager cdm = getContext().createContextAsUser(
|
||||
user.getUserHandle(), 0)
|
||||
.getSystemService(CompanionDeviceManager.class);
|
||||
OnAssociationsChangedListener listener = mOnAssociationsChangedListeners.get(userId);
|
||||
if (listener != null) {
|
||||
cdm.removeOnAssociationsChangedListener(listener);
|
||||
mOnAssociationsChangedListeners.remove(userId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class VirtualDeviceImpl extends IVirtualDevice.Stub implements IBinder.DeathRecipient {
|
||||
|
||||
private final AssociationInfo mAssociationInfo;
|
||||
|
||||
private VirtualDeviceImpl(IBinder token, AssociationInfo associationInfo) {
|
||||
mAssociationInfo = associationInfo;
|
||||
try {
|
||||
token.linkToDeath(this, 0);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
}
|
||||
mVirtualDevices.put(associationInfo.getId(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAssociationId() {
|
||||
return mAssociationInfo.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
synchronized (mVirtualDevices) {
|
||||
mVirtualDevices.remove(this);
|
||||
synchronized (mVirtualDeviceManagerLock) {
|
||||
mVirtualDevices.remove(mAssociationInfo.getId());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void binderDied() {
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
class VirtualDeviceManagerImpl extends IVirtualDeviceManager.Stub {
|
||||
|
||||
@Override
|
||||
public IVirtualDevice createVirtualDevice() {
|
||||
public IVirtualDevice createVirtualDevice(
|
||||
IBinder token, String packageName, int associationId) {
|
||||
getContext().enforceCallingOrSelfPermission(
|
||||
android.Manifest.permission.CREATE_VIRTUAL_DEVICE,
|
||||
"createVirtualDevice");
|
||||
VirtualDeviceImpl virtualDevice = new VirtualDeviceImpl();
|
||||
synchronized (mVirtualDevices) {
|
||||
mVirtualDevices.add(virtualDevice);
|
||||
if (!PermissionUtils.validatePackageName(getContext(), packageName, getCallingUid())) {
|
||||
throw new SecurityException(
|
||||
"Package name " + packageName + " does not belong to calling uid "
|
||||
+ getCallingUid());
|
||||
}
|
||||
return virtualDevice;
|
||||
AssociationInfo associationInfo = getAssociationInfo(packageName, associationId);
|
||||
if (associationInfo == null) {
|
||||
throw new IllegalArgumentException("No association with ID " + associationId);
|
||||
}
|
||||
synchronized (mVirtualDeviceManagerLock) {
|
||||
if (mVirtualDevices.contains(associationId)) {
|
||||
throw new IllegalStateException(
|
||||
"Virtual device for association ID " + associationId
|
||||
+ " already exists");
|
||||
}
|
||||
return new VirtualDeviceImpl(token, associationInfo);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AssociationInfo getAssociationInfo(String packageName, int associationId) {
|
||||
final int callingUserId = getCallingUserHandle().getIdentifier();
|
||||
final List<AssociationInfo> associations =
|
||||
mAllAssociations.get(callingUserId);
|
||||
if (associations != null) {
|
||||
final int associationSize = associations.size();
|
||||
for (int i = 0; i < associationSize; i++) {
|
||||
AssociationInfo associationInfo = associations.get(i);
|
||||
if (associationInfo.belongsToPackage(callingUserId, packageName)
|
||||
&& associationId == associationInfo.getId()) {
|
||||
return associationInfo;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Slog.w(LOG_TAG, "No associations for user " + callingUserId);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -101,9 +214,10 @@ public class VirtualDeviceManagerService extends SystemService {
|
||||
return;
|
||||
}
|
||||
fout.println("Created virtual devices: ");
|
||||
synchronized (mVirtualDevices) {
|
||||
for (VirtualDeviceImpl virtualDevice : mVirtualDevices) {
|
||||
fout.println(virtualDevice.toString());
|
||||
synchronized (mVirtualDeviceManagerLock) {
|
||||
for (int i = 0; i < mVirtualDevices.size(); i++) {
|
||||
VirtualDeviceImpl virtualDevice = mVirtualDevices.valueAt(i);
|
||||
fout.printf("%d: %s\n", mVirtualDevices.keyAt(i), virtualDevice);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user