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