1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions an 14 * limitations under the License. 15 */ 16 17 package com.android.server.midi; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.RequiresPermission; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothUuid; 24 import android.content.BroadcastReceiver; 25 import android.content.ComponentName; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.ServiceConnection; 29 import android.content.pm.ApplicationInfo; 30 import android.content.pm.PackageInfo; 31 import android.content.pm.PackageManager; 32 import android.content.pm.ResolveInfo; 33 import android.content.pm.ServiceInfo; 34 import android.content.res.XmlResourceParser; 35 import android.media.MediaMetrics; 36 import android.media.midi.IBluetoothMidiService; 37 import android.media.midi.IMidiDeviceListener; 38 import android.media.midi.IMidiDeviceOpenCallback; 39 import android.media.midi.IMidiDeviceServer; 40 import android.media.midi.IMidiManager; 41 import android.media.midi.MidiDevice; 42 import android.media.midi.MidiDeviceInfo; 43 import android.media.midi.MidiDeviceService; 44 import android.media.midi.MidiDeviceStatus; 45 import android.media.midi.MidiManager; 46 import android.os.Binder; 47 import android.os.Bundle; 48 import android.os.IBinder; 49 import android.os.ParcelUuid; 50 import android.os.Process; 51 import android.os.RemoteException; 52 import android.os.UserHandle; 53 import android.os.UserManager; 54 import android.util.EventLog; 55 import android.util.Log; 56 57 import com.android.internal.annotations.GuardedBy; 58 import com.android.internal.content.PackageMonitor; 59 import com.android.internal.util.DumpUtils; 60 import com.android.internal.util.IndentingPrintWriter; 61 import com.android.server.SystemService; 62 import com.android.server.SystemService.TargetUser; 63 64 import org.xmlpull.v1.XmlPullParser; 65 66 import java.io.FileDescriptor; 67 import java.io.IOException; 68 import java.io.PrintWriter; 69 import java.time.Duration; 70 import java.time.Instant; 71 import java.util.ArrayList; 72 import java.util.HashMap; 73 import java.util.HashSet; 74 import java.util.Iterator; 75 import java.util.List; 76 import java.util.UUID; 77 import java.util.concurrent.atomic.AtomicInteger; 78 import java.util.concurrent.atomic.AtomicLong; 79 80 // NOTE about locking order: 81 // if there is a path that syncs on BOTH mDevicesByInfo AND mDeviceConnections, 82 // this order must be observed 83 // 1. synchronized (mDevicesByInfo) 84 // 2. synchronized (mDeviceConnections) 85 //TODO Introduce a single lock object to lock the whole state and avoid the requirement above. 86 87 // All users should be able to connect to USB and Bluetooth MIDI devices. 88 // All users can create can install an app that provides, a Virtual MIDI Device Service. 89 // Users can not open virtual MIDI devices created by other users. 90 // getDevices() surfaces devices that can be opened by that user. 91 // openDevice() rejects devices that are cannot be opened by that user. 92 public class MidiService extends IMidiManager.Stub { 93 94 public static class Lifecycle extends SystemService { 95 private MidiService mMidiService; 96 Lifecycle(Context context)97 public Lifecycle(Context context) { 98 super(context); 99 } 100 101 @Override onStart()102 public void onStart() { 103 mMidiService = new MidiService(getContext()); 104 publishBinderService(Context.MIDI_SERVICE, mMidiService); 105 } 106 107 @Override 108 @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, 109 anyOf = {Manifest.permission.QUERY_USERS, 110 Manifest.permission.CREATE_USERS, 111 Manifest.permission.MANAGE_USERS}) onUserStarting(@onNull TargetUser user)112 public void onUserStarting(@NonNull TargetUser user) { 113 mMidiService.onStartOrUnlockUser(user, false /* matchDirectBootUnaware */); 114 } 115 116 @Override 117 @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, 118 anyOf = {Manifest.permission.QUERY_USERS, 119 Manifest.permission.CREATE_USERS, 120 Manifest.permission.MANAGE_USERS}) onUserUnlocking(@onNull TargetUser user)121 public void onUserUnlocking(@NonNull TargetUser user) { 122 mMidiService.onStartOrUnlockUser(user, true /* matchDirectBootUnaware */); 123 } 124 } 125 126 private static final String TAG = "MidiService"; 127 128 // These limits are much higher than any normal app should need. 129 private static final int MAX_DEVICE_SERVERS_PER_UID = 16; 130 private static final int MAX_LISTENERS_PER_CLIENT = 16; 131 private static final int MAX_CONNECTIONS_PER_CLIENT = 64; 132 133 private final Context mContext; 134 135 // list of all our clients, keyed by Binder token 136 private final HashMap<IBinder, Client> mClients = new HashMap<IBinder, Client>(); 137 138 // list of all devices, keyed by MidiDeviceInfo 139 private final HashMap<MidiDeviceInfo, Device> mDevicesByInfo 140 = new HashMap<MidiDeviceInfo, Device>(); 141 142 // list of all Bluetooth devices, keyed by BluetoothDevice 143 private final HashMap<BluetoothDevice, Device> mBluetoothDevices 144 = new HashMap<BluetoothDevice, Device>(); 145 146 private final HashMap<BluetoothDevice, MidiDevice> mBleMidiDeviceMap = 147 new HashMap<BluetoothDevice, MidiDevice>(); 148 149 // list of all devices, keyed by IMidiDeviceServer 150 private final HashMap<IBinder, Device> mDevicesByServer = new HashMap<IBinder, Device>(); 151 152 // used for assigning IDs to MIDI devices 153 private int mNextDeviceId = 1; 154 155 private final PackageManager mPackageManager; 156 private final UserManager mUserManager; 157 158 private static final String MIDI_LEGACY_STRING = "MIDI 1.0"; 159 private static final String MIDI_UNIVERSAL_STRING = "MIDI 2.0"; 160 161 // Used to lock mUsbMidiLegacyDeviceOpenCount and mUsbMidiUniversalDeviceInUse. 162 private final Object mUsbMidiLock = new Object(); 163 164 // Number of times a USB MIDI 1.0 device has opened, based on the device name. 165 @GuardedBy("mUsbMidiLock") 166 private final HashMap<String, Integer> mUsbMidiLegacyDeviceOpenCount = 167 new HashMap<String, Integer>(); 168 169 // Whether a USB MIDI device has opened, based on the device name. 170 @GuardedBy("mUsbMidiLock") 171 private final HashSet<String> mUsbMidiUniversalDeviceInUse = new HashSet<String>(); 172 173 // UID of BluetoothMidiService 174 private int mBluetoothServiceUid; 175 176 private static final UUID MIDI_SERVICE = UUID.fromString( 177 "03B80E5A-EDE8-4B33-A751-6CE34EC4C700"); 178 179 private final HashSet<ParcelUuid> mNonMidiUUIDs = new HashSet<ParcelUuid>(); 180 181 // PackageMonitor for listening to package changes 182 // uid is the uid of the package so use getChangingUserId() to fetch the userId. 183 private final PackageMonitor mPackageMonitor = new PackageMonitor() { 184 @Override 185 @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) 186 public void onPackageAdded(String packageName, int uid) { 187 addPackageDeviceServers(packageName, getChangingUserId()); 188 } 189 190 @Override 191 @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) 192 public void onPackageModified(String packageName) { 193 removePackageDeviceServers(packageName, getChangingUserId()); 194 addPackageDeviceServers(packageName, getChangingUserId()); 195 } 196 197 @Override 198 public void onPackageRemoved(String packageName, int uid) { 199 removePackageDeviceServers(packageName, getChangingUserId()); 200 } 201 }; 202 203 private final class Client implements IBinder.DeathRecipient { 204 private static final String TAG = "MidiService.Client"; 205 // Binder token for this client 206 private final IBinder mToken; 207 // This client's UID 208 private final int mUid; 209 // This client's PID 210 private final int mPid; 211 // List of all receivers for this client 212 private final HashMap<IBinder, IMidiDeviceListener> mListeners 213 = new HashMap<IBinder, IMidiDeviceListener>(); 214 // List of all device connections for this client 215 private final HashMap<IBinder, DeviceConnection> mDeviceConnections 216 = new HashMap<IBinder, DeviceConnection>(); 217 Client(IBinder token)218 public Client(IBinder token) { 219 mToken = token; 220 mUid = Binder.getCallingUid(); 221 mPid = Binder.getCallingPid(); 222 } 223 getUid()224 public int getUid() { 225 return mUid; 226 } 227 getUserId()228 private int getUserId() { 229 return UserHandle.getUserId(mUid); 230 } 231 addListener(IMidiDeviceListener listener)232 public void addListener(IMidiDeviceListener listener) { 233 if (mListeners.size() >= MAX_LISTENERS_PER_CLIENT) { 234 throw new SecurityException( 235 "too many MIDI listeners for UID = " + mUid); 236 } 237 // Use asBinder() so that we can match it in removeListener(). 238 // The listener proxy objects themselves do not match. 239 mListeners.put(listener.asBinder(), listener); 240 } 241 removeListener(IMidiDeviceListener listener)242 public void removeListener(IMidiDeviceListener listener) { 243 mListeners.remove(listener.asBinder()); 244 if (mListeners.size() == 0 && mDeviceConnections.size() == 0) { 245 close(); 246 } 247 } 248 249 @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, 250 Manifest.permission.INTERACT_ACROSS_USERS, 251 Manifest.permission.INTERACT_ACROSS_PROFILES}) addDeviceConnection(Device device, IMidiDeviceOpenCallback callback, int userId)252 public void addDeviceConnection(Device device, IMidiDeviceOpenCallback callback, 253 int userId) { 254 Log.d(TAG, "addDeviceConnection() device:" + device + " userId:" + userId); 255 if (mDeviceConnections.size() >= MAX_CONNECTIONS_PER_CLIENT) { 256 Log.i(TAG, "too many MIDI connections for UID = " + mUid); 257 throw new SecurityException( 258 "too many MIDI connections for UID = " + mUid); 259 } 260 DeviceConnection connection = new DeviceConnection(device, this, callback); 261 mDeviceConnections.put(connection.getToken(), connection); 262 device.addDeviceConnection(connection, userId); 263 } 264 265 // called from MidiService.closeDevice() removeDeviceConnection(IBinder token)266 public void removeDeviceConnection(IBinder token) { 267 DeviceConnection connection = mDeviceConnections.remove(token); 268 if (connection != null) { 269 connection.getDevice().removeDeviceConnection(connection); 270 } 271 if (mListeners.size() == 0 && mDeviceConnections.size() == 0) { 272 close(); 273 } 274 } 275 276 // called from Device.closeLocked() removeDeviceConnection(DeviceConnection connection)277 public void removeDeviceConnection(DeviceConnection connection) { 278 mDeviceConnections.remove(connection.getToken()); 279 if (mListeners.size() == 0 && mDeviceConnections.size() == 0) { 280 close(); 281 } 282 } 283 deviceAdded(Device device)284 public void deviceAdded(Device device) { 285 // ignore devices that our client cannot access 286 if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; 287 288 MidiDeviceInfo deviceInfo = device.getDeviceInfo(); 289 try { 290 for (IMidiDeviceListener listener : mListeners.values()) { 291 listener.onDeviceAdded(deviceInfo); 292 } 293 } catch (RemoteException e) { 294 Log.e(TAG, "remote exception", e); 295 } 296 } 297 deviceRemoved(Device device)298 public void deviceRemoved(Device device) { 299 // ignore devices that our client cannot access 300 if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; 301 302 MidiDeviceInfo deviceInfo = device.getDeviceInfo(); 303 try { 304 for (IMidiDeviceListener listener : mListeners.values()) { 305 listener.onDeviceRemoved(deviceInfo); 306 } 307 } catch (RemoteException e) { 308 Log.e(TAG, "remote exception", e); 309 } 310 } 311 deviceStatusChanged(Device device, MidiDeviceStatus status)312 public void deviceStatusChanged(Device device, MidiDeviceStatus status) { 313 // ignore devices that our client cannot access 314 if (!device.isUidAllowed(mUid) || !device.isUserIdAllowed(getUserId())) return; 315 316 try { 317 for (IMidiDeviceListener listener : mListeners.values()) { 318 listener.onDeviceStatusChanged(status); 319 } 320 } catch (RemoteException e) { 321 Log.e(TAG, "remote exception", e); 322 } 323 } 324 close()325 private void close() { 326 synchronized (mClients) { 327 mClients.remove(mToken); 328 mToken.unlinkToDeath(this, 0); 329 } 330 331 for (DeviceConnection connection : mDeviceConnections.values()) { 332 connection.getDevice().removeDeviceConnection(connection); 333 } 334 } 335 336 @Override binderDied()337 public void binderDied() { 338 Log.d(TAG, "Client died: " + this); 339 close(); 340 } 341 342 @Override toString()343 public String toString() { 344 StringBuilder sb = new StringBuilder("Client: UID: "); 345 sb.append(mUid); 346 sb.append(" PID: "); 347 sb.append(mPid); 348 sb.append(" listener count: "); 349 sb.append(mListeners.size()); 350 sb.append(" Device Connections:"); 351 for (DeviceConnection connection : mDeviceConnections.values()) { 352 sb.append(" <device "); 353 sb.append(connection.getDevice().getDeviceInfo().getId()); 354 sb.append(">"); 355 } 356 return sb.toString(); 357 } 358 } 359 getClient(IBinder token)360 private Client getClient(IBinder token) { 361 synchronized (mClients) { 362 Client client = mClients.get(token); 363 if (client == null) { 364 client = new Client(token); 365 366 try { 367 token.linkToDeath(client, 0); 368 } catch (RemoteException e) { 369 return null; 370 } 371 mClients.put(token, client); 372 } 373 return client; 374 } 375 } 376 377 private final class Device implements IBinder.DeathRecipient { 378 private static final String TAG = "MidiService.Device"; 379 private IMidiDeviceServer mServer; 380 private MidiDeviceInfo mDeviceInfo; 381 private final BluetoothDevice mBluetoothDevice; 382 private MidiDeviceStatus mDeviceStatus; 383 384 // ServiceInfo for the device's MidiDeviceServer implementation (virtual devices only) 385 private final ServiceInfo mServiceInfo; 386 // UID of device implementation 387 private final int mUid; 388 // User Id of the app. Only used for virtual devices 389 private final int mUserId; 390 391 // ServiceConnection for implementing Service (virtual devices only) 392 // mServiceConnection is non-null when connected or attempting to connect to the service 393 private ServiceConnection mServiceConnection; 394 395 // List of all device connections for this device 396 private final ArrayList<DeviceConnection> mDeviceConnections 397 = new ArrayList<DeviceConnection>(); 398 399 // Keep track of number of added and removed collections for logging 400 private AtomicInteger mDeviceConnectionsAdded = new AtomicInteger(); 401 private AtomicInteger mDeviceConnectionsRemoved = new AtomicInteger(); 402 403 // Keep track of total time with at least one active connection 404 private AtomicLong mTotalTimeConnectedNs = new AtomicLong(); 405 private Instant mPreviousCounterInstant = null; 406 407 private AtomicInteger mTotalInputBytes = new AtomicInteger(); 408 private AtomicInteger mTotalOutputBytes = new AtomicInteger(); 409 Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo, ServiceInfo serviceInfo, int uid, int userId)410 public Device(IMidiDeviceServer server, MidiDeviceInfo deviceInfo, 411 ServiceInfo serviceInfo, int uid, int userId) { 412 mDeviceInfo = deviceInfo; 413 mServiceInfo = serviceInfo; 414 mUid = uid; 415 mUserId = userId; 416 mBluetoothDevice = (BluetoothDevice)deviceInfo.getProperties().getParcelable( 417 MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class);; 418 setDeviceServer(server); 419 } 420 Device(BluetoothDevice bluetoothDevice)421 public Device(BluetoothDevice bluetoothDevice) { 422 mBluetoothDevice = bluetoothDevice; 423 mServiceInfo = null; 424 mUid = mBluetoothServiceUid; 425 mUserId = UserHandle.getUserId(mUid); 426 } 427 setDeviceServer(IMidiDeviceServer server)428 private void setDeviceServer(IMidiDeviceServer server) { 429 Log.i(TAG, "setDeviceServer()"); 430 if (server != null) { 431 if (mServer != null) { 432 Log.e(TAG, "mServer already set in setDeviceServer"); 433 return; 434 } 435 IBinder binder = server.asBinder(); 436 try { 437 binder.linkToDeath(this, 0); 438 mServer = server; 439 } catch (RemoteException e) { 440 mServer = null; 441 return; 442 } 443 mDevicesByServer.put(binder, this); 444 } else if (mServer != null) { 445 server = mServer; 446 mServer = null; 447 448 IBinder binder = server.asBinder(); 449 mDevicesByServer.remove(binder); 450 // Clearing mDeviceStatus is needed because setDeviceStatus() 451 // relies on finding the device in mDevicesByServer. 452 // So the status can no longer be updated after we remove it. 453 // Then we can end up with input ports that are stuck open. 454 mDeviceStatus = null; 455 456 try { 457 server.closeDevice(); 458 binder.unlinkToDeath(this, 0); 459 } catch (RemoteException e) { 460 // nothing to do here 461 } 462 } 463 464 if (mDeviceConnections != null) { 465 synchronized (mDeviceConnections) { 466 for (DeviceConnection connection : mDeviceConnections) { 467 connection.notifyClient(server); 468 } 469 } 470 } 471 } 472 getDeviceInfo()473 public MidiDeviceInfo getDeviceInfo() { 474 return mDeviceInfo; 475 } 476 477 // only used for bluetooth devices, which are created before we have a MidiDeviceInfo setDeviceInfo(MidiDeviceInfo deviceInfo)478 public void setDeviceInfo(MidiDeviceInfo deviceInfo) { 479 mDeviceInfo = deviceInfo; 480 } 481 getDeviceStatus()482 public MidiDeviceStatus getDeviceStatus() { 483 return mDeviceStatus; 484 } 485 setDeviceStatus(MidiDeviceStatus status)486 public void setDeviceStatus(MidiDeviceStatus status) { 487 mDeviceStatus = status; 488 } 489 getDeviceServer()490 public IMidiDeviceServer getDeviceServer() { 491 return mServer; 492 } 493 getServiceInfo()494 public ServiceInfo getServiceInfo() { 495 return mServiceInfo; 496 } 497 getPackageName()498 public String getPackageName() { 499 return (mServiceInfo == null ? null : mServiceInfo.packageName); 500 } 501 getUid()502 public int getUid() { 503 return mUid; 504 } 505 getUserId()506 public int getUserId() { 507 return mUserId; 508 } 509 isUidAllowed(int uid)510 public boolean isUidAllowed(int uid) { 511 return (!mDeviceInfo.isPrivate() || mUid == uid); 512 } 513 isUserIdAllowed(int userId)514 public boolean isUserIdAllowed(int userId) { 515 return (mDeviceInfo.getType() != MidiDeviceInfo.TYPE_VIRTUAL || mUserId == userId); 516 } 517 518 @RequiresPermission(anyOf = {Manifest.permission.INTERACT_ACROSS_USERS_FULL, 519 Manifest.permission.INTERACT_ACROSS_USERS, 520 Manifest.permission.INTERACT_ACROSS_PROFILES}) addDeviceConnection(DeviceConnection connection, int userId)521 public void addDeviceConnection(DeviceConnection connection, int userId) { 522 Log.d(TAG, "addDeviceConnection() [A] connection:" + connection); 523 synchronized (mDeviceConnections) { 524 mDeviceConnectionsAdded.incrementAndGet(); 525 if (mPreviousCounterInstant == null) { 526 mPreviousCounterInstant = Instant.now(); 527 } 528 529 Log.d(TAG, " mServer:" + mServer); 530 if (mServer != null) { 531 Log.i(TAG, "++++ A"); 532 mDeviceConnections.add(connection); 533 connection.notifyClient(mServer); 534 } else if (mServiceConnection == null && 535 (mServiceInfo != null || mBluetoothDevice != null)) { 536 Log.i(TAG, "++++ B"); 537 mDeviceConnections.add(connection); 538 539 mServiceConnection = new ServiceConnection() { 540 @Override 541 public void onServiceConnected(ComponentName name, IBinder service) { 542 Log.i(TAG, "++++ onServiceConnected() mBluetoothDevice:" 543 + mBluetoothDevice); 544 IMidiDeviceServer server = null; 545 if (mBluetoothDevice != null) { 546 IBluetoothMidiService mBluetoothMidiService = 547 IBluetoothMidiService.Stub.asInterface(service); 548 Log.i(TAG, "++++ mBluetoothMidiService:" + mBluetoothMidiService); 549 if (mBluetoothMidiService != null) { 550 try { 551 // We need to explicitly add the device in a separate method 552 // because onBind() is only called once. 553 IBinder deviceBinder = 554 mBluetoothMidiService.addBluetoothDevice( 555 mBluetoothDevice); 556 server = IMidiDeviceServer.Stub.asInterface(deviceBinder); 557 } catch (RemoteException e) { 558 Log.e(TAG, "Could not call addBluetoothDevice()", e); 559 } 560 } 561 } else { 562 server = IMidiDeviceServer.Stub.asInterface(service); 563 } 564 setDeviceServer(server); 565 } 566 567 @Override 568 public void onServiceDisconnected(ComponentName name) { 569 setDeviceServer(null); 570 mServiceConnection = null; 571 } 572 }; 573 574 Intent intent; 575 if (mBluetoothDevice != null) { 576 intent = new Intent(MidiManager.BLUETOOTH_MIDI_SERVICE_INTENT); 577 intent.setComponent(new ComponentName( 578 MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 579 MidiManager.BLUETOOTH_MIDI_SERVICE_CLASS)); 580 } else { 581 intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); 582 intent.setComponent( 583 new ComponentName(mServiceInfo.packageName, mServiceInfo.name)); 584 } 585 586 if (!mContext.bindServiceAsUser(intent, mServiceConnection, 587 Context.BIND_AUTO_CREATE, UserHandle.of(mUserId))) { 588 Log.e(TAG, "Unable to bind service: " + intent); 589 setDeviceServer(null); 590 mServiceConnection = null; 591 } 592 } else { 593 Log.e(TAG, "No way to connect to device in addDeviceConnection"); 594 connection.notifyClient(null); 595 } 596 } 597 } 598 removeDeviceConnection(DeviceConnection connection)599 public void removeDeviceConnection(DeviceConnection connection) { 600 synchronized (mDevicesByInfo) { 601 synchronized (mDeviceConnections) { 602 int numRemovedConnections = mDeviceConnectionsRemoved.incrementAndGet(); 603 if (mPreviousCounterInstant != null) { 604 mTotalTimeConnectedNs.addAndGet(Duration.between( 605 mPreviousCounterInstant, Instant.now()).toNanos()); 606 } 607 // Stop the clock if all devices have been removed. 608 // Otherwise, start the clock from the current instant. 609 if (numRemovedConnections >= mDeviceConnectionsAdded.get()) { 610 mPreviousCounterInstant = null; 611 } else { 612 mPreviousCounterInstant = Instant.now(); 613 } 614 logMetrics(false /* isDeviceDisconnected */); 615 616 mDeviceConnections.remove(connection); 617 618 if (connection.getDevice().getDeviceInfo().getType() 619 == MidiDeviceInfo.TYPE_USB) { 620 synchronized (mUsbMidiLock) { 621 removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo()); 622 } 623 } 624 625 if (mDeviceConnections.size() == 0 && mServiceConnection != null) { 626 mContext.unbindService(mServiceConnection); 627 mServiceConnection = null; 628 if (mBluetoothDevice != null) { 629 // Bluetooth devices are ephemeral - remove when no clients exist 630 closeLocked(); 631 } else { 632 setDeviceServer(null); 633 } 634 } 635 } 636 } 637 } 638 639 // synchronize on mDevicesByInfo closeLocked()640 public void closeLocked() { 641 synchronized (mDeviceConnections) { 642 for (DeviceConnection connection : mDeviceConnections) { 643 if (connection.getDevice().getDeviceInfo().getType() 644 == MidiDeviceInfo.TYPE_USB) { 645 synchronized (mUsbMidiLock) { 646 removeUsbMidiDeviceLocked(connection.getDevice().getDeviceInfo()); 647 } 648 } 649 connection.getClient().removeDeviceConnection(connection); 650 } 651 mDeviceConnections.clear(); 652 653 // If the timer is still going, some clients have not closed the connection yet. 654 if (mPreviousCounterInstant != null) { 655 Instant currentInstant = Instant.now(); 656 mTotalTimeConnectedNs.addAndGet(Duration.between( 657 mPreviousCounterInstant, currentInstant).toNanos()); 658 mPreviousCounterInstant = currentInstant; 659 } 660 661 logMetrics(true /* isDeviceDisconnected */); 662 } 663 setDeviceServer(null); 664 665 // closed virtual devices should not be removed from mDevicesByInfo 666 // since they can be restarted on demand 667 if (mServiceInfo == null) { 668 removeDeviceLocked(this); 669 } else { 670 mDeviceStatus = new MidiDeviceStatus(mDeviceInfo); 671 } 672 673 if (mBluetoothDevice != null) { 674 mBluetoothDevices.remove(mBluetoothDevice); 675 } 676 } 677 logMetrics(boolean isDeviceDisconnected)678 private void logMetrics(boolean isDeviceDisconnected) { 679 // Only log metrics if the device was used in a connection 680 int numDeviceConnectionAdded = mDeviceConnectionsAdded.get(); 681 if (mDeviceInfo != null && numDeviceConnectionAdded > 0) { 682 new MediaMetrics.Item(MediaMetrics.Name.AUDIO_MIDI) 683 .setUid(mUid) 684 .set(MediaMetrics.Property.DEVICE_ID, mDeviceInfo.getId()) 685 .set(MediaMetrics.Property.INPUT_PORT_COUNT, mDeviceInfo.getInputPortCount()) 686 .set(MediaMetrics.Property.OUTPUT_PORT_COUNT, 687 mDeviceInfo.getOutputPortCount()) 688 .set(MediaMetrics.Property.HARDWARE_TYPE, mDeviceInfo.getType()) 689 .set(MediaMetrics.Property.DURATION_NS, mTotalTimeConnectedNs.get()) 690 .set(MediaMetrics.Property.OPENED_COUNT, numDeviceConnectionAdded) 691 .set(MediaMetrics.Property.CLOSED_COUNT, mDeviceConnectionsRemoved.get()) 692 .set(MediaMetrics.Property.DEVICE_DISCONNECTED, 693 isDeviceDisconnected ? "true" : "false") 694 .set(MediaMetrics.Property.IS_SHARED, 695 !mDeviceInfo.isPrivate() ? "true" : "false") 696 .set(MediaMetrics.Property.SUPPORTS_MIDI_UMP, mDeviceInfo.getDefaultProtocol() 697 != MidiDeviceInfo.PROTOCOL_UNKNOWN ? "true" : "false") 698 .set(MediaMetrics.Property.USING_ALSA, mDeviceInfo.getProperties().get( 699 MidiDeviceInfo.PROPERTY_ALSA_CARD) != null ? "true" : "false") 700 .set(MediaMetrics.Property.EVENT, "deviceClosed") 701 .set(MediaMetrics.Property.TOTAL_INPUT_BYTES, mTotalInputBytes.get()) 702 .set(MediaMetrics.Property.TOTAL_OUTPUT_BYTES, mTotalOutputBytes.get()) 703 .record(); 704 } 705 } 706 707 @Override binderDied()708 public void binderDied() { 709 Log.d(TAG, "Device died: " + this); 710 synchronized (mDevicesByInfo) { 711 closeLocked(); 712 } 713 } 714 updateTotalBytes(int totalInputBytes, int totalOutputBytes)715 public void updateTotalBytes(int totalInputBytes, int totalOutputBytes) { 716 mTotalInputBytes.set(totalInputBytes); 717 mTotalOutputBytes.set(totalOutputBytes); 718 } 719 720 @Override toString()721 public String toString() { 722 StringBuilder sb = new StringBuilder("Device Info: "); 723 sb.append(mDeviceInfo); 724 sb.append(" Status: "); 725 sb.append(mDeviceStatus); 726 sb.append(" UID: "); 727 sb.append(mUid); 728 sb.append(" DeviceConnection count: "); 729 sb.append(mDeviceConnections.size()); 730 sb.append(" mServiceConnection: "); 731 sb.append(mServiceConnection); 732 return sb.toString(); 733 } 734 } 735 736 // Represents a connection between a client and a device 737 private final class DeviceConnection { 738 private static final String TAG = "MidiService.DeviceConnection"; 739 private final IBinder mToken = new Binder(); 740 private final Device mDevice; 741 private final Client mClient; 742 private IMidiDeviceOpenCallback mCallback; 743 DeviceConnection(Device device, Client client, IMidiDeviceOpenCallback callback)744 public DeviceConnection(Device device, Client client, IMidiDeviceOpenCallback callback) { 745 mDevice = device; 746 mClient = client; 747 mCallback = callback; 748 } 749 getDevice()750 public Device getDevice() { 751 return mDevice; 752 } 753 getClient()754 public Client getClient() { 755 return mClient; 756 } 757 getToken()758 public IBinder getToken() { 759 return mToken; 760 } 761 notifyClient(IMidiDeviceServer deviceServer)762 public void notifyClient(IMidiDeviceServer deviceServer) { 763 Log.d(TAG, "notifyClient"); 764 765 if (mCallback != null) { 766 try { 767 mCallback.onDeviceOpened(deviceServer, (deviceServer == null ? null : mToken)); 768 } catch (RemoteException e) { 769 // Client binderDied() method will do necessary cleanup, so nothing to do here 770 } 771 mCallback = null; 772 } 773 } 774 775 @Override toString()776 public String toString() { 777 // return "DeviceConnection Device ID: " + mDevice.getDeviceInfo().getId(); 778 return mDevice != null && mDevice.getDeviceInfo() != null 779 ? ("" + mDevice.getDeviceInfo().getId()) : "null"; 780 } 781 } 782 783 // Note, this isn't useful at connect-time because the service UUIDs haven't 784 // been gathered yet. isBLEMIDIDevice(BluetoothDevice btDevice)785 private boolean isBLEMIDIDevice(BluetoothDevice btDevice) { 786 ParcelUuid[] uuids = btDevice.getUuids(); 787 if (uuids != null) { 788 for (ParcelUuid uuid : uuids) { 789 if (uuid.getUuid().equals(MIDI_SERVICE)) { 790 return true; 791 } 792 } 793 } 794 return false; 795 } 796 dumpIntentExtras(Intent intent)797 private static void dumpIntentExtras(Intent intent) { 798 String action = intent.getAction(); 799 Log.d(TAG, "Intent: " + action); 800 Bundle bundle = intent.getExtras(); 801 if (bundle != null) { 802 for (String key : bundle.keySet()) { 803 Log.d(TAG, " " + key + " : " 804 + (bundle.get(key) != null ? bundle.get(key) : "NULL")); 805 } 806 } 807 } 808 isBleTransport(Intent intent)809 private static boolean isBleTransport(Intent intent) { 810 Bundle bundle = intent.getExtras(); 811 boolean isBle = false; 812 if (bundle != null) { 813 isBle = bundle.getInt(BluetoothDevice.EXTRA_TRANSPORT, BluetoothDevice.TRANSPORT_AUTO) 814 == BluetoothDevice.TRANSPORT_LE; 815 } 816 return isBle; 817 } 818 dumpUuids(BluetoothDevice btDevice)819 private void dumpUuids(BluetoothDevice btDevice) { 820 ParcelUuid[] uuidParcels = btDevice.getUuids(); 821 Log.d(TAG, "dumpUuids(" + btDevice + ") numParcels:" 822 + (uuidParcels != null ? uuidParcels.length : 0)); 823 824 if (uuidParcels == null) { 825 Log.d(TAG, "No UUID Parcels"); 826 return; 827 } 828 829 for (ParcelUuid parcel : uuidParcels) { 830 UUID uuid = parcel.getUuid(); 831 Log.d(TAG, " uuid:" + uuid); 832 } 833 } 834 hasNonMidiUuids(BluetoothDevice btDevice)835 private boolean hasNonMidiUuids(BluetoothDevice btDevice) { 836 ParcelUuid[] uuidParcels = btDevice.getUuids(); 837 if (uuidParcels != null) { 838 // The assumption is that these services are indicative of devices that 839 // ARE NOT MIDI devices. 840 for (ParcelUuid parcel : uuidParcels) { 841 if (mNonMidiUUIDs.contains(parcel)) { 842 return true; 843 } 844 } 845 } 846 return false; 847 } 848 849 private final BroadcastReceiver mBleMidiReceiver = new BroadcastReceiver() { 850 @Override 851 public void onReceive(Context context, Intent intent) { 852 String action = intent.getAction(); 853 if (action == null) { 854 Log.w(TAG, "MidiService, action is null"); 855 return; 856 } 857 858 switch (action) { 859 case BluetoothDevice.ACTION_ACL_CONNECTED: 860 { 861 Log.d(TAG, "ACTION_ACL_CONNECTED"); 862 dumpIntentExtras(intent); 863 // BLE-MIDI controllers are by definition BLE, so if this device 864 // isn't, it CAN'T be a midi device 865 if (!isBleTransport(intent)) { 866 Log.i(TAG, "No BLE transport - NOT MIDI"); 867 break; 868 } 869 870 Log.d(TAG, "BLE Device"); 871 BluetoothDevice btDevice = 872 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, android.bluetooth.BluetoothDevice.class); 873 dumpUuids(btDevice); 874 875 // See if there are any service UUIDs and if so do any of them indicate a 876 // Non-MIDI device (headset, headphones, QWERTY keyboard....) 877 if (hasNonMidiUuids(btDevice)) { 878 Log.d(TAG, "Non-MIDI service UUIDs found. NOT MIDI"); 879 break; 880 } 881 882 Log.d(TAG, "Potential MIDI Device."); 883 openBluetoothDevice(btDevice); 884 } 885 break; 886 887 case BluetoothDevice.ACTION_ACL_DISCONNECTED: 888 { 889 Log.d(TAG, "ACTION_ACL_DISCONNECTED"); 890 BluetoothDevice btDevice = 891 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, android.bluetooth.BluetoothDevice.class); 892 // We DO know at this point if we are disconnecting a MIDI device, so 893 // don't bother if we are not. 894 if (isBLEMIDIDevice(btDevice)) { 895 closeBluetoothDevice(btDevice); 896 } 897 } 898 break; 899 900 case BluetoothDevice.ACTION_BOND_STATE_CHANGED: 901 // { 902 // Log.d(TAG, "ACTION_BOND_STATE_CHANGED"); 903 // int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1); 904 // Log.d(TAG, " bondState:" + bondState); 905 // BluetoothDevice btDevice = 906 // intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 907 // Log.d(TAG, " btDevice:" + btDevice); 908 // dumpUuids(btDevice); 909 // if (isBLEMIDIDevice(btDevice)) { 910 // Log.d(TAG, "BT MIDI DEVICE"); 911 // openBluetoothDevice(btDevice); 912 // } 913 // } 914 // break; 915 916 case BluetoothDevice.ACTION_UUID: 917 { 918 Log.d(TAG, "ACTION_UUID"); 919 BluetoothDevice btDevice = 920 intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE, android.bluetooth.BluetoothDevice.class); 921 dumpUuids(btDevice); 922 if (isBLEMIDIDevice(btDevice)) { 923 Log.d(TAG, "BT MIDI DEVICE"); 924 openBluetoothDevice(btDevice); 925 } 926 } 927 break; 928 } 929 } 930 }; 931 MidiService(Context context)932 public MidiService(Context context) { 933 mContext = context; 934 mPackageManager = context.getPackageManager(); 935 mUserManager = mContext.getSystemService(UserManager.class); 936 mPackageMonitor.register(mContext, null, UserHandle.ALL, true); 937 938 // TEMPORARY - Disable BTL-MIDI 939 //FIXME - b/25689266 940 // Setup broadcast receivers 941 // IntentFilter filter = new IntentFilter(); 942 // filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 943 // filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 944 // filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 945 // filter.addAction(BluetoothDevice.ACTION_UUID); 946 // context.registerReceiver(mBleMidiReceiver, filter); 947 948 mBluetoothServiceUid = -1; 949 950 mNonMidiUUIDs.add(BluetoothUuid.A2DP_SINK); // Headphones? 951 mNonMidiUUIDs.add(BluetoothUuid.A2DP_SOURCE); // Headset? 952 mNonMidiUUIDs.add(BluetoothUuid.ADV_AUDIO_DIST); 953 mNonMidiUUIDs.add(BluetoothUuid.AVRCP_CONTROLLER); 954 mNonMidiUUIDs.add(BluetoothUuid.HFP); 955 mNonMidiUUIDs.add(BluetoothUuid.HSP); 956 mNonMidiUUIDs.add(BluetoothUuid.HID); 957 mNonMidiUUIDs.add(BluetoothUuid.LE_AUDIO); 958 mNonMidiUUIDs.add(BluetoothUuid.HOGP); 959 mNonMidiUUIDs.add(BluetoothUuid.HEARING_AID); 960 // This one is coming up 961 // mNonMidiUUIDs.add(BluetoothUuid.BATTERY); 962 } 963 964 @RequiresPermission(allOf = {Manifest.permission.INTERACT_ACROSS_USERS}, 965 anyOf = {Manifest.permission.QUERY_USERS, 966 Manifest.permission.CREATE_USERS, 967 Manifest.permission.MANAGE_USERS}) onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware)968 private void onStartOrUnlockUser(TargetUser user, boolean matchDirectBootUnaware) { 969 Log.d(TAG, "onStartOrUnlockUser " + user.getUserIdentifier() + " matchDirectBootUnaware: " 970 + matchDirectBootUnaware); 971 Intent intent = new Intent(MidiDeviceService.SERVICE_INTERFACE); 972 int resolveFlags = PackageManager.GET_META_DATA; 973 if (matchDirectBootUnaware) { 974 resolveFlags |= PackageManager.MATCH_DIRECT_BOOT_UNAWARE; 975 } 976 List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServicesAsUser(intent, 977 resolveFlags, user.getUserIdentifier()); 978 if (resolveInfos != null) { 979 int count = resolveInfos.size(); 980 for (int i = 0; i < count; i++) { 981 ServiceInfo serviceInfo = resolveInfos.get(i).serviceInfo; 982 if (serviceInfo != null) { 983 addPackageDeviceServer(serviceInfo, user.getUserIdentifier()); 984 } 985 } 986 } 987 988 if (user.getUserIdentifier() == mUserManager.getMainUser().getIdentifier()) { 989 PackageInfo info; 990 try { 991 info = mPackageManager.getPackageInfoAsUser( 992 MidiManager.BLUETOOTH_MIDI_SERVICE_PACKAGE, 0, user.getUserIdentifier()); 993 } catch (PackageManager.NameNotFoundException e) { 994 info = null; 995 } 996 if (info != null && info.applicationInfo != null) { 997 mBluetoothServiceUid = info.applicationInfo.uid; 998 } 999 } 1000 } 1001 1002 @Override registerListener(IBinder token, IMidiDeviceListener listener)1003 public void registerListener(IBinder token, IMidiDeviceListener listener) { 1004 Client client = getClient(token); 1005 if (client == null) return; 1006 client.addListener(listener); 1007 // Let listener know whether any ports are already busy. 1008 updateStickyDeviceStatus(client.mUid, listener); 1009 } 1010 1011 @Override unregisterListener(IBinder token, IMidiDeviceListener listener)1012 public void unregisterListener(IBinder token, IMidiDeviceListener listener) { 1013 Client client = getClient(token); 1014 if (client == null) return; 1015 client.removeListener(listener); 1016 } 1017 1018 // Inform listener of the status of all known devices. updateStickyDeviceStatus(int uid, IMidiDeviceListener listener)1019 private void updateStickyDeviceStatus(int uid, IMidiDeviceListener listener) { 1020 int userId = UserHandle.getUserId(uid); 1021 synchronized (mDevicesByInfo) { 1022 for (Device device : mDevicesByInfo.values()) { 1023 // ignore devices that our client cannot access 1024 if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { 1025 try { 1026 MidiDeviceStatus status = device.getDeviceStatus(); 1027 if (status != null) { 1028 listener.onDeviceStatusChanged(status); 1029 } 1030 } catch (RemoteException e) { 1031 Log.e(TAG, "remote exception", e); 1032 } 1033 } 1034 } 1035 } 1036 } 1037 1038 private static final MidiDeviceInfo[] EMPTY_DEVICE_INFO_ARRAY = new MidiDeviceInfo[0]; 1039 getDevices()1040 public MidiDeviceInfo[] getDevices() { 1041 return getDevicesForTransport(MidiManager.TRANSPORT_MIDI_BYTE_STREAM); 1042 } 1043 1044 /** 1045 * @hide 1046 */ getDevicesForTransport(int transport)1047 public MidiDeviceInfo[] getDevicesForTransport(int transport) { 1048 ArrayList<MidiDeviceInfo> deviceInfos = new ArrayList<MidiDeviceInfo>(); 1049 int uid = Binder.getCallingUid(); 1050 int userId = getCallingUserId(); 1051 1052 synchronized (mDevicesByInfo) { 1053 for (Device device : mDevicesByInfo.values()) { 1054 if (device.isUidAllowed(uid) && device.isUserIdAllowed(userId)) { 1055 // UMP devices have protocols that are not PROTOCOL_UNKNOWN 1056 if (transport == MidiManager.TRANSPORT_UNIVERSAL_MIDI_PACKETS) { 1057 if (device.getDeviceInfo().getDefaultProtocol() 1058 != MidiDeviceInfo.PROTOCOL_UNKNOWN) { 1059 deviceInfos.add(device.getDeviceInfo()); 1060 } 1061 } else if (transport == MidiManager.TRANSPORT_MIDI_BYTE_STREAM) { 1062 if (device.getDeviceInfo().getDefaultProtocol() 1063 == MidiDeviceInfo.PROTOCOL_UNKNOWN) { 1064 deviceInfos.add(device.getDeviceInfo()); 1065 } 1066 } 1067 } 1068 } 1069 } 1070 1071 return deviceInfos.toArray(EMPTY_DEVICE_INFO_ARRAY); 1072 } 1073 1074 @Override openDevice(IBinder token, MidiDeviceInfo deviceInfo, IMidiDeviceOpenCallback callback)1075 public void openDevice(IBinder token, MidiDeviceInfo deviceInfo, 1076 IMidiDeviceOpenCallback callback) { 1077 Client client = getClient(token); 1078 Log.d(TAG, "openDevice() client:" + client); 1079 if (client == null) return; 1080 1081 Device device; 1082 synchronized (mDevicesByInfo) { 1083 device = mDevicesByInfo.get(deviceInfo); 1084 Log.d(TAG, " device:" + device); 1085 if (device == null) { 1086 throw new IllegalArgumentException("device does not exist: " + deviceInfo); 1087 } 1088 if (!device.isUidAllowed(Binder.getCallingUid())) { 1089 throw new SecurityException("Attempt to open private device with wrong UID"); 1090 } 1091 if (!device.isUserIdAllowed(getCallingUserId())) { 1092 throw new SecurityException("Attempt to open virtual device with wrong user id"); 1093 } 1094 } 1095 1096 if (deviceInfo.getType() == MidiDeviceInfo.TYPE_USB) { 1097 synchronized (mUsbMidiLock) { 1098 if (isUsbMidiDeviceInUseLocked(deviceInfo)) { 1099 throw new IllegalArgumentException("device already in use: " + deviceInfo); 1100 } 1101 addUsbMidiDeviceLocked(deviceInfo); 1102 } 1103 } 1104 1105 // clear calling identity so bindService does not fail 1106 final long identity = Binder.clearCallingIdentity(); 1107 try { 1108 Log.i(TAG, "addDeviceConnection() [B] device:" + device); 1109 client.addDeviceConnection(device, callback, getCallingUserId()); 1110 } finally { 1111 Binder.restoreCallingIdentity(identity); 1112 } 1113 } 1114 openBluetoothDevice(BluetoothDevice bluetoothDevice)1115 private void openBluetoothDevice(BluetoothDevice bluetoothDevice) { 1116 Log.d(TAG, "openBluetoothDevice() device: " + bluetoothDevice); 1117 1118 MidiManager midiManager = mContext.getSystemService(MidiManager.class); 1119 midiManager.openBluetoothDevice(bluetoothDevice, 1120 new MidiManager.OnDeviceOpenedListener() { 1121 @Override 1122 public void onDeviceOpened(MidiDevice device) { 1123 synchronized (mBleMidiDeviceMap) { 1124 Log.i(TAG, "onDeviceOpened() device:" + device); 1125 mBleMidiDeviceMap.put(bluetoothDevice, device); 1126 } 1127 } 1128 }, null); 1129 } 1130 closeBluetoothDevice(BluetoothDevice bluetoothDevice)1131 private void closeBluetoothDevice(BluetoothDevice bluetoothDevice) { 1132 Log.d(TAG, "closeBluetoothDevice() device: " + bluetoothDevice); 1133 1134 MidiDevice midiDevice; 1135 synchronized (mBleMidiDeviceMap) { 1136 midiDevice = mBleMidiDeviceMap.remove(bluetoothDevice); 1137 } 1138 1139 if (midiDevice != null) { 1140 try { 1141 midiDevice.close(); 1142 } catch (IOException ex) { 1143 Log.e(TAG, "Exception closing BLE-MIDI device" + ex); 1144 } 1145 } 1146 } 1147 1148 @Override openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice, IMidiDeviceOpenCallback callback)1149 public void openBluetoothDevice(IBinder token, BluetoothDevice bluetoothDevice, 1150 IMidiDeviceOpenCallback callback) { 1151 Log.d(TAG, "openBluetoothDevice()"); 1152 1153 Client client = getClient(token); 1154 if (client == null) return; 1155 1156 // Bluetooth devices are created on demand 1157 Device device; 1158 Log.i(TAG, "alloc device..."); 1159 synchronized (mDevicesByInfo) { 1160 device = mBluetoothDevices.get(bluetoothDevice); 1161 if (device == null) { 1162 device = new Device(bluetoothDevice); 1163 mBluetoothDevices.put(bluetoothDevice, device); 1164 } 1165 } 1166 Log.i(TAG, "device: " + device); 1167 // clear calling identity so bindService does not fail 1168 final long identity = Binder.clearCallingIdentity(); 1169 try { 1170 Log.i(TAG, "addDeviceConnection() [C] device:" + device); 1171 client.addDeviceConnection(device, callback, getCallingUserId()); 1172 } finally { 1173 Binder.restoreCallingIdentity(identity); 1174 } 1175 } 1176 1177 @Override closeDevice(IBinder clientToken, IBinder deviceToken)1178 public void closeDevice(IBinder clientToken, IBinder deviceToken) { 1179 Client client = getClient(clientToken); 1180 if (client == null) return; 1181 client.removeDeviceConnection(deviceToken); 1182 } 1183 1184 @Override registerDeviceServer(IMidiDeviceServer server, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, int type, int defaultProtocol)1185 public MidiDeviceInfo registerDeviceServer(IMidiDeviceServer server, int numInputPorts, 1186 int numOutputPorts, String[] inputPortNames, String[] outputPortNames, 1187 Bundle properties, int type, int defaultProtocol) { 1188 int uid = Binder.getCallingUid(); 1189 int userId = getCallingUserId(); 1190 if (type == MidiDeviceInfo.TYPE_USB && uid != Process.SYSTEM_UID) { 1191 throw new SecurityException("only system can create USB devices"); 1192 } else if (type == MidiDeviceInfo.TYPE_BLUETOOTH && uid != mBluetoothServiceUid) { 1193 throw new SecurityException("only MidiBluetoothService can create Bluetooth devices"); 1194 } 1195 1196 synchronized (mDevicesByInfo) { 1197 return addDeviceLocked(type, numInputPorts, numOutputPorts, inputPortNames, 1198 outputPortNames, properties, server, null, false, uid, 1199 defaultProtocol, userId); 1200 } 1201 } 1202 1203 @Override unregisterDeviceServer(IMidiDeviceServer server)1204 public void unregisterDeviceServer(IMidiDeviceServer server) { 1205 synchronized (mDevicesByInfo) { 1206 Device device = mDevicesByServer.get(server.asBinder()); 1207 if (device != null) { 1208 device.closeLocked(); 1209 } 1210 } 1211 } 1212 1213 @Override getServiceDeviceInfo(String packageName, String className)1214 public MidiDeviceInfo getServiceDeviceInfo(String packageName, String className) { 1215 int uid = Binder.getCallingUid(); 1216 synchronized (mDevicesByInfo) { 1217 for (Device device : mDevicesByInfo.values()) { 1218 ServiceInfo serviceInfo = device.getServiceInfo(); 1219 if (serviceInfo != null && 1220 packageName.equals(serviceInfo.packageName) && 1221 className.equals(serviceInfo.name)) { 1222 if (device.isUidAllowed(uid)) { 1223 return device.getDeviceInfo(); 1224 } else { 1225 EventLog.writeEvent(0x534e4554, "185796676", -1, ""); 1226 return null; 1227 } 1228 } 1229 } 1230 return null; 1231 } 1232 } 1233 1234 @Override getDeviceStatus(MidiDeviceInfo deviceInfo)1235 public MidiDeviceStatus getDeviceStatus(MidiDeviceInfo deviceInfo) { 1236 Device device = mDevicesByInfo.get(deviceInfo); 1237 if (device == null) { 1238 throw new IllegalArgumentException("no such device for " + deviceInfo); 1239 } 1240 int uid = Binder.getCallingUid(); 1241 if (device.isUidAllowed(uid)) { 1242 return device.getDeviceStatus(); 1243 } else { 1244 Log.e(TAG, "getDeviceStatus() invalid UID = " + uid); 1245 EventLog.writeEvent(0x534e4554, "203549963", 1246 uid, "getDeviceStatus: invalid uid"); 1247 return null; 1248 } 1249 } 1250 1251 @Override setDeviceStatus(IMidiDeviceServer server, MidiDeviceStatus status)1252 public void setDeviceStatus(IMidiDeviceServer server, MidiDeviceStatus status) { 1253 Device device = mDevicesByServer.get(server.asBinder()); 1254 if (device != null) { 1255 if (Binder.getCallingUid() != device.getUid()) { 1256 throw new SecurityException("setDeviceStatus() caller UID " + Binder.getCallingUid() 1257 + " does not match device's UID " + device.getUid()); 1258 } 1259 device.setDeviceStatus(status); 1260 notifyDeviceStatusChanged(device, status); 1261 } 1262 } 1263 notifyDeviceStatusChanged(Device device, MidiDeviceStatus status)1264 private void notifyDeviceStatusChanged(Device device, MidiDeviceStatus status) { 1265 synchronized (mClients) { 1266 for (Client c : mClients.values()) { 1267 c.deviceStatusChanged(device, status); 1268 } 1269 } 1270 } 1271 1272 // synchronize on mDevicesByInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts, String[] inputPortNames, String[] outputPortNames, Bundle properties, IMidiDeviceServer server, ServiceInfo serviceInfo, boolean isPrivate, int uid, int defaultProtocol, int userId)1273 private MidiDeviceInfo addDeviceLocked(int type, int numInputPorts, int numOutputPorts, 1274 String[] inputPortNames, String[] outputPortNames, Bundle properties, 1275 IMidiDeviceServer server, ServiceInfo serviceInfo, 1276 boolean isPrivate, int uid, int defaultProtocol, int userId) { 1277 Log.d(TAG, "addDeviceLocked()" + uid + " type:" + type); 1278 1279 // Limit the number of devices per app. 1280 int deviceCountForApp = 0; 1281 for (Device device : mDevicesByInfo.values()) { 1282 if (device.getUid() == uid) { 1283 deviceCountForApp++; 1284 } 1285 } 1286 if (deviceCountForApp >= MAX_DEVICE_SERVERS_PER_UID) { 1287 throw new SecurityException( 1288 "too many MIDI devices already created for UID = " 1289 + uid); 1290 } 1291 1292 int id = mNextDeviceId++; 1293 MidiDeviceInfo deviceInfo = new MidiDeviceInfo(type, id, numInputPorts, numOutputPorts, 1294 inputPortNames, outputPortNames, properties, isPrivate, 1295 defaultProtocol); 1296 1297 if (server != null) { 1298 try { 1299 server.setDeviceInfo(deviceInfo); 1300 } catch (RemoteException e) { 1301 Log.e(TAG, "RemoteException in setDeviceInfo()"); 1302 return null; 1303 } 1304 } 1305 1306 Device device = null; 1307 BluetoothDevice bluetoothDevice = null; 1308 if (type == MidiDeviceInfo.TYPE_BLUETOOTH) { 1309 bluetoothDevice = (BluetoothDevice)properties.getParcelable( 1310 MidiDeviceInfo.PROPERTY_BLUETOOTH_DEVICE, android.bluetooth.BluetoothDevice.class); 1311 device = mBluetoothDevices.get(bluetoothDevice); 1312 if (device != null) { 1313 device.setDeviceInfo(deviceInfo); 1314 } 1315 } 1316 if (device == null) { 1317 device = new Device(server, deviceInfo, serviceInfo, uid, userId); 1318 } 1319 mDevicesByInfo.put(deviceInfo, device); 1320 if (bluetoothDevice != null) { 1321 mBluetoothDevices.put(bluetoothDevice, device); 1322 } 1323 1324 synchronized (mClients) { 1325 for (Client c : mClients.values()) { 1326 c.deviceAdded(device); 1327 } 1328 } 1329 1330 return deviceInfo; 1331 } 1332 1333 // synchronize on mDevicesByInfo removeDeviceLocked(Device device)1334 private void removeDeviceLocked(Device device) { 1335 IMidiDeviceServer server = device.getDeviceServer(); 1336 if (server != null) { 1337 mDevicesByServer.remove(server.asBinder()); 1338 } 1339 mDevicesByInfo.remove(device.getDeviceInfo()); 1340 1341 synchronized (mClients) { 1342 for (Client c : mClients.values()) { 1343 c.deviceRemoved(device); 1344 } 1345 } 1346 } 1347 1348 @RequiresPermission(Manifest.permission.INTERACT_ACROSS_USERS) addPackageDeviceServers(String packageName, int userId)1349 private void addPackageDeviceServers(String packageName, int userId) { 1350 PackageInfo info; 1351 1352 try { 1353 info = mPackageManager.getPackageInfoAsUser(packageName, 1354 PackageManager.GET_SERVICES | PackageManager.GET_META_DATA 1355 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, userId); 1356 } catch (PackageManager.NameNotFoundException e) { 1357 Log.e(TAG, "handlePackageUpdate could not find package " + packageName, e); 1358 return; 1359 } 1360 1361 ServiceInfo[] services = info.services; 1362 if (services == null) return; 1363 for (int i = 0; i < services.length; i++) { 1364 addPackageDeviceServer(services[i], userId); 1365 } 1366 } 1367 1368 private static final String[] EMPTY_STRING_ARRAY = new String[0]; 1369 addPackageDeviceServer(ServiceInfo serviceInfo, int userId)1370 private void addPackageDeviceServer(ServiceInfo serviceInfo, int userId) { 1371 Log.d(TAG, "addPackageDeviceServer()" + userId); 1372 XmlResourceParser parser = null; 1373 1374 try { 1375 parser = serviceInfo.loadXmlMetaData(mPackageManager, 1376 MidiDeviceService.SERVICE_INTERFACE); 1377 if (parser == null) return; 1378 1379 // ignore virtual device servers that do not require the correct permission 1380 if (!android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE.equals( 1381 serviceInfo.permission)) { 1382 Log.w(TAG, "Skipping MIDI device service " + serviceInfo.packageName 1383 + ": it does not require the permission " 1384 + android.Manifest.permission.BIND_MIDI_DEVICE_SERVICE); 1385 return; 1386 } 1387 1388 Bundle properties = null; 1389 int numInputPorts = 0; 1390 int numOutputPorts = 0; 1391 boolean isPrivate = false; 1392 ArrayList<String> inputPortNames = new ArrayList<String>(); 1393 ArrayList<String> outputPortNames = new ArrayList<String>(); 1394 1395 while (true) { 1396 int eventType = parser.next(); 1397 if (eventType == XmlPullParser.END_DOCUMENT) { 1398 break; 1399 } else if (eventType == XmlPullParser.START_TAG) { 1400 String tagName = parser.getName(); 1401 if ("device".equals(tagName)) { 1402 if (properties != null) { 1403 Log.w(TAG, "nested <device> elements in metadata for " 1404 + serviceInfo.packageName); 1405 continue; 1406 } 1407 properties = new Bundle(); 1408 properties.putParcelable(MidiDeviceInfo.PROPERTY_SERVICE_INFO, serviceInfo); 1409 numInputPorts = 0; 1410 numOutputPorts = 0; 1411 isPrivate = false; 1412 1413 int count = parser.getAttributeCount(); 1414 for (int i = 0; i < count; i++) { 1415 String name = parser.getAttributeName(i); 1416 String value = parser.getAttributeValue(i); 1417 if ("private".equals(name)) { 1418 isPrivate = "true".equals(value); 1419 } else { 1420 properties.putString(name, value); 1421 } 1422 } 1423 } else if ("input-port".equals(tagName)) { 1424 if (properties == null) { 1425 Log.w(TAG, "<input-port> outside of <device> in metadata for " 1426 + serviceInfo.packageName); 1427 continue; 1428 } 1429 numInputPorts++; 1430 1431 String portName = null; 1432 int count = parser.getAttributeCount(); 1433 for (int i = 0; i < count; i++) { 1434 String name = parser.getAttributeName(i); 1435 String value = parser.getAttributeValue(i); 1436 if ("name".equals(name)) { 1437 portName = value; 1438 break; 1439 } 1440 } 1441 inputPortNames.add(portName); 1442 } else if ("output-port".equals(tagName)) { 1443 if (properties == null) { 1444 Log.w(TAG, "<output-port> outside of <device> in metadata for " 1445 + serviceInfo.packageName); 1446 continue; 1447 } 1448 numOutputPorts++; 1449 1450 String portName = null; 1451 int count = parser.getAttributeCount(); 1452 for (int i = 0; i < count; i++) { 1453 String name = parser.getAttributeName(i); 1454 String value = parser.getAttributeValue(i); 1455 if ("name".equals(name)) { 1456 portName = value; 1457 break; 1458 } 1459 } 1460 outputPortNames.add(portName); 1461 } 1462 } else if (eventType == XmlPullParser.END_TAG) { 1463 String tagName = parser.getName(); 1464 if ("device".equals(tagName)) { 1465 if (properties != null) { 1466 if (numInputPorts == 0 && numOutputPorts == 0) { 1467 Log.w(TAG, "<device> with no ports in metadata for " 1468 + serviceInfo.packageName); 1469 continue; 1470 } 1471 1472 int uid; 1473 try { 1474 ApplicationInfo appInfo = mPackageManager.getApplicationInfoAsUser( 1475 serviceInfo.packageName, 0, userId); 1476 uid = appInfo.uid; 1477 } catch (PackageManager.NameNotFoundException e) { 1478 Log.e(TAG, "could not fetch ApplicationInfo for " 1479 + serviceInfo.packageName); 1480 continue; 1481 } 1482 1483 synchronized (mDevicesByInfo) { 1484 addDeviceLocked(MidiDeviceInfo.TYPE_VIRTUAL, 1485 numInputPorts, numOutputPorts, 1486 inputPortNames.toArray(EMPTY_STRING_ARRAY), 1487 outputPortNames.toArray(EMPTY_STRING_ARRAY), 1488 properties, null, serviceInfo, isPrivate, uid, 1489 MidiDeviceInfo.PROTOCOL_UNKNOWN, userId); 1490 } 1491 // setting properties to null signals that we are no longer 1492 // processing a <device> 1493 properties = null; 1494 inputPortNames.clear(); 1495 outputPortNames.clear(); 1496 } 1497 } 1498 } 1499 } 1500 } catch (Exception e) { 1501 Log.w(TAG, "Unable to load component info " + serviceInfo.toString(), e); 1502 } finally { 1503 if (parser != null) parser.close(); 1504 } 1505 } 1506 removePackageDeviceServers(String packageName, int userId)1507 private void removePackageDeviceServers(String packageName, int userId) { 1508 synchronized (mDevicesByInfo) { 1509 Iterator<Device> iterator = mDevicesByInfo.values().iterator(); 1510 while (iterator.hasNext()) { 1511 Device device = iterator.next(); 1512 if (packageName.equals(device.getPackageName()) 1513 && (device.getUserId() == userId)) { 1514 iterator.remove(); 1515 removeDeviceLocked(device); 1516 } 1517 } 1518 } 1519 } 1520 1521 @Override updateTotalBytes(IMidiDeviceServer server, int totalInputBytes, int totalOutputBytes)1522 public void updateTotalBytes(IMidiDeviceServer server, int totalInputBytes, 1523 int totalOutputBytes) { 1524 synchronized (mDevicesByInfo) { 1525 Device device = mDevicesByServer.get(server.asBinder()); 1526 if (device != null) { 1527 device.updateTotalBytes(totalInputBytes, totalOutputBytes); 1528 } 1529 } 1530 } 1531 1532 @Override dump(FileDescriptor fd, PrintWriter writer, String[] args)1533 public void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 1534 if (!DumpUtils.checkDumpPermission(mContext, TAG, writer)) return; 1535 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); 1536 1537 pw.println("MIDI Manager State:"); 1538 pw.increaseIndent(); 1539 1540 pw.println("Devices:"); 1541 pw.increaseIndent(); 1542 synchronized (mDevicesByInfo) { 1543 for (Device device : mDevicesByInfo.values()) { 1544 pw.println(device.toString()); 1545 } 1546 } 1547 pw.decreaseIndent(); 1548 1549 pw.println("Clients:"); 1550 pw.increaseIndent(); 1551 synchronized (mClients) { 1552 for (Client client : mClients.values()) { 1553 pw.println(client.toString()); 1554 } 1555 } 1556 pw.decreaseIndent(); 1557 } 1558 1559 @GuardedBy("mUsbMidiLock") isUsbMidiDeviceInUseLocked(MidiDeviceInfo info)1560 private boolean isUsbMidiDeviceInUseLocked(MidiDeviceInfo info) { 1561 String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); 1562 if (name.length() < MIDI_LEGACY_STRING.length()) { 1563 return false; 1564 } 1565 String deviceName = extractUsbDeviceName(name); 1566 String tagName = extractUsbDeviceTag(name); 1567 1568 Log.i(TAG, "Checking " + deviceName + " " + tagName); 1569 1570 // Only one MIDI 2.0 device can be used at once. 1571 // Multiple MIDI 1.0 devices can be used at once. 1572 if (mUsbMidiUniversalDeviceInUse.contains(deviceName) 1573 || ((tagName).equals(MIDI_UNIVERSAL_STRING) 1574 && (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)))) { 1575 return true; 1576 } 1577 return false; 1578 } 1579 1580 @GuardedBy("mUsbMidiLock") addUsbMidiDeviceLocked(MidiDeviceInfo info)1581 void addUsbMidiDeviceLocked(MidiDeviceInfo info) { 1582 String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); 1583 if (name.length() < MIDI_LEGACY_STRING.length()) { 1584 return; 1585 } 1586 String deviceName = extractUsbDeviceName(name); 1587 String tagName = extractUsbDeviceTag(name); 1588 1589 Log.i(TAG, "Adding " + deviceName + " " + tagName); 1590 1591 if ((tagName).equals(MIDI_UNIVERSAL_STRING)) { 1592 mUsbMidiUniversalDeviceInUse.add(deviceName); 1593 } else if ((tagName).equals(MIDI_LEGACY_STRING)) { 1594 int count = mUsbMidiLegacyDeviceOpenCount.getOrDefault(deviceName, 0) + 1; 1595 mUsbMidiLegacyDeviceOpenCount.put(deviceName, count); 1596 } 1597 } 1598 1599 @GuardedBy("mUsbMidiLock") removeUsbMidiDeviceLocked(MidiDeviceInfo info)1600 void removeUsbMidiDeviceLocked(MidiDeviceInfo info) { 1601 String name = info.getProperties().getString(MidiDeviceInfo.PROPERTY_NAME); 1602 if (name.length() < MIDI_LEGACY_STRING.length()) { 1603 return; 1604 } 1605 String deviceName = extractUsbDeviceName(name); 1606 String tagName = extractUsbDeviceTag(name); 1607 1608 Log.i(TAG, "Removing " + deviceName + " " + tagName); 1609 1610 if ((tagName).equals(MIDI_UNIVERSAL_STRING)) { 1611 mUsbMidiUniversalDeviceInUse.remove(deviceName); 1612 } else if ((tagName).equals(MIDI_LEGACY_STRING)) { 1613 if (mUsbMidiLegacyDeviceOpenCount.containsKey(deviceName)) { 1614 int count = mUsbMidiLegacyDeviceOpenCount.get(deviceName); 1615 if (count > 1) { 1616 mUsbMidiLegacyDeviceOpenCount.put(deviceName, count - 1); 1617 } else { 1618 mUsbMidiLegacyDeviceOpenCount.remove(deviceName); 1619 } 1620 } 1621 } 1622 } 1623 1624 // The USB property name is in the form "manufacturer product#Id MIDI 1.0". 1625 // This is defined in UsbDirectMidiDevice.java. 1626 // This function extracts out the "manufacturer product#Id " part. 1627 // Two devices would have the same device name if they had the following property name: 1628 // "manufacturer product#Id MIDI 1.0" 1629 // "manufacturer product#Id MIDI 2.0" 1630 // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length. extractUsbDeviceName(String propertyName)1631 String extractUsbDeviceName(String propertyName) { 1632 return propertyName.substring(0, propertyName.length() - MIDI_LEGACY_STRING.length()); 1633 } 1634 1635 // The USB property name is in the form "manufacturer product#Id MIDI 1.0". 1636 // This is defined in UsbDirectMidiDevice.java. 1637 // This function extracts the "MIDI 1.0" part. 1638 // Note that MIDI_LEGACY_STRING and MIDI_UNIVERSAL_STRING are the same length. extractUsbDeviceTag(String propertyName)1639 String extractUsbDeviceTag(String propertyName) { 1640 return propertyName.substring(propertyName.length() - MIDI_LEGACY_STRING.length()); 1641 } 1642 1643 /** 1644 * @return the user id of the calling user. 1645 */ getCallingUserId()1646 private int getCallingUserId() { 1647 return UserHandle.getUserId(Binder.getCallingUid()); 1648 } 1649 } 1650