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