summaryrefslogtreecommitdiffstats
path: root/services/usb
diff options
context:
space:
mode:
Diffstat (limited to 'services/usb')
-rw-r--r--services/usb/java/com/android/server/usb/UsbAudioManager.java197
-rw-r--r--services/usb/java/com/android/server/usb/UsbDebuggingManager.java22
-rw-r--r--services/usb/java/com/android/server/usb/UsbDeviceManager.java36
-rw-r--r--services/usb/java/com/android/server/usb/UsbHostManager.java145
4 files changed, 251 insertions, 149 deletions
diff --git a/services/usb/java/com/android/server/usb/UsbAudioManager.java b/services/usb/java/com/android/server/usb/UsbAudioManager.java
new file mode 100644
index 0000000..bb45ee8
--- /dev/null
+++ b/services/usb/java/com/android/server/usb/UsbAudioManager.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions an
+ * limitations under the License.
+ */
+
+package com.android.server.usb;
+
+import android.alsa.AlsaCardsParser;
+import android.alsa.AlsaDevicesParser;
+import android.content.Context;
+import android.content.Intent;
+import android.hardware.usb.UsbConstants;
+import android.hardware.usb.UsbDevice;
+import android.hardware.usb.UsbInterface;
+import android.media.AudioManager;
+import android.os.UserHandle;
+import android.util.Slog;
+
+import java.io.File;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.HashMap;
+
+/**
+ * UsbAudioManager manages USB audio devices.
+ */
+public class UsbAudioManager {
+ private static final String TAG = UsbAudioManager.class.getSimpleName();
+ private static final boolean DEBUG = false;
+
+ private final Context mContext;
+
+ private final class AudioDevice {
+ public int mCard;
+ public int mDevice;
+ public boolean mHasPlayback;
+ public boolean mHasCapture;
+ public boolean mHasMIDI;
+
+ public AudioDevice(int card, int device,
+ boolean hasPlayback, boolean hasCapture, boolean hasMidi) {
+ mCard = card;
+ mDevice = device;
+ mHasPlayback = hasPlayback;
+ mHasCapture = hasCapture;
+ mHasMIDI = hasMidi;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("AudioDevice: [card: " + mCard);
+ sb.append(", device: " + mDevice);
+ sb.append(", hasPlayback: " + mHasPlayback);
+ sb.append(", hasCapture: " + mHasCapture);
+ sb.append(", hasMidi: " + mHasMIDI);
+ sb.append("]");
+ return sb.toString();
+ }
+ }
+
+ private final HashMap<UsbDevice,AudioDevice> mAudioDevices
+ = new HashMap<UsbDevice,AudioDevice>();
+
+ /* package */ UsbAudioManager(Context context) {
+ mContext = context;
+ }
+
+ // Broadcasts the arrival/departure of a USB audio interface
+ // audioDevice - the AudioDevice that was added or removed
+ // enabled - if true, we're connecting a device (it's arrived), else disconnecting
+ private void sendDeviceNotification(AudioDevice audioDevice, boolean enabled) {
+ // send a sticky broadcast containing current USB state
+ Intent intent = new Intent(AudioManager.ACTION_USB_AUDIO_DEVICE_PLUG);
+ intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
+ intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+ intent.putExtra("state", enabled ? 1 : 0);
+ intent.putExtra("card", audioDevice.mCard);
+ intent.putExtra("device", audioDevice.mDevice);
+ intent.putExtra("hasPlayback", audioDevice.mHasPlayback);
+ intent.putExtra("hasCapture", audioDevice.mHasCapture);
+ intent.putExtra("hasMIDI", audioDevice.mHasMIDI);
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ }
+
+ private boolean waitForAlsaFile(int card, int device, boolean capture) {
+ // These values were empirically determined.
+ final int kNumRetries = 5;
+ final int kSleepTime = 500; // ms
+ String alsaDevPath = "/dev/snd/pcmC" + card + "D" + device + (capture ? "c" : "p");
+ File alsaDevFile = new File(alsaDevPath);
+ boolean exists = false;
+ for (int retry = 0; !exists && retry < kNumRetries; retry++) {
+ exists = alsaDevFile.exists();
+ if (!exists) {
+ try {
+ Thread.sleep(kSleepTime);
+ } catch (IllegalThreadStateException ex) {
+ Slog.d(TAG, "usb: IllegalThreadStateException while waiting for ALSA file.");
+ } catch (java.lang.InterruptedException ex) {
+ Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
+ }
+ }
+ }
+
+ return exists;
+ }
+
+ /* package */ void deviceAdded(UsbDevice usbDevice) {
+ // Is there an audio interface in there?
+ boolean isAudioDevice = false;
+
+ // FIXME - handle multiple configurations?
+ int interfaceCount = usbDevice.getInterfaceCount();
+ for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < interfaceCount;
+ ntrfaceIndex++) {
+ UsbInterface ntrface = usbDevice.getInterface(ntrfaceIndex);
+ if (ntrface.getInterfaceClass() == UsbConstants.USB_CLASS_AUDIO) {
+ isAudioDevice = true;
+ }
+ }
+ if (!isAudioDevice) {
+ return;
+ }
+
+ //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is
+ // present, unlike the waitForAlsaFile() which waits on a file in /dev/snd. It is not
+ // clear why this works, or that it can be relied on going forward. Needs further
+ // research.
+ AlsaCardsParser cardsParser = new AlsaCardsParser();
+ cardsParser.scan();
+ // cardsParser.Log();
+
+ // But we need to parse the device to determine its capabilities.
+ AlsaDevicesParser devicesParser = new AlsaDevicesParser();
+ devicesParser.scan();
+ // devicesParser.Log();
+
+ // The protocol for now will be to select the last-connected (highest-numbered)
+ // Alsa Card.
+ int card = cardsParser.getNumCardRecords() - 1;
+ int device = 0;
+
+ boolean hasPlayback = devicesParser.hasPlaybackDevices(card);
+ boolean hasCapture = devicesParser.hasCaptureDevices(card);
+ boolean hasMidi = devicesParser.hasMIDIDevices(card);
+
+ // Playback device file needed/present?
+ if (hasPlayback &&
+ !waitForAlsaFile(card, device, false)) {
+ return;
+ }
+
+ // Capture device file needed/present?
+ if (hasCapture &&
+ !waitForAlsaFile(card, device, true)) {
+ return;
+ }
+
+ if (DEBUG) {
+ Slog.d(TAG,
+ "usb: hasPlayback:" + hasPlayback + " hasCapture:" + hasCapture);
+ }
+
+ AudioDevice audioDevice = new AudioDevice(card, device, hasPlayback, hasCapture, hasMidi);
+ mAudioDevices.put(usbDevice, audioDevice);
+ sendDeviceNotification(audioDevice, true);
+ }
+
+ /* package */ void deviceRemoved(UsbDevice device) {
+ if (DEBUG) {
+ Slog.d(TAG, "deviceRemoved(): " + device);
+ }
+
+ AudioDevice audioDevice = mAudioDevices.remove(device);
+ if (audioDevice != null) {
+ sendDeviceNotification(audioDevice, false);
+ }
+ }
+
+ public void dump(FileDescriptor fd, PrintWriter pw) {
+ pw.println(" USB AudioDevices:");
+ for (UsbDevice device : mAudioDevices.keySet()) {
+ pw.println(" " + device.getDeviceName() + ": " + mAudioDevices.get(device));
+ }
+ }
+}
diff --git a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
index cc5d004..1cf00d2 100644
--- a/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDebuggingManager.java
@@ -207,7 +207,12 @@ public class UsbDebuggingManager implements Runnable {
case MESSAGE_ADB_CONFIRM: {
String key = (String)msg.obj;
- mFingerprints = getFingerprints(key);
+ String fingerprints = getFingerprints(key);
+ if ("".equals(fingerprints)) {
+ sendResponse("NO");
+ break;
+ }
+ mFingerprints = fingerprints;
startConfirmation(key, mFingerprints);
break;
}
@@ -224,16 +229,25 @@ public class UsbDebuggingManager implements Runnable {
StringBuilder sb = new StringBuilder();
MessageDigest digester;
+ if (key == null) {
+ return "";
+ }
+
try {
digester = MessageDigest.getInstance("MD5");
} catch (Exception ex) {
- Slog.e(TAG, "Error getting digester: " + ex);
+ Slog.e(TAG, "Error getting digester", ex);
return "";
}
byte[] base64_data = key.split("\\s+")[0].getBytes();
- byte[] digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
-
+ byte[] digest;
+ try {
+ digest = digester.digest(Base64.decode(base64_data, Base64.DEFAULT));
+ } catch (IllegalArgumentException e) {
+ Slog.e(TAG, "error doing base64 decoding", e);
+ return "";
+ }
for (int i = 0; i < digest.length; i++) {
sb.append(hex.charAt((digest[i] >> 4) & 0xf));
sb.append(hex.charAt(digest[i] & 0xf));
diff --git a/services/usb/java/com/android/server/usb/UsbDeviceManager.java b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
index c63eb18..f3fa747 100644
--- a/services/usb/java/com/android/server/usb/UsbDeviceManager.java
+++ b/services/usb/java/com/android/server/usb/UsbDeviceManager.java
@@ -209,8 +209,13 @@ public class UsbDeviceManager {
mUseUsbNotification = !massStorageSupported;
// make sure the ADB_ENABLED setting value matches the current state
- Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
-
+ try {
+ Settings.Global.putInt(mContentResolver,
+ Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0);
+ } catch (SecurityException e) {
+ // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed.
+ Slog.d(TAG, "ADB_ENABLED is restricted.");
+ }
mHandler.sendEmptyMessage(MSG_SYSTEM_READY);
}
@@ -349,7 +354,7 @@ public class UsbDeviceManager {
SystemProperties.set("sys.usb.config", mDefaultFunctions);
}
- mCurrentFunctions = mDefaultFunctions;
+ mCurrentFunctions = getDefaultFunctions();
String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim();
updateState(state);
mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB);
@@ -455,8 +460,10 @@ public class UsbDeviceManager {
if (enable != mAdbEnabled) {
mAdbEnabled = enable;
// Due to the persist.sys.usb.config property trigger, changing adb state requires
- // switching to default function
+ // persisting default function
setEnabledFunctions(mDefaultFunctions, true);
+ // After persisting them use the lock-down aware function set
+ setEnabledFunctions(getDefaultFunctions(), false);
updateAdbNotification();
}
if (mDebuggingManager != null) {
@@ -552,7 +559,7 @@ public class UsbDeviceManager {
// make sure accessory mode is off
// and restore default functions
Slog.d(TAG, "exited USB accessory mode");
- setEnabledFunctions(mDefaultFunctions, false);
+ setEnabledFunctions(getDefaultFunctions(), false);
if (mCurrentAccessory != null) {
if (mBootCompleted) {
@@ -626,7 +633,7 @@ public class UsbDeviceManager {
updateCurrentAccessory();
} else if (!mConnected) {
// restore defaults when USB is disconnected
- setEnabledFunctions(mDefaultFunctions, false);
+ setEnabledFunctions(getDefaultFunctions(), false);
}
if (mBootCompleted) {
updateUsbState();
@@ -659,9 +666,11 @@ public class UsbDeviceManager {
case MSG_USER_SWITCHED: {
UserManager userManager =
(UserManager) mContext.getSystemService(Context.USER_SERVICE);
- if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER)) {
- Slog.v(TAG, "Switched to user with DISALLOW_USB_FILE_TRANSFER restriction;"
- + " disabling USB.");
+ UserHandle userHandle = new UserHandle(msg.arg1);
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER,
+ userHandle)) {
+ Slog.v(TAG, "Switched to user " + msg.arg1 +
+ " with DISALLOW_USB_FILE_TRANSFER restriction; disabling USB.");
setUsbConfig("none");
mCurrentUser = msg.arg1;
break;
@@ -785,6 +794,15 @@ public class UsbDeviceManager {
}
}
+ private String getDefaultFunctions() {
+ UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+ if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER,
+ new UserHandle(mCurrentUser))) {
+ return "none";
+ }
+ return mDefaultFunctions;
+ }
+
public void dump(FileDescriptor fd, PrintWriter pw) {
pw.println(" USB Device State:");
pw.println(" Current Functions: " + mCurrentFunctions);
diff --git a/services/usb/java/com/android/server/usb/UsbHostManager.java b/services/usb/java/com/android/server/usb/UsbHostManager.java
index 06febb3..e769bda 100644
--- a/services/usb/java/com/android/server/usb/UsbHostManager.java
+++ b/services/usb/java/com/android/server/usb/UsbHostManager.java
@@ -16,8 +16,6 @@
package com.android.server.usb;
-import android.alsa.AlsaCardsParser;
-import android.alsa.AlsaDevicesParser;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbConfiguration;
@@ -25,16 +23,13 @@ import android.hardware.usb.UsbConstants;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbEndpoint;
import android.hardware.usb.UsbInterface;
-import android.media.AudioManager;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.Parcelable;
-import android.os.UserHandle;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
-import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.PrintWriter;
@@ -46,11 +41,12 @@ import java.util.HashMap;
*/
public class UsbHostManager {
private static final String TAG = UsbHostManager.class.getSimpleName();
- private static final boolean DEBUG_AUDIO = false;
+ private static final boolean DEBUG = false;
// contains all connected USB devices
private final HashMap<String, UsbDevice> mDevices = new HashMap<String, UsbDevice>();
+
// USB busses to exclude from USB host support
private final String[] mHostBlacklist;
@@ -64,14 +60,7 @@ public class UsbHostManager {
private ArrayList<UsbInterface> mNewInterfaces;
private ArrayList<UsbEndpoint> mNewEndpoints;
- // Attributes of any connected USB audio device.
- //TODO(pmclean) When we extend to multiple, USB Audio devices, we will need to get
- // more clever about this.
- private int mConnectedUsbCard = -1;
- private int mConnectedUsbDeviceNum = -1;
- private boolean mConnectedHasPlayback = false;
- private boolean mConnectedHasCapture = false;
- private boolean mConnectedHasMIDI = false;
+ private UsbAudioManager mUsbAudioManager;
@GuardedBy("mLock")
private UsbSettingsManager mCurrentSettings;
@@ -80,6 +69,7 @@ public class UsbHostManager {
mContext = context;
mHostBlacklist = context.getResources().getStringArray(
com.android.internal.R.array.config_usbHostBlacklist);
+ mUsbAudioManager = new UsbAudioManager(context);
}
public void setCurrentSettings(UsbSettingsManager settings) {
@@ -118,48 +108,6 @@ public class UsbHostManager {
return false;
}
- // Broadcasts the arrival/departure of a USB audio interface
- // card - the ALSA card number of the physical interface
- // device - the ALSA device number of the physical interface
- // enabled - if true, we're connecting a device (it's arrived), else disconnecting
- private void sendDeviceNotification(int card, int device, boolean enabled,
- boolean hasPlayback, boolean hasCapture, boolean hasMIDI) {
- // send a sticky broadcast containing current USB state
- Intent intent = new Intent(AudioManager.ACTION_USB_AUDIO_DEVICE_PLUG);
- intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
- intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
- intent.putExtra("state", enabled ? 1 : 0);
- intent.putExtra("card", card);
- intent.putExtra("device", device);
- intent.putExtra("hasPlayback", hasPlayback);
- intent.putExtra("hasCapture", hasCapture);
- intent.putExtra("hasMIDI", hasMIDI);
- mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
- }
-
- private boolean waitForAlsaFile(int card, int device, boolean capture) {
- // These values were empirically determined.
- final int kNumRetries = 5;
- final int kSleepTime = 500; // ms
- String alsaDevPath = "/dev/snd/pcmC" + card + "D" + device + (capture ? "c" : "p");
- File alsaDevFile = new File(alsaDevPath);
- boolean exists = false;
- for (int retry = 0; !exists && retry < kNumRetries; retry++) {
- exists = alsaDevFile.exists();
- if (!exists) {
- try {
- Thread.sleep(kSleepTime);
- } catch (IllegalThreadStateException ex) {
- Slog.d(TAG, "usb: IllegalThreadStateException while waiting for ALSA file.");
- } catch (java.lang.InterruptedException ex) {
- Slog.d(TAG, "usb: InterruptedException while waiting for ALSA file.");
- }
- }
- }
-
- return exists;
- }
-
/* Called from JNI in monitorUsbHostBus() to report new USB devices
Returns true if successful, in which case the JNI code will continue adding configurations,
interfaces and endpoints, and finally call endUsbDeviceAdded after all descriptors
@@ -169,7 +117,7 @@ public class UsbHostManager {
int deviceClass, int deviceSubclass, int deviceProtocol,
String manufacturerName, String productName, String serialNumber) {
- if (DEBUG_AUDIO) {
+ if (DEBUG) {
Slog.d(TAG, "usb:UsbHostManager.beginUsbDeviceAdded(" + deviceName + ")");
// Audio Class Codes:
// Audio: 0x01
@@ -254,7 +202,7 @@ public class UsbHostManager {
/* Called from JNI in monitorUsbHostBus() to finish adding a new device */
private void endUsbDeviceAdded() {
- if (DEBUG_AUDIO) {
+ if (DEBUG) {
Slog.d(TAG, "usb:UsbHostManager.endUsbDeviceAdded()");
}
if (mNewInterface != null) {
@@ -266,16 +214,6 @@ public class UsbHostManager {
mNewInterfaces.toArray(new UsbInterface[mNewInterfaces.size()]));
}
- // Is there an audio interface in there?
- final int kUsbClassId_Audio = 0x01;
- boolean isAudioDevice = false;
- for (int ntrfaceIndex = 0; !isAudioDevice && ntrfaceIndex < mNewInterfaces.size();
- ntrfaceIndex++) {
- UsbInterface ntrface = mNewInterfaces.get(ntrfaceIndex);
- if (ntrface.getInterfaceClass() == kUsbClassId_Audio) {
- isAudioDevice = true;
- }
- }
synchronized (mLock) {
if (mNewDevice != null) {
@@ -284,6 +222,7 @@ public class UsbHostManager {
mDevices.put(mNewDevice.getDeviceName(), mNewDevice);
Slog.d(TAG, "Added device " + mNewDevice);
getCurrentSettings().deviceAttached(mNewDevice);
+ mUsbAudioManager.deviceAdded(mNewDevice);
} else {
Slog.e(TAG, "mNewDevice is null in endUsbDeviceAdded");
}
@@ -292,81 +231,14 @@ public class UsbHostManager {
mNewInterfaces = null;
mNewEndpoints = null;
}
-
- if (!isAudioDevice) {
- return; // bail
- }
-
- //TODO(pmclean) The "Parser" objects inspect files in "/proc/asound" which we presume is
- // present, unlike the waitForAlsaFile() which waits on a file in /dev/snd. It is not
- // clear why this works, or that it can be relied on going forward. Needs further
- // research.
- AlsaCardsParser cardsParser = new AlsaCardsParser();
- cardsParser.scan();
- // cardsParser.Log();
-
- // But we need to parse the device to determine its capabilities.
- AlsaDevicesParser devicesParser = new AlsaDevicesParser();
- devicesParser.scan();
- // devicesParser.Log();
-
- // The protocol for now will be to select the last-connected (highest-numbered)
- // Alsa Card.
- mConnectedUsbCard = cardsParser.getNumCardRecords() - 1;
- mConnectedUsbDeviceNum = 0;
-
- mConnectedHasPlayback = devicesParser.hasPlaybackDevices(mConnectedUsbCard);
- mConnectedHasCapture = devicesParser.hasCaptureDevices(mConnectedUsbCard);
- mConnectedHasMIDI = devicesParser.hasMIDIDevices(mConnectedUsbCard);
-
- // Playback device file needed/present?
- if (mConnectedHasPlayback &&
- !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, false)) {
- return;
- }
-
- // Capture device file needed/present?
- if (mConnectedHasCapture &&
- !waitForAlsaFile(mConnectedUsbCard, mConnectedUsbDeviceNum, true)) {
- return;
- }
-
- if (DEBUG_AUDIO) {
- Slog.d(TAG,
- "usb: hasPlayback:" + mConnectedHasPlayback + " hasCapture:" + mConnectedHasCapture);
- }
-
- sendDeviceNotification(mConnectedUsbCard,
- mConnectedUsbDeviceNum,
- true,
- mConnectedHasPlayback,
- mConnectedHasCapture,
- mConnectedHasMIDI);
}
/* Called from JNI in monitorUsbHostBus to report USB device removal */
private void usbDeviceRemoved(String deviceName) {
- if (DEBUG_AUDIO) {
- Slog.d(TAG, "usb:UsbHostManager.usbDeviceRemoved() nm:" + deviceName);
- }
-
- if (mConnectedUsbCard != -1 && mConnectedUsbDeviceNum != -1) {
- sendDeviceNotification(mConnectedUsbCard,
- mConnectedUsbDeviceNum,
- false,
- mConnectedHasPlayback,
- mConnectedHasCapture,
- mConnectedHasMIDI);
- mConnectedUsbCard = -1;
- mConnectedUsbDeviceNum = -1;
- mConnectedHasPlayback = false;
- mConnectedHasCapture = false;
- mConnectedHasMIDI = false;
- }
-
synchronized (mLock) {
UsbDevice device = mDevices.remove(deviceName);
if (device != null) {
+ mUsbAudioManager.deviceRemoved(device);
getCurrentSettings().deviceDetached(device);
}
}
@@ -418,6 +290,7 @@ public class UsbHostManager {
pw.println(" " + name + ": " + mDevices.get(name));
}
}
+ mUsbAudioManager.dump(fd, pw);
}
private native void monitorUsbHostBus();