Merge "MIDI: Add virtual devices per user" into udc-qpr-dev
This commit is contained in:
@@ -16,7 +16,9 @@
|
|||||||
|
|
||||||
package com.android.server.midi;
|
package com.android.server.midi;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
import android.annotation.NonNull;
|
import android.annotation.NonNull;
|
||||||
|
import android.annotation.RequiresPermission;
|
||||||
import android.bluetooth.BluetoothDevice;
|
import android.bluetooth.BluetoothDevice;
|
||||||
import android.bluetooth.BluetoothUuid;
|
import android.bluetooth.BluetoothUuid;
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
@@ -48,6 +50,7 @@ import android.os.ParcelUuid;
|
|||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.os.UserManager;
|
||||||
import android.util.EventLog;
|
import android.util.EventLog;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@@ -81,6 +84,11 @@ import java.util.concurrent.atomic.AtomicLong;
|
|||||||
// 2. synchronized (mDeviceConnections)
|
// 2. synchronized (mDeviceConnections)
|
||||||
//TODO Introduce a single lock object to lock the whole state and avoid the requirement above.
|
//TODO Introduce a single lock object to lock the whole state and avoid the requirement above.
|
||||||
|
|
||||||
|
// All users should be able to connect to USB and Bluetooth MIDI devices.
|
||||||
|
// All users can create can install an app that provides, a Virtual MIDI Device Service.
|
||||||
|
// Users can not open virtual MIDI devices created by other users.
|
||||||
|
// getDevices() surfaces devices that can be opened by that user.
|
||||||
|
// openDevice() rejects devices that are cannot be opened by that user.
|
||||||
public class MidiService extends IMidiManager.Stub {
|
public class MidiService extends IMidiManager.Stub {
|
||||||
|
|
||||||
public static class Lifecycle extends SystemService {
|
public static class Lifecycle extends SystemService {
|
||||||
@@ -97,10 +105,21 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS},
|
||||||
|
anyOf = {Manifest.permission.QUERY_USERS,
|
||||||
|
Manifest.permission.CREATE_USERS,
|
||||||
|
Manifest.permission.MANAGE_USERS})
|
||||||
|
public void onUserStarting(@NonNull TargetUser user) {
|
||||||
|
mMidiService.onStartOrUnlockUser(user, false /* matchDirectBootUnaware */);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS},
|
||||||
|
anyOf = {Manifest.permission.QUERY_USERS,
|
||||||
|
Manifest.permission.CREATE_USERS,
|
||||||
|
Manifest.permission.MANAGE_USERS})
|
||||||
public void onUserUnlocking(@NonNull TargetUser user) {
|
public void onUserUnlocking(@NonNull TargetUser user) {
|
||||||
if (user.getUserIdentifier() == UserHandle.USER_SYSTEM) {
|
mMidiService.onStartOrUnlockUser(user, true /* matchDirectBootUnaware */);
|
||||||
mMidiService.onUnlockUser();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -134,6 +153,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
private int mNextDeviceId = 1;
|
private int mNextDeviceId = 1;
|
||||||
|
|
||||||
private final PackageManager mPackageManager;
|
private final PackageManager mPackageManager;
|
||||||
|
private final UserManager mUserManager;
|
||||||
|
|
||||||
private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
|
private static final String MIDI_LEGACY_STRING = "MIDI 1.0";
|
||||||
private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
|
private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0";
|
||||||
@@ -159,21 +179,24 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>();
|
private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>();
|
||||||
|
|
||||||
// PackageMonitor for listening to package changes
|
// PackageMonitor for listening to package changes
|
||||||
|
// uid is the uid of the package so use getChangingUserId() to fetch the userId.
|
||||||
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
|
private final PackageMonitor mPackageMonitor = new PackageMonitor() {
|
||||||
@Override
|
@Override
|
||||||
|
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
|
||||||
public void onPackageAdded(String packageName, int uid) {
|
public void onPackageAdded(String packageName, int uid) {
|
||||||
addPackageDeviceServers(packageName);
|
addPackageDeviceServers(packageName, getChangingUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
|
||||||
public void onPackageModified(String packageName) {
|
public void onPackageModified(String packageName) {
|
||||||
removePackageDeviceServers(packageName);
|
removePackageDeviceServers(packageName, getChangingUserId());
|
||||||
addPackageDeviceServers(packageName);
|
addPackageDeviceServers(packageName, getChangingUserId());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPackageRemoved(String packageName, int uid) {
|
public void onPackageRemoved(String packageName, int uid) {
|
||||||
removePackageDeviceServers(packageName);
|
removePackageDeviceServers(packageName, getChangingUserId());
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -202,6 +225,10 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
return mUid;
|
return mUid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getUserId() {
|
||||||
|
return UserHandle.getUserId(mUid);
|
||||||
|
}
|
||||||
|
|
||||||
public void addListener(IMidiDeviceListener listener) {
|
public void addListener(IMidiDeviceListener listener) {
|
||||||
if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) {
|
if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) {
|
||||||
throw new SecurityException(
|
throw new SecurityException(
|
||||||
@@ -219,8 +246,12 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback) {
|
@RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL,
|
||||||
Log.d(TAG, "addDeviceConnection() device:" + device);
|
Manifest.permission.INTERACT_ACROSS_USERS,
|
||||||
|
Manifest.permission.INTERACT_ACROSS_PROFILES})
|
||||||
|
public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback,
|
||||||
|
int userId) {
|
||||||
|
Log.d(TAG, "addDeviceConnection() device:" + device + " userId:" + userId);
|
||||||
if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) {
|
if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) {
|
||||||
Log.i(TAG, "too many MIDI connections for UID = " + mUid);
|
Log.i(TAG, "too many MIDI connections for UID = " + mUid);
|
||||||
throw new SecurityException(
|
throw new SecurityException(
|
||||||
@@ -228,7 +259,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
DeviceConnection connection = new DeviceConnection(device, this, callback);
|
DeviceConnection connection = new DeviceConnection(device, this, callback);
|
||||||
mDeviceConnections.put(connection.getToken(), connection);
|
mDeviceConnections.put(connection.getToken(), connection);
|
||||||
device.addDeviceConnection(connection);
|
device.addDeviceConnection(connection, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
// called from MidiService.closeDevice()
|
// called from MidiService.closeDevice()
|
||||||
@@ -251,8 +282,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deviceAdded(Device device) {
|
public void deviceAdded(Device device) {
|
||||||
// ignore private devices that our client cannot access
|
// ignore devices that our client cannot access
|
||||||
if (!device.isUidAllowed(mUid)) return;
|
if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return;
|
||||||
|
|
||||||
MidiDeviceInfo deviceInfo = device.getDeviceInfo();
|
MidiDeviceInfo deviceInfo = device.getDeviceInfo();
|
||||||
try {
|
try {
|
||||||
@@ -265,8 +296,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deviceRemoved(Device device) {
|
public void deviceRemoved(Device device) {
|
||||||
// ignore private devices that our client cannot access
|
// ignore devices that our client cannot access
|
||||||
if (!device.isUidAllowed(mUid)) return;
|
if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return;
|
||||||
|
|
||||||
MidiDeviceInfo deviceInfo = device.getDeviceInfo();
|
MidiDeviceInfo deviceInfo = device.getDeviceInfo();
|
||||||
try {
|
try {
|
||||||
@@ -279,8 +310,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void deviceStatusChanged(Device device, MidiDeviceStatus status) {
|
public void deviceStatusChanged(Device device, MidiDeviceStatus status) {
|
||||||
// ignore private devices that our client cannot access
|
// ignore devices that our client cannot access
|
||||||
if (!device.isUidAllowed(mUid)) return;
|
if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (IMidiDeviceListener listener : mListeners.values()) {
|
for (IMidiDeviceListener listener : mListeners.values()) {
|
||||||
@@ -354,6 +385,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
private final ServiceInfo mServiceInfo;
|
private final ServiceInfo mServiceInfo;
|
||||||
// UID of device implementation
|
// UID of device implementation
|
||||||
private final int mUid;
|
private final int mUid;
|
||||||
|
// User Id of the app. Only used for virtual devices
|
||||||
|
private final int mUserId;
|
||||||
|
|
||||||
// ServiceConnection for implementing Service (virtual devices only)
|
// ServiceConnection for implementing Service (virtual devices only)
|
||||||
// mServiceConnection is non-null when connected or attempting to connect to the service
|
// mServiceConnection is non-null when connected or attempting to connect to the service
|
||||||
@@ -375,19 +408,24 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
private AtomicInteger mTotalOutputBytes = new AtomicInteger();
|
private AtomicInteger mTotalOutputBytes = new AtomicInteger();
|
||||||
|
|
||||||
public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo,
|
public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo,
|
||||||
ServiceInfo serviceInfo, int uid) {
|
ServiceInfo serviceInfo, int uid, int userId) {
|
||||||
mDeviceInfo = deviceInfo;
|
mDeviceInfo = deviceInfo;
|
||||||
mServiceInfo = serviceInfo;
|
mServiceInfo = serviceInfo;
|
||||||
mUid = uid;
|
mUid = uid;
|
||||||
|
mUserId = userId;
|
||||||
mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable(
|
mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable(
|
||||||
MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);;
|
MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);;
|
||||||
setDeviceServer(server);
|
setDeviceServer(server);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@RequiresPermission(anyOf = {Manifest.permission.QUERY_USERS,
|
||||||
|
Manifest.permission.CREATE_USERS,
|
||||||
|
Manifest.permission.MANAGE_USERS})
|
||||||
public Device(BluetoothDevice bluetoothDevice) {
|
public Device(BluetoothDevice bluetoothDevice) {
|
||||||
mBluetoothDevice = bluetoothDevice;
|
mBluetoothDevice = bluetoothDevice;
|
||||||
mServiceInfo = null;
|
mServiceInfo = null;
|
||||||
mUid = mBluetoothServiceUid;
|
mUid = mBluetoothServiceUid;
|
||||||
|
mUserId = mUserManager.getMainUser().getIdentifier();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDeviceServer(IMidiDeviceServer server) {
|
private void setDeviceServer(IMidiDeviceServer server) {
|
||||||
@@ -468,11 +506,22 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
return mUid;
|
return mUid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getUserId() {
|
||||||
|
return mUserId;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isUidAllowed(int uid) {
|
public boolean isUidAllowed(int uid) {
|
||||||
return (!mDeviceInfo.isPrivate() || mUid == uid);
|
return (!mDeviceInfo.isPrivate() || mUid == uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addDeviceConnection(DeviceConnection connection) {
|
public boolean isUserIdAllowed(int userId) {
|
||||||
|
return (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL || mUserId == userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
@RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL,
|
||||||
|
Manifest.permission.INTERACT_ACROSS_USERS,
|
||||||
|
Manifest.permission.INTERACT_ACROSS_PROFILES})
|
||||||
|
public void addDeviceConnection(DeviceConnection connection, int userId) {
|
||||||
Log.d(TAG, "addDeviceConnection() [A] connection:" + connection);
|
Log.d(TAG, "addDeviceConnection() [A] connection:" + connection);
|
||||||
synchronized (mDeviceConnections) {
|
synchronized (mDeviceConnections) {
|
||||||
mDeviceConnectionsAdded.incrementAndGet();
|
mDeviceConnectionsAdded.incrementAndGet();
|
||||||
@@ -537,8 +586,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
new ComponentName(mServiceInfo.packageName, mServiceInfo.name));
|
new ComponentName(mServiceInfo.packageName, mServiceInfo.name));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mContext.bindService(intent, mServiceConnection,
|
if (!mContext.bindServiceAsUser(intent, mServiceConnection,
|
||||||
Context.BIND_AUTO_CREATE)) {
|
Context.BIND_AUTO_CREATE, UserHandle.of(mUserId))) {
|
||||||
Log.e(TAG, "Unable to bind service: " + intent);
|
Log.e(TAG, "Unable to bind service: " + intent);
|
||||||
setDeviceServer(null);
|
setDeviceServer(null);
|
||||||
mServiceConnection = null;
|
mServiceConnection = null;
|
||||||
@@ -886,6 +935,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
public MidiService(Context context) {
|
public MidiService(Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mPackageManager = context.getPackageManager();
|
mPackageManager = context.getPackageManager();
|
||||||
|
mUserManager = mContext.getSystemService(UserManager.class);
|
||||||
|
mPackageMonitor.register(mContext, null, UserHandle.ALL, true);
|
||||||
|
|
||||||
// TEMPORARY - Disable BTL-MIDI
|
// TEMPORARY - Disable BTL-MIDI
|
||||||
//FIXME - b/25689266
|
//FIXME - b/25689266
|
||||||
@@ -913,32 +964,41 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
// mNonMidiUUIDs.add(BluetoothUuid.BATTERY);
|
// mNonMidiUUIDs.add(BluetoothUuid.BATTERY);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onUnlockUser() {
|
@RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS},
|
||||||
mPackageMonitor.register(mContext, null, true);
|
anyOf = {Manifest.permission.QUERY_USERS,
|
||||||
|
Manifest.permission.CREATE_USERS,
|
||||||
|
Manifest.permission.MANAGE_USERS})
|
||||||
|
private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) {
|
||||||
|
Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: "
|
||||||
|
+ matchDirectBootUnaware);
|
||||||
Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
|
Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE);
|
||||||
List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices(intent,
|
int resolveFlags = PackageManager.GET_META_DATA;
|
||||||
PackageManager.GET_META_DATA);
|
if (matchDirectBootUnaware) {
|
||||||
|
resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE;
|
||||||
|
}
|
||||||
|
List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent,
|
||||||
|
resolveFlags, user.getUserIdentifier());
|
||||||
if (resolveInfos != null) {
|
if (resolveInfos != null) {
|
||||||
int count = resolveInfos.size();
|
int count = resolveInfos.size();
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
|
ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo;
|
||||||
if (serviceInfo != null) {
|
if (serviceInfo != null) {
|
||||||
addPackageDeviceServer(serviceInfo);
|
addPackageDeviceServer(serviceInfo, user.getUserIdentifier());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PackageInfo info;
|
if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) {
|
||||||
try {
|
PackageInfo info;
|
||||||
info = mPackageManager.getPackageInfo(MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0);
|
try {
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
info = mPackageManager.getPackageInfoAsUser(
|
||||||
info = null;
|
MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0, user.getUserIdentifier());
|
||||||
}
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
if (info != null && info.applicationInfo != null) {
|
info = null;
|
||||||
mBluetoothServiceUid = info.applicationInfo.uid;
|
}
|
||||||
} else {
|
if (info != null && info.applicationInfo != null) {
|
||||||
mBluetoothServiceUid = -1;
|
mBluetoothServiceUid = info.applicationInfo.uid;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -960,10 +1020,11 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
|
|
||||||
// Inform listener of the status of all known devices.
|
// Inform listener of the status of all known devices.
|
||||||
private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) {
|
private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) {
|
||||||
|
int userId = UserHandle.getUserId(uid);
|
||||||
synchronized (mDevicesByInfo) {
|
synchronized (mDevicesByInfo) {
|
||||||
for (Device device : mDevicesByInfo.values()) {
|
for (Device device : mDevicesByInfo.values()) {
|
||||||
// ignore private devices that our client cannot access
|
// ignore devices that our client cannot access
|
||||||
if (device.isUidAllowed(uid)) {
|
if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) {
|
||||||
try {
|
try {
|
||||||
MidiDeviceStatus status = device.getDeviceStatus();
|
MidiDeviceStatus status = device.getDeviceStatus();
|
||||||
if (status != null) {
|
if (status != null) {
|
||||||
@@ -989,10 +1050,11 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
public MidiDeviceInfo[] getDevicesForTransport(int transport) {
|
public MidiDeviceInfo[] getDevicesForTransport(int transport) {
|
||||||
ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>();
|
ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>();
|
||||||
int uid = Binder.getCallingUid();
|
int uid = Binder.getCallingUid();
|
||||||
|
int userId = getCallingUserId();
|
||||||
|
|
||||||
synchronized (mDevicesByInfo) {
|
synchronized (mDevicesByInfo) {
|
||||||
for (Device device : mDevicesByInfo.values()) {
|
for (Device device : mDevicesByInfo.values()) {
|
||||||
if (device.isUidAllowed(uid)) {
|
if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) {
|
||||||
// UMP devices have protocols that are not PROTOCOL_UNKNOWN
|
// UMP devices have protocols that are not PROTOCOL_UNKNOWN
|
||||||
if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
|
if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) {
|
||||||
if (device.getDeviceInfo().getDefaultProtocol()
|
if (device.getDeviceInfo().getDefaultProtocol()
|
||||||
@@ -1029,6 +1091,9 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
if (!device.isUidAllowed(Binder.getCallingUid())) {
|
if (!device.isUidAllowed(Binder.getCallingUid())) {
|
||||||
throw new SecurityException("Attempt to open private device with wrong UID");
|
throw new SecurityException("Attempt to open private device with wrong UID");
|
||||||
}
|
}
|
||||||
|
if (!device.isUserIdAllowed(getCallingUserId())) {
|
||||||
|
throw new SecurityException("Attempt to open virtual device with wrong user id");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
|
if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) {
|
||||||
@@ -1044,7 +1109,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
Log.i(TAG, "addDeviceConnection() [B] device:" + device);
|
Log.i(TAG, "addDeviceConnection() [B] device:" + device);
|
||||||
client.addDeviceConnection(device, callback);
|
client.addDeviceConnection(device, callback, getCallingUserId());
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(identity);
|
Binder.restoreCallingIdentity(identity);
|
||||||
}
|
}
|
||||||
@@ -1106,7 +1171,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
final long identity = Binder.clearCallingIdentity();
|
final long identity = Binder.clearCallingIdentity();
|
||||||
try {
|
try {
|
||||||
Log.i(TAG, "addDeviceConnection() [C] device:" + device);
|
Log.i(TAG, "addDeviceConnection() [C] device:" + device);
|
||||||
client.addDeviceConnection(device, callback);
|
client.addDeviceConnection(device, callback, getCallingUserId());
|
||||||
} finally {
|
} finally {
|
||||||
Binder.restoreCallingIdentity(identity);
|
Binder.restoreCallingIdentity(identity);
|
||||||
}
|
}
|
||||||
@@ -1124,6 +1189,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
|
int numOutputPorts, String[] inputPortNames, String[] outputPortNames,
|
||||||
Bundle properties, int type, int defaultProtocol) {
|
Bundle properties, int type, int defaultProtocol) {
|
||||||
int uid = Binder.getCallingUid();
|
int uid = Binder.getCallingUid();
|
||||||
|
int userId = getCallingUserId();
|
||||||
if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
|
if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) {
|
||||||
throw new SecurityException("only system can create USB devices");
|
throw new SecurityException("only system can create USB devices");
|
||||||
} else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) {
|
} else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) {
|
||||||
@@ -1133,7 +1199,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
synchronized (mDevicesByInfo) {
|
synchronized (mDevicesByInfo) {
|
||||||
return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames,
|
return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames,
|
||||||
outputPortNames, properties, server, null, false, uid,
|
outputPortNames, properties, server, null, false, uid,
|
||||||
defaultProtocol);
|
defaultProtocol, userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1210,7 +1276,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts,
|
private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts,
|
||||||
String[] inputPortNames, String[] outputPortNames, Bundle properties,
|
String[] inputPortNames, String[] outputPortNames, Bundle properties,
|
||||||
IMidiDeviceServer server, ServiceInfo serviceInfo,
|
IMidiDeviceServer server, ServiceInfo serviceInfo,
|
||||||
boolean isPrivate, int uid, int defaultProtocol) {
|
boolean isPrivate, int uid, int defaultProtocol, int userId) {
|
||||||
|
Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type);
|
||||||
|
|
||||||
// Limit the number of devices per app.
|
// Limit the number of devices per app.
|
||||||
int deviceCountForApp = 0;
|
int deviceCountForApp = 0;
|
||||||
@@ -1250,7 +1317,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (device == null) {
|
if (device == null) {
|
||||||
device = new Device(server, deviceInfo, serviceInfo, uid);
|
device = new Device(server, deviceInfo, serviceInfo, uid, userId);
|
||||||
}
|
}
|
||||||
mDevicesByInfo.put(deviceInfo, device);
|
mDevicesByInfo.put(deviceInfo, device);
|
||||||
if (bluetoothDevice != null) {
|
if (bluetoothDevice != null) {
|
||||||
@@ -1281,12 +1348,14 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addPackageDeviceServers(String packageName) {
|
@RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS)
|
||||||
|
private void addPackageDeviceServers(String packageName, int userId) {
|
||||||
PackageInfo info;
|
PackageInfo info;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
info = mPackageManager.getPackageInfo(packageName,
|
info = mPackageManager.getPackageInfoAsUser(packageName,
|
||||||
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA);
|
PackageManager.GET_SERVICES | PackageManager.GET_META_DATA
|
||||||
|
| PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId);
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
|
Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e);
|
||||||
return;
|
return;
|
||||||
@@ -1295,13 +1364,14 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
ServiceInfo[] services = info.services;
|
ServiceInfo[] services = info.services;
|
||||||
if (services == null) return;
|
if (services == null) return;
|
||||||
for (int i = 0; i < services.length; i++) {
|
for (int i = 0; i < services.length; i++) {
|
||||||
addPackageDeviceServer(services[i]);
|
addPackageDeviceServer(services[i], userId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||||
|
|
||||||
private void addPackageDeviceServer(ServiceInfo serviceInfo) {
|
private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) {
|
||||||
|
Log.d(TAG, "addPackageDeviceServer()" + userId);
|
||||||
XmlResourceParser parser = null;
|
XmlResourceParser parser = null;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1404,8 +1474,8 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
|
|
||||||
int uid;
|
int uid;
|
||||||
try {
|
try {
|
||||||
ApplicationInfo appInfo = mPackageManager.getApplicationInfo(
|
ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser(
|
||||||
serviceInfo.packageName, 0);
|
serviceInfo.packageName, 0, userId);
|
||||||
uid = appInfo.uid;
|
uid = appInfo.uid;
|
||||||
} catch (PackageManager.NameNotFoundException e) {
|
} catch (PackageManager.NameNotFoundException e) {
|
||||||
Log.e(TAG, "could not fetch ApplicationInfo for "
|
Log.e(TAG, "could not fetch ApplicationInfo for "
|
||||||
@@ -1419,7 +1489,7 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
inputPortNames.toArray(EMPTY_STRING_ARRAY),
|
inputPortNames.toArray(EMPTY_STRING_ARRAY),
|
||||||
outputPortNames.toArray(EMPTY_STRING_ARRAY),
|
outputPortNames.toArray(EMPTY_STRING_ARRAY),
|
||||||
properties, null, serviceInfo, isPrivate, uid,
|
properties, null, serviceInfo, isPrivate, uid,
|
||||||
MidiDeviceInfo.PROTOCOL_UNKNOWN);
|
MidiDeviceInfo.PROTOCOL_UNKNOWN, userId);
|
||||||
}
|
}
|
||||||
// setting properties to null signals that we are no longer
|
// setting properties to null signals that we are no longer
|
||||||
// processing a <device>
|
// processing a <device>
|
||||||
@@ -1437,12 +1507,13 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePackageDeviceServers(String packageName) {
|
private void removePackageDeviceServers(String packageName, int userId) {
|
||||||
synchronized (mDevicesByInfo) {
|
synchronized (mDevicesByInfo) {
|
||||||
Iterator<Device> iterator = mDevicesByInfo.values().iterator();
|
Iterator<Device> iterator = mDevicesByInfo.values().iterator();
|
||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
Device device = iterator.next();
|
Device device = iterator.next();
|
||||||
if (packageName.equals(device.getPackageName())) {
|
if (packageName.equals(device.getPackageName())
|
||||||
|
&& (device.getUserId() == userId)) {
|
||||||
iterator.remove();
|
iterator.remove();
|
||||||
removeDeviceLocked(device);
|
removeDeviceLocked(device);
|
||||||
}
|
}
|
||||||
@@ -1571,4 +1642,11 @@ public class MidiService extends IMidiManager.Stub {
|
|||||||
String extractUsbDeviceTag(String propertyName) {
|
String extractUsbDeviceTag(String propertyName) {
|
||||||
return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
|
return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the user id of the calling user.
|
||||||
|
*/
|
||||||
|
private int getCallingUserId() {
|
||||||
|
return UserHandle.getUserId(Binder.getCallingUid());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user