DO NOT MERGE: Backport USB accessory support to gingerbread

Signed-off-by: Mike Lockwood <lockwood@android.com>
This commit is contained in:
Mike Lockwood
2011-03-01 10:23:48 -08:00
parent db52ab69f2
commit 40bbf9295d
25 changed files with 1956 additions and 376 deletions

View File

@ -113,6 +113,7 @@ LOCAL_SRC_FILES += \
core/java/android/content/pm/IPackageMoveObserver.aidl \ core/java/android/content/pm/IPackageMoveObserver.aidl \
core/java/android/content/pm/IPackageStatsObserver.aidl \ core/java/android/content/pm/IPackageStatsObserver.aidl \
core/java/android/database/IContentObserver.aidl \ core/java/android/database/IContentObserver.aidl \
core/java/android/hardware/usb/IUsbManager.aidl \
core/java/android/net/IConnectivityManager.aidl \ core/java/android/net/IConnectivityManager.aidl \
core/java/android/net/INetworkManagementEventObserver.aidl \ core/java/android/net/INetworkManagementEventObserver.aidl \
core/java/android/net/IThrottleManager.aidl \ core/java/android/net/IThrottleManager.aidl \

View File

@ -62,6 +62,8 @@ import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.hardware.SensorManager; import android.hardware.SensorManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbManager;
import android.location.ILocationManager; import android.location.ILocationManager;
import android.location.LocationManager; import android.location.LocationManager;
import android.media.AudioManager; import android.media.AudioManager;
@ -191,6 +193,7 @@ class ContextImpl extends Context {
private SearchManager mSearchManager = null; private SearchManager mSearchManager = null;
private SensorManager mSensorManager = null; private SensorManager mSensorManager = null;
private StorageManager mStorageManager = null; private StorageManager mStorageManager = null;
private UsbManager mUsbManager = null;
private Vibrator mVibrator = null; private Vibrator mVibrator = null;
private LayoutInflater mLayoutInflater = null; private LayoutInflater mLayoutInflater = null;
private StatusBarManager mStatusBarManager = null; private StatusBarManager mStatusBarManager = null;
@ -954,6 +957,8 @@ class ContextImpl extends Context {
return getSensorManager(); return getSensorManager();
} else if (STORAGE_SERVICE.equals(name)) { } else if (STORAGE_SERVICE.equals(name)) {
return getStorageManager(); return getStorageManager();
} else if (USB_SERVICE.equals(name)) {
return getUsbManager();
} else if (VIBRATOR_SERVICE.equals(name)) { } else if (VIBRATOR_SERVICE.equals(name)) {
return getVibrator(); return getVibrator();
} else if (STATUS_BAR_SERVICE.equals(name)) { } else if (STATUS_BAR_SERVICE.equals(name)) {
@ -1148,6 +1153,17 @@ class ContextImpl extends Context {
return mStorageManager; return mStorageManager;
} }
private UsbManager getUsbManager() {
synchronized (mSync) {
if (mUsbManager == null) {
IBinder b = ServiceManager.getService(USB_SERVICE);
IUsbManager service = IUsbManager.Stub.asInterface(b);
mUsbManager = new UsbManager(service);
}
}
return mUsbManager;
}
private Vibrator getVibrator() { private Vibrator getVibrator() {
synchronized (mSync) { synchronized (mSync) {
if (mVibrator == null) { if (mVibrator == null) {

View File

@ -1566,6 +1566,17 @@ public abstract class Context {
/** @hide */ /** @hide */
public static final String SIP_SERVICE = "sip"; public static final String SIP_SERVICE = "sip";
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.hardware.usb.UsbManager} for access to USB devices (as a USB host)
* and for controlling this device's behavior as a USB device.
*
* @see #getSystemService
* @see android.harware.usb.UsbManager
* @hide
*/
public static final String USB_SERVICE = "usb";
/** /**
* Determine whether the given permission is allowed for a particular * Determine whether the given permission is allowed for a particular
* process and user ID running in the system. * process and user ID running in the system.

View File

@ -783,6 +783,13 @@ public abstract class PackageManager {
@SdkConstant(SdkConstantType.FEATURE) @SdkConstant(SdkConstantType.FEATURE)
public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm"; public static final String FEATURE_TELEPHONY_GSM = "android.hardware.telephony.gsm";
/**
* Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports connecting to USB accessories.
* @hide
*/
public static final String FEATURE_USB_ACCESSORY = "android.hardware.usb.accessory";
/** /**
* Feature for {@link #getSystemAvailableFeatures} and * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The SIP API is enabled on the device. * {@link #hasSystemFeature}: The SIP API is enabled on the device.

View File

@ -1,115 +0,0 @@
/*
* Copyright (C) 2010 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 and
* limitations under the License.
*/
package android.hardware;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
/**
* Class for accessing USB state information.
* @hide
*/
public class UsbManager {
/**
* Broadcast Action: A sticky broadcast for USB state change events.
*
* This is a sticky broadcast for clients that includes USB connected/disconnected state,
* the USB configuration that is currently set and a bundle containing name/value pairs
* with the names of the functions and a value of either {@link #USB_FUNCTION_ENABLED}
* or {@link #USB_FUNCTION_DISABLED}.
* Possible USB function names include {@link #USB_FUNCTION_MASS_STORAGE},
* {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS} and {@link #USB_FUNCTION_MTP}.
*/
public static final String ACTION_USB_STATE =
"android.hardware.action.USB_STATE";
/**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*/
public static final String USB_CONNECTED = "connected";
/**
* Integer extra containing currently set USB configuration.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*/
public static final String USB_CONFIGURATION = "configuration";
/**
* Name of the USB mass storage USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
/**
* Name of the adb USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_ADB = "adb";
/**
* Name of the RNDIS ethernet USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_RNDIS = "rndis";
/**
* Name of the MTP USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_MTP = "mtp";
/**
* Value indicating that a USB function is enabled.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_ENABLED = "enabled";
/**
* Value indicating that a USB function is disabled.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_DISABLED = "disabled";
private static File getFunctionEnableFile(String function) {
return new File("/sys/class/usb_composite/" + function + "/enable");
}
/**
* Returns true if the specified USB function is supported by the kernel.
* Note that a USB function maybe supported but disabled.
*/
public static boolean isFunctionSupported(String function) {
return getFunctionEnableFile(function).exists();
}
/**
* Returns true if the specified USB function is currently enabled.
*/
public static boolean isFunctionEnabled(String function) {
try {
FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
boolean enabled = (stream.read() == '1');
stream.close();
return enabled;
} catch (IOException e) {
return false;
}
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright (C) 2010 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 and
* limitations under the License.
*/
package android.hardware.usb;
import android.hardware.usb.UsbAccessory;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
/** @hide */
interface IUsbManager
{
/* Returns the currently attached USB accessory */
UsbAccessory getCurrentAccessory();
/* Returns a file descriptor for communicating with the USB accessory.
* This file descriptor can be used with standard Java file operations.
*/
ParcelFileDescriptor openAccessory(in UsbAccessory accessory);
/* Sets the default package for a USB accessory
* (or clears it if the package name is null)
*/
void setAccessoryPackage(in UsbAccessory accessory, String packageName);
/* Grants permission for the given UID to access the accessory */
void grantAccessoryPermission(in UsbAccessory accessory, int uid);
/* Returns true if the USB manager has default preferences or permissions for the package */
boolean hasDefaults(String packageName, int uid);
/* Clears default preferences and permissions for the package */
oneway void clearDefaults(String packageName, int uid);
}

View File

@ -0,0 +1,19 @@
/*
* Copyright (C) 2011, 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 and
* limitations under the License.
*/
package android.hardware.usb;
parcelable UsbAccessory;

View File

@ -0,0 +1,145 @@
/*
* Copyright (C) 2011 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 and
* limitations under the License.
*/
package android.hardware.usb;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
/**
* A class representing a USB accessory.
* @hide
*/
public class UsbAccessory implements Parcelable {
private static final String TAG = "UsbAccessory";
private final String mManufacturer;
private final String mModel;
private final String mType;
private final String mVersion;
/**
* UsbAccessory should only be instantiated by UsbService implementation
* @hide
*/
public UsbAccessory(String manufacturer, String model, String type, String version) {
mManufacturer = manufacturer;
mModel = model;
mType = type;
mVersion = version;
}
/**
* UsbAccessory should only be instantiated by UsbService implementation
* @hide
*/
public UsbAccessory(String[] strings) {
mManufacturer = strings[0];
mModel = strings[1];
mType = strings[2];
mVersion = strings[3];
}
/**
* Returns the manufacturer of the accessory.
*
* @return the accessory manufacturer
*/
public String getManufacturer() {
return mManufacturer;
}
/**
* Returns the model name of the accessory.
*
* @return the accessory model
*/
public String getModel() {
return mModel;
}
/**
* Returns the type of the accessory.
*
* @return the accessory type
*/
public String getType() {
return mType;
}
/**
* Returns the version of the accessory.
*
* @return the accessory version
*/
public String getVersion() {
return mVersion;
}
private static boolean compare(String s1, String s2) {
if (s1 == null) return (s2 == null);
return s1.equals(s2);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UsbAccessory) {
UsbAccessory accessory = (UsbAccessory)obj;
return (compare(mManufacturer, accessory.getManufacturer()) &&
compare(mModel, accessory.getModel()) &&
compare(mType, accessory.getType()) &&
compare(mVersion, accessory.getVersion()));
}
return false;
}
@Override
public String toString() {
return "UsbAccessory[mManufacturer=" + mManufacturer +
", mModel=" + mModel +
", mType=" + mType +
", mVersion=" + mVersion + "]";
}
public static final Parcelable.Creator<UsbAccessory> CREATOR =
new Parcelable.Creator<UsbAccessory>() {
public UsbAccessory createFromParcel(Parcel in) {
String manufacturer = in.readString();
String model = in.readString();
String type = in.readString();
String version = in.readString();
return new UsbAccessory(manufacturer, model, type, version);
}
public UsbAccessory[] newArray(int size) {
return new UsbAccessory[size];
}
};
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeString(mManufacturer);
parcel.writeString(mModel);
parcel.writeString(mType);
parcel.writeString(mVersion);
}
}

View File

@ -0,0 +1,239 @@
/*
* Copyright (C) 2010 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 and
* limitations under the License.
*/
package android.hardware.usb;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.util.Log;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
/**
* This class allows you to access the state of USB.
*
* <p>You can obtain an instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
*
* {@samplecode
* UsbManager manager = (UsbManager) getSystemService(Context.USB_SERVICE);
* }
* @hide
*/
public class UsbManager {
private static final String TAG = "UsbManager";
/**
* Broadcast Action: A sticky broadcast for USB state change events when in device mode.
*
* This is a sticky broadcast for clients that includes USB connected/disconnected state,
* <ul>
* <li> {@link #USB_CONNECTED} boolean indicating whether USB is connected or disconnected.
* <li> {@link #USB_CONFIGURATION} a Bundle containing name/value pairs where the name
* is the name of a USB function and the value is either {@link #USB_FUNCTION_ENABLED}
* or {@link #USB_FUNCTION_DISABLED}. The possible function names include
* {@link #USB_FUNCTION_MASS_STORAGE}, {@link #USB_FUNCTION_ADB}, {@link #USB_FUNCTION_RNDIS},
* {@link #USB_FUNCTION_MTP} and {@link #USB_FUNCTION_ACCESSORY}.
* </ul>
*/
public static final String ACTION_USB_STATE =
"android.hardware.usb.action.USB_STATE";
/**
* Broadcast Action: A broadcast for USB accessory attached event.
*
* This intent is sent when a USB accessory is attached.
* <ul>
* <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
* for the attached accessory
* </ul>
*/
public static final String ACTION_USB_ACCESSORY_ATTACHED =
"android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
/**
* Broadcast Action: A broadcast for USB accessory detached event.
*
* This intent is sent when a USB accessory is detached.
* <ul>
* <li> {@link #EXTRA_ACCESSORY} containing the {@link android.hardware.usb.UsbAccessory}
* for the attached accessory that was detached
* </ul>
*/
public static final String ACTION_USB_ACCESSORY_DETACHED =
"android.hardware.usb.action.USB_ACCESSORY_DETACHED";
/**
* Boolean extra indicating whether USB is connected or disconnected.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*/
public static final String USB_CONNECTED = "connected";
/**
* Integer extra containing currently set USB configuration.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast.
*/
public static final String USB_CONFIGURATION = "configuration";
/**
* Name of the USB mass storage USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_MASS_STORAGE = "mass_storage";
/**
* Name of the adb USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_ADB = "adb";
/**
* Name of the RNDIS ethernet USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_RNDIS = "rndis";
/**
* Name of the MTP USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_MTP = "mtp";
/**
* Name of the Accessory USB function.
* Used in extras for the {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_ACCESSORY = "accessory";
/**
* Value indicating that a USB function is enabled.
* Used in {@link #USB_CONFIGURATION} extras bundle for the
* {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_ENABLED = "enabled";
/**
* Value indicating that a USB function is disabled.
* Used in {@link #USB_CONFIGURATION} extras bundle for the
* {@link #ACTION_USB_STATE} broadcast
*/
public static final String USB_FUNCTION_DISABLED = "disabled";
/**
* Name of extra for {@link #ACTION_USB_ACCESSORY_ATTACHED} and
* {@link #ACTION_USB_ACCESSORY_DETACHED} broadcasts
* containing the UsbAccessory object for the accessory.
*/
public static final String EXTRA_ACCESSORY = "accessory";
private IUsbManager mService;
/**
* {@hide}
*/
public UsbManager(IUsbManager service) {
mService = service;
}
/**
* Returns a list of currently attached USB accessories.
* (in the current implementation there can be at most one)
*
* @return list of USB accessories, or null if none are attached.
*/
public UsbAccessory[] getAccessoryList() {
try {
UsbAccessory accessory = mService.getCurrentAccessory();
if (accessory == null) {
return null;
} else {
return new UsbAccessory[] { accessory };
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getAccessoryList" , e);
return null;
}
}
/**
* Opens a file descriptor for reading and writing data to the USB accessory.
*
* @param accessory the USB accessory to open
* @return file descriptor, or null if the accessor could not be opened.
*/
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
try {
return mService.openAccessory(accessory);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in openAccessory" , e);
return null;
}
}
private static File getFunctionEnableFile(String function) {
return new File("/sys/class/usb_composite/" + function + "/enable");
}
/**
* Returns true if the specified USB function is supported by the kernel.
* Note that a USB function maybe supported but disabled.
*
* @param function name of the USB function
* @return true if the USB function is supported.
*/
public static boolean isFunctionSupported(String function) {
return getFunctionEnableFile(function).exists();
}
/**
* Returns true if the specified USB function is currently enabled.
*
* @param function name of the USB function
* @return true if the USB function is enabled.
*/
public static boolean isFunctionEnabled(String function) {
try {
FileInputStream stream = new FileInputStream(getFunctionEnableFile(function));
boolean enabled = (stream.read() == '1');
stream.close();
return enabled;
} catch (IOException e) {
return false;
}
}
/**
* Enables or disables a USB function.
*
* @hide
*/
public static boolean setFunctionEnabled(String function, boolean enable) {
try {
FileOutputStream stream = new FileOutputStream(getFunctionEnableFile(function));
stream.write(enable ? '1' : '0');
stream.close();
return true;
} catch (IOException e) {
return false;
}
}
}

View File

@ -90,6 +90,17 @@ public class SparseArray<E> {
delete(key); delete(key);
} }
/**
* Removes the mapping at the specified index.
* @hide
*/
public void removeAt(int index) {
if (mValues[index] != DELETED) {
mValues[index] = DELETED;
mGarbage = true;
}
}
private void gc() { private void gc() {
// Log.e("SparseArray", "gc start with " + mSize); // Log.e("SparseArray", "gc start with " + mSize);

View File

@ -68,7 +68,7 @@ public class ResolverActivity extends AlertActivity implements
protected void onCreate(Bundle savedInstanceState, Intent intent, protected void onCreate(Bundle savedInstanceState, Intent intent,
CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList, CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
boolean alwaysUseOption) { boolean alwaysUseOption, boolean alwaysChoose) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
mPm = getPackageManager(); mPm = getPackageManager();
intent.setComponent(null); intent.setComponent(null);
@ -90,9 +90,10 @@ public class ResolverActivity extends AlertActivity implements
mClearDefaultHint.setVisibility(View.GONE); mClearDefaultHint.setVisibility(View.GONE);
} }
mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList); mAdapter = new ResolveListAdapter(this, intent, initialIntents, rList);
if (mAdapter.getCount() > 1) { int count = mAdapter.getCount();
if (count > 1 || (count == 1 && alwaysChoose)) {
ap.mAdapter = mAdapter; ap.mAdapter = mAdapter;
} else if (mAdapter.getCount() == 1) { } else if (count == 1) {
startActivity(mAdapter.intentForPosition(0)); startActivity(mAdapter.intentForPosition(0));
finish(); finish();
return; return;
@ -103,11 +104,22 @@ public class ResolverActivity extends AlertActivity implements
setupAlert(); setupAlert();
} }
protected void onCreate(Bundle savedInstanceState, Intent intent,
CharSequence title, Intent[] initialIntents, List<ResolveInfo> rList,
boolean alwaysUseOption) {
onCreate(savedInstanceState, intent, title, initialIntents, rList, alwaysUseOption, false);
}
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
ResolveInfo ri = mAdapter.resolveInfoForPosition(which); ResolveInfo ri = mAdapter.resolveInfoForPosition(which);
Intent intent = mAdapter.intentForPosition(which); Intent intent = mAdapter.intentForPosition(which);
boolean alwaysCheck = (mAlwaysCheck != null && mAlwaysCheck.isChecked());
onIntentSelected(ri, intent, alwaysCheck);
finish();
}
if ((mAlwaysCheck != null) && mAlwaysCheck.isChecked()) { protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
if (alwaysCheck) {
// Build a reasonable intent filter, based on what matched. // Build a reasonable intent filter, based on what matched.
IntentFilter filter = new IntentFilter(); IntentFilter filter = new IntentFilter();
@ -190,7 +202,6 @@ public class ResolverActivity extends AlertActivity implements
if (intent != null) { if (intent != null) {
startActivity(intent); startActivity(intent);
} }
finish();
} }
private final class DisplayResolveInfo { private final class DisplayResolveInfo {

View File

@ -82,9 +82,9 @@
<protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" /> <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_REQUEST" />
<protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" /> <protected-broadcast android:name="android.bluetooth.device.action.PAIRING_CANCEL" />
<protected-broadcast android:name="android.hardware.action.USB_CONNECTED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_STATE" />
<protected-broadcast android:name="android.hardware.action.USB_DISCONNECTED" /> <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
<protected-broadcast android:name="android.hardware.action.USB_STATE" /> <protected-broadcast android:name="android.hardware.usb.action.USB_ACCESSORY_ATTACHED" />
<protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" /> <protected-broadcast android:name="android.nfc.action.LLCP_LINK_STATE_CHANGED" />
<protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" /> <protected-broadcast android:name="android.nfc.action.TRANSACTION_DETECTED" />
@ -453,13 +453,13 @@
android:label="@string/permlab_flashlight" android:label="@string/permlab_flashlight"
android:description="@string/permdesc_flashlight" /> android:description="@string/permdesc_flashlight" />
<!-- Allows an application to access USB devices <!-- Allows an application to manage preferences and permissions for USB devices
@hide --> @hide -->
<permission android:name="android.permission.ACCESS_USB" <permission android:name="android.permission.MANAGE_USB"
android:permissionGroup="android.permission-group.HARDWARE_CONTROLS" android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
android:protectionLevel="signatureOrSystem" android:protectionLevel="signatureOrSystem"
android:label="@string/permlab_accessUsb" android:label="@string/permlab_manageUsb"
android:description="@string/permdesc_accessUsb" /> android:description="@string/permdesc_manageUsb" />
<!-- Allows access to hardware peripherals. Intended only for hardware testing --> <!-- Allows access to hardware peripherals. Intended only for hardware testing -->
<permission android:name="android.permission.HARDWARE_TEST" <permission android:name="android.permission.HARDWARE_TEST"
@ -1330,6 +1330,12 @@
android:excludeFromRecents="true"> android:excludeFromRecents="true">
</activity> </activity>
<activity android:name="com.android.server.usb.UsbResolverActivity"
android:theme="@style/Theme.Dialog.Alert"
android:finishOnCloseSystemDialogs="true"
android:excludeFromRecents="true">
</activity>
<service android:name="com.android.server.LoadAverageService" <service android:name="com.android.server.LoadAverageService"
android:exported="true" /> android:exported="true" />

View File

@ -927,9 +927,9 @@
the flashlight.</string> the flashlight.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_accessUsb">access USB devices</string> <string name="permlab_manageUsb">manage preferences and permissions for USB devices</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_accessUsb">Allows the application to access USB devices.</string> <string name="permdesc_manageUsb">Allows the application to manage preferences and permissions for USB devices.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_hardware_test">test hardware</string> <string name="permlab_hardware_test">test hardware</string>
@ -1926,6 +1926,8 @@
<string name="clearDefaultHintMsg">Clear default in Home Settings &gt; Applications &gt; Manage applications.</string> <string name="clearDefaultHintMsg">Clear default in Home Settings &gt; Applications &gt; Manage applications.</string>
<!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action. for example, there may be many ringtone pickers installed. A dialog is shown to the user allowing him to pick which activity should be used. This is the title. --> <!-- Default title for the activity chooser, when one is not given. Android allows multiple activities to perform an action. for example, there may be many ringtone pickers installed. A dialog is shown to the user allowing him to pick which activity should be used. This is the title. -->
<string name="chooseActivity">Select an action</string> <string name="chooseActivity">Select an action</string>
<!-- title for the USB activity chooser. -->
<string name="chooseUsbActivity">Select an application for the USB device</string>
<!-- Text to display when there are no activities found to display in the <!-- Text to display when there are no activities found to display in the
activity chooser. See the "Select an action" title. --> activity chooser. See the "Select an action" title. -->
<string name="noApplications">No applications can perform this action.</string> <string name="noApplications">No applications can perform this action.</string>

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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 and
limitations under the License.
-->
<!-- This is the standard feature indicating that the device supports USB accessories. -->
<permissions>
<feature name="android.hardware.usb.accessory" />
</permissions>

View File

@ -0,0 +1,96 @@
/*
* Copyright (C) 2011 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 and
* limitations under the License.
*/
package com.google.android.usb;
/**
* A class representing a USB accessory.
*/
public final class UsbAccessory {
private final String mManufacturer;
private final String mModel;
private final String mType;
private final String mVersion;
/* package */ UsbAccessory(android.hardware.usb.UsbAccessory accessory) {
mManufacturer = accessory.getManufacturer();
mModel = accessory.getModel();
mType = accessory.getType();
mVersion = accessory.getVersion();
}
/**
* Returns the manufacturer of the accessory.
*
* @return the accessory manufacturer
*/
public String getManufacturer() {
return mManufacturer;
}
/**
* Returns the model name of the accessory.
*
* @return the accessory model
*/
public String getModel() {
return mModel;
}
/**
* Returns the type of the accessory.
*
* @return the accessory type
*/
public String getType() {
return mType;
}
/**
* Returns the version of the accessory.
*
* @return the accessory version
*/
public String getVersion() {
return mVersion;
}
private static boolean compare(String s1, String s2) {
if (s1 == null) return (s2 == null);
return s1.equals(s2);
}
@Override
public boolean equals(Object obj) {
if (obj instanceof UsbAccessory) {
UsbAccessory accessory = (UsbAccessory)obj;
return (compare(mManufacturer, accessory.getManufacturer()) &&
compare(mModel, accessory.getModel()) &&
compare(mType, accessory.getType()) &&
compare(mVersion, accessory.getVersion()));
}
return false;
}
@Override
public String toString() {
return "UsbAccessory[mManufacturer=" + mManufacturer +
", mModel=" + mModel +
", mType=" + mType +
", mVersion=" + mVersion + "]";
}
}

View File

@ -0,0 +1,127 @@
/*
* Copyright (C) 2011 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 and
* limitations under the License.
*/
package com.google.android.usb;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.IUsbManager;
import android.os.IBinder;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
/**
* This class allows you to access the state of USB, both in host and device mode.
*
* <p>You can obtain an instance of this class by calling {@link #getInstance}
*
*/
public class UsbManager {
private static final String TAG = "UsbManager";
/**
* Broadcast Action: A broadcast for USB accessory attached event.
*
* This intent is sent when a USB accessory is attached.
* Call {@link #getAccessory(android.content.Intent)} to retrieve the
* {@link com.google.android.usb.UsbAccessory} for the attached accessory.
*/
public static final String ACTION_USB_ACCESSORY_ATTACHED =
"android.hardware.usb.action.USB_ACCESSORY_ATTACHED";
/**
* Broadcast Action: A broadcast for USB accessory detached event.
*
* This intent is sent when a USB accessory is detached.
* Call {@link #getAccessory(android.content.Intent)} to retrieve the
* {@link com.google.android.usb.UsbAccessory} for the attached accessory that was detached.
*/
public static final String ACTION_USB_ACCESSORY_DETACHED =
"android.hardware.usb.action.USB_ACCESSORY_DETACHED";
private final IUsbManager mService;
private UsbManager(IUsbManager service) {
mService = service;
}
/**
* Returns a new instance of this class.
*
* @return UsbManager instance.
*/
public static UsbManager getInstance() {
IBinder b = ServiceManager.getService(Context.USB_SERVICE);
return new UsbManager(IUsbManager.Stub.asInterface(b));
}
/**
* Returns the {@link com.google.android.usb.UsbAccessory} for
* a {@link #ACTION_USB_ACCESSORY_ATTACHED} or {@link #ACTION_USB_ACCESSORY_ATTACHED}
* broadcast Intent
*
* @return UsbAccessory for the broadcast.
*/
public static UsbAccessory getAccessory(Intent intent) {
android.hardware.usb.UsbAccessory accessory =
intent.getParcelableExtra(android.hardware.usb.UsbManager.EXTRA_ACCESSORY);
if (accessory == null) {
return null;
} else {
return new UsbAccessory(accessory);
}
}
/**
* Returns a list of currently attached USB accessories.
* (in the current implementation there can be at most one)
*
* @return list of USB accessories, or null if none are attached.
*/
public UsbAccessory[] getAccessoryList() {
try {
android.hardware.usb.UsbAccessory accessory = mService.getCurrentAccessory();
if (accessory == null) {
return null;
} else {
return new UsbAccessory[] { new UsbAccessory(accessory) };
}
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in getAccessoryList" , e);
return null;
}
}
/**
* Opens a file descriptor for reading and writing data to the USB accessory.
*
* @param accessory the USB accessory to open
* @return file descriptor, or null if the accessor could not be opened.
*/
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
try {
return mService.openAccessory(new android.hardware.usb.UsbAccessory(
accessory.getManufacturer(),accessory.getModel(),
accessory.getType(), accessory.getVersion()));
} catch (RemoteException e) {
Log.e(TAG, "RemoteException in openAccessory" , e);
return null;
}
}
}

View File

@ -30,7 +30,7 @@ import android.content.DialogInterface.OnCancelListener;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.hardware.UsbManager; import android.hardware.usb.UsbManager;
import android.os.Bundle; import android.os.Bundle;
import android.os.Environment; import android.os.Environment;
import android.os.Handler; import android.os.Handler;

View File

@ -38,7 +38,7 @@ import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.hardware.UsbManager; import android.hardware.usb.UsbManager;
import android.media.AudioManager; import android.media.AudioManager;
import android.net.Uri; import android.net.Uri;
import android.os.BatteryManager; import android.os.BatteryManager;

View File

@ -17,6 +17,7 @@
package com.android.server; package com.android.server;
import com.android.server.am.ActivityManagerService; import com.android.server.am.ActivityManagerService;
import com.android.server.usb.UsbService;
import com.android.internal.app.ShutdownThread; import com.android.internal.app.ShutdownThread;
import com.android.internal.os.BinderInternal; import com.android.internal.os.BinderInternal;
import com.android.internal.os.SamplingProfilerIntegration; import com.android.internal.os.SamplingProfilerIntegration;
@ -397,9 +398,10 @@ class ServerThread extends Thread {
} }
try { try {
Slog.i(TAG, "USB Observer"); Slog.i(TAG, "USB Service");
// Listen for USB changes // Listen for USB changes
usb = new UsbService(context); usb = new UsbService(context);
ServiceManager.addService(Context.USB_SERVICE, usb);
} catch (Throwable e) { } catch (Throwable e) {
Slog.e(TAG, "Failure starting UsbService", e); Slog.e(TAG, "Failure starting UsbService", e);
} }

View File

@ -1,241 +0,0 @@
/*
* Copyright (C) 2010 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 and
* limitations under the License.
*/
package com.android.server;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.hardware.UsbManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.os.UEventObserver;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.util.ArrayList;
/**
* <p>UsbService monitors for changes to USB state.
*/
class UsbService {
private static final String TAG = UsbService.class.getSimpleName();
private static final boolean LOG = false;
private static final String USB_CONNECTED_MATCH =
"DEVPATH=/devices/virtual/switch/usb_connected";
private static final String USB_CONFIGURATION_MATCH =
"DEVPATH=/devices/virtual/switch/usb_configuration";
private static final String USB_FUNCTIONS_MATCH =
"DEVPATH=/devices/virtual/usb_composite/";
private static final String USB_CONNECTED_PATH =
"/sys/class/switch/usb_connected/state";
private static final String USB_CONFIGURATION_PATH =
"/sys/class/switch/usb_configuration/state";
private static final String USB_COMPOSITE_CLASS_PATH =
"/sys/class/usb_composite";
private static final int MSG_UPDATE = 0;
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
private static final int UPDATE_DELAY = 1000;
// current connected and configuration state
private int mConnected;
private int mConfiguration;
// last broadcasted connected and configuration state
private int mLastConnected = -1;
private int mLastConfiguration = -1;
// lists of enabled and disabled USB functions
private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
private boolean mSystemReady;
private final Context mContext;
private final UEventObserver mUEventObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "USB UEVENT: " + event.toString());
}
synchronized (this) {
String name = event.get("SWITCH_NAME");
String state = event.get("SWITCH_STATE");
if (name != null && state != null) {
try {
int intState = Integer.parseInt(state);
if ("usb_connected".equals(name)) {
mConnected = intState;
// trigger an Intent broadcast
if (mSystemReady) {
// debounce disconnects
update(mConnected == 0);
}
} else if ("usb_configuration".equals(name)) {
mConfiguration = intState;
// trigger an Intent broadcast
if (mSystemReady) {
update(mConnected == 0);
}
}
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
} else {
String function = event.get("FUNCTION");
String enabledStr = event.get("ENABLED");
if (function != null && enabledStr != null) {
// Note: we do not broadcast a change when a function is enabled or disabled.
// We just record the state change for the next broadcast.
boolean enabled = "1".equals(enabledStr);
if (enabled) {
if (!mEnabledFunctions.contains(function)) {
mEnabledFunctions.add(function);
}
mDisabledFunctions.remove(function);
} else {
if (!mDisabledFunctions.contains(function)) {
mDisabledFunctions.add(function);
}
mEnabledFunctions.remove(function);
}
}
}
}
}
};
public UsbService(Context context) {
mContext = context;
init(); // set initial status
if (mConfiguration >= 0) {
mUEventObserver.startObserving(USB_CONNECTED_MATCH);
mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
}
}
private final void init() {
char[] buffer = new char[1024];
mConfiguration = -1;
try {
FileReader file = new FileReader(USB_CONNECTED_PATH);
int len = file.read(buffer, 0, 1024);
file.close();
mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
file = new FileReader(USB_CONFIGURATION_PATH);
len = file.read(buffer, 0, 1024);
file.close();
mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
} catch (FileNotFoundException e) {
Slog.i(TAG, "This kernel does not have USB configuration switch support");
} catch (Exception e) {
Slog.e(TAG, "" , e);
}
if (mConfiguration < 0)
return;
try {
File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
for (int i = 0; i < files.length; i++) {
File file = new File(files[i], "enable");
FileReader reader = new FileReader(file);
int len = reader.read(buffer, 0, 1024);
reader.close();
int value = Integer.valueOf((new String(buffer, 0, len)).trim());
String functionName = files[i].getName();
if (value == 1) {
mEnabledFunctions.add(functionName);
} else {
mDisabledFunctions.add(functionName);
}
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "This kernel does not have USB composite class support");
} catch (Exception e) {
Slog.e(TAG, "" , e);
}
}
void systemReady() {
synchronized (this) {
update(false);
mSystemReady = true;
}
}
private final void update(boolean delayed) {
mHandler.removeMessages(MSG_UPDATE);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE, delayed ? UPDATE_DELAY : 0);
}
private final Handler mHandler = new Handler() {
private void addEnabledFunctions(Intent intent) {
// include state of all USB functions in our extras
for (int i = 0; i < mEnabledFunctions.size(); i++) {
intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
}
for (int i = 0; i < mDisabledFunctions.size(); i++) {
intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
}
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MSG_UPDATE:
synchronized (this) {
if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
final ContentResolver cr = mContext.getContentResolver();
if (Settings.Secure.getInt(cr,
Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
return;
}
mLastConnected = mConnected;
mLastConfiguration = mConfiguration;
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
addEnabledFunctions(intent);
mContext.sendStickyBroadcast(intent);
}
}
break;
}
}
};
}

View File

@ -26,7 +26,7 @@ import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.hardware.UsbManager; import android.hardware.usb.UsbManager;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.InterfaceConfiguration; import android.net.InterfaceConfiguration;
import android.net.IConnectivityManager; import android.net.IConnectivityManager;

View File

@ -0,0 +1,551 @@
/*
* Copyright (C) 2011 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 and
* limitations under the License.
*/
package com.android.server.usb;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.XmlResourceParser;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Binder;
import android.os.FileUtils;
import android.os.Process;
import android.util.Log;
import android.util.SparseArray;
import android.util.Xml;
import com.android.internal.content.PackageMonitor;
import com.android.internal.util.FastXmlSerializer;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
class UsbDeviceSettingsManager {
private static final String TAG = "UsbDeviceSettingsManager";
private static final File sSettingsFile = new File("/data/system/usb_device_manager.xml");
private final Context mContext;
// maps UID to user approved USB accessories
private final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap =
new SparseArray<ArrayList<AccessoryFilter>>();
// Maps AccessoryFilter to user preferred application package
private final HashMap<AccessoryFilter, String> mAccessoryPreferenceMap =
new HashMap<AccessoryFilter, String>();
private final Object mLock = new Object();
// This class is used to describe a USB accessory.
// When used in HashMaps all values must be specified,
// but wildcards can be used for any of the fields in
// the package meta-data.
private static class AccessoryFilter {
// USB accessory manufacturer (or null for unspecified)
public final String mManufacturer;
// USB accessory model (or null for unspecified)
public final String mModel;
// USB accessory type (or null for unspecified)
public final String mType;
// USB accessory version (or null for unspecified)
public final String mVersion;
public AccessoryFilter(String manufacturer, String model, String type, String version) {
mManufacturer = manufacturer;
mModel = model;
mType = type;
mVersion = version;
}
public AccessoryFilter(UsbAccessory accessory) {
mManufacturer = accessory.getManufacturer();
mModel = accessory.getModel();
mType = accessory.getType();
mVersion = accessory.getVersion();
}
public static AccessoryFilter read(XmlPullParser parser)
throws XmlPullParserException, IOException {
String manufacturer = null;
String model = null;
String type = null;
String version = null;
int count = parser.getAttributeCount();
for (int i = 0; i < count; i++) {
String name = parser.getAttributeName(i);
String value = parser.getAttributeValue(i);
if ("manufacturer".equals(name)) {
manufacturer = value;
} else if ("model".equals(name)) {
model = value;
} else if ("type".equals(name)) {
type = value;
} else if ("version".equals(name)) {
version = value;
}
}
return new AccessoryFilter(manufacturer, model, type, version);
}
public void write(XmlSerializer serializer)throws IOException {
serializer.startTag(null, "usb-accessory");
if (mManufacturer != null) {
serializer.attribute(null, "manufacturer", mManufacturer);
}
if (mModel != null) {
serializer.attribute(null, "model", mModel);
}
if (mType != null) {
serializer.attribute(null, "type", mType);
}
if (mVersion != null) {
serializer.attribute(null, "version", mVersion);
}
serializer.endTag(null, "usb-accessory");
}
public boolean matches(UsbAccessory acc) {
if (mManufacturer != null && !acc.getManufacturer().equals(mManufacturer)) return false;
if (mModel != null && !acc.getModel().equals(mModel)) return false;
if (mType != null && !acc.getType().equals(mType)) return false;
if (mVersion != null && !acc.getVersion().equals(mVersion)) return false;
return true;
}
@Override
public boolean equals(Object obj) {
// can't compare if we have wildcard strings
if (mManufacturer == null || mModel == null || mType == null || mVersion == null) {
return false;
}
if (obj instanceof AccessoryFilter) {
AccessoryFilter filter = (AccessoryFilter)obj;
return (mManufacturer.equals(filter.mManufacturer) &&
mModel.equals(filter.mModel) &&
mType.equals(filter.mType) &&
mVersion.equals(filter.mVersion));
}
if (obj instanceof UsbAccessory) {
UsbAccessory accessory = (UsbAccessory)obj;
return (mManufacturer.equals(accessory.getManufacturer()) &&
mModel.equals(accessory.getModel()) &&
mType.equals(accessory.getType()) &&
mVersion.equals(accessory.getVersion()));
}
return false;
}
@Override
public int hashCode() {
return ((mManufacturer == null ? 0 : mManufacturer.hashCode()) ^
(mModel == null ? 0 : mModel.hashCode()) ^
(mType == null ? 0 : mType.hashCode()) ^
(mVersion == null ? 0 : mVersion.hashCode()));
}
@Override
public String toString() {
return "AccessoryFilter[mManufacturer=\"" + mManufacturer +
"\", mModel=\"" + mModel +
"\", mType=\"" + mType +
"\", mVersion=\"" + mVersion + "\"]";
}
}
private class MyPackageMonitor extends PackageMonitor {
public void onPackageRemoved(String packageName, int uid) {
synchronized (mLock) {
// clear all activity preferences for the package
if (clearPackageDefaultsLocked(packageName)) {
writeSettingsLocked();
}
}
}
public void onUidRemoved(int uid) {
synchronized (mLock) {
// clear all permissions for the UID
if (clearUidDefaultsLocked(uid)) {
writeSettingsLocked();
}
}
}
}
MyPackageMonitor mPackageMonitor = new MyPackageMonitor();
public UsbDeviceSettingsManager(Context context) {
mContext = context;
synchronized (mLock) {
readSettingsLocked();
}
mPackageMonitor.register(context, true);
}
private void readAccessoryPermission(XmlPullParser parser)
throws XmlPullParserException, IOException {
int uid = -1;
ArrayList<AccessoryFilter> filters = new ArrayList<AccessoryFilter>();
int count = parser.getAttributeCount();
for (int i = 0; i < count; i++) {
if ("uid".equals(parser.getAttributeName(i))) {
uid = Integer.parseInt(parser.getAttributeValue(i));
break;
}
}
XmlUtils.nextElement(parser);
while ("usb-accessory".equals(parser.getName())) {
filters.add(AccessoryFilter.read(parser));
XmlUtils.nextElement(parser);
}
mAccessoryPermissionMap.put(uid, filters);
}
private void readPreference(XmlPullParser parser)
throws XmlPullParserException, IOException {
String packageName = null;
int count = parser.getAttributeCount();
for (int i = 0; i < count; i++) {
if ("package".equals(parser.getAttributeName(i))) {
packageName = parser.getAttributeValue(i);
break;
}
}
XmlUtils.nextElement(parser);
if ("usb-accessory".equals(parser.getName())) {
AccessoryFilter filter = AccessoryFilter.read(parser);
mAccessoryPreferenceMap.put(filter, packageName);
}
XmlUtils.nextElement(parser);
}
private void readSettingsLocked() {
FileInputStream stream = null;
try {
stream = new FileInputStream(sSettingsFile);
XmlPullParser parser = Xml.newPullParser();
parser.setInput(stream, null);
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if ("accessory-permission".equals(tagName)) {
readAccessoryPermission(parser);
} else if ("preference".equals(tagName)) {
readPreference(parser);
} else {
XmlUtils.nextElement(parser);
}
}
} catch (FileNotFoundException e) {
Log.w(TAG, "settings file not found");
} catch (Exception e) {
Log.e(TAG, "error reading settings file, deleting to start fresh", e);
sSettingsFile.delete();
} finally {
if (stream != null) {
try {
stream.close();
} catch (IOException e) {
}
}
}
}
private void writeSettingsLocked() {
FileOutputStream fos = null;
try {
FileOutputStream fstr = new FileOutputStream(sSettingsFile);
Log.d(TAG, "writing settings to " + fstr);
BufferedOutputStream str = new BufferedOutputStream(fstr);
FastXmlSerializer serializer = new FastXmlSerializer();
serializer.setOutput(str, "utf-8");
serializer.startDocument(null, true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
serializer.startTag(null, "settings");
int count = mAccessoryPermissionMap.size();
for (int i = 0; i < count; i++) {
int uid = mAccessoryPermissionMap.keyAt(i);
ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
serializer.startTag(null, "accessory-permission");
serializer.attribute(null, "uid", Integer.toString(uid));
int filterCount = filters.size();
for (int j = 0; j < filterCount; j++) {
filters.get(j).write(serializer);
}
serializer.endTag(null, "accessory-permission");
}
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
serializer.startTag(null, "preference");
serializer.attribute(null, "package", mAccessoryPreferenceMap.get(filter));
filter.write(serializer);
serializer.endTag(null, "preference");
}
serializer.endTag(null, "settings");
serializer.endDocument();
str.flush();
FileUtils.sync(fstr);
str.close();
} catch (Exception e) {
Log.e(TAG, "error writing settings file, deleting to start fresh", e);
sSettingsFile.delete();
}
}
// Checks to see if a package matches an accessory.
private boolean packageMatchesLocked(ResolveInfo info, String metaDataName,
UsbAccessory accessory) {
ActivityInfo ai = info.activityInfo;
PackageManager pm = mContext.getPackageManager();
XmlResourceParser parser = null;
try {
parser = ai.loadXmlMetaData(pm, metaDataName);
if (parser == null) {
Log.w(TAG, "no meta-data for " + info);
return false;
}
XmlUtils.nextElement(parser);
while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
String tagName = parser.getName();
if (accessory != null && "usb-accessory".equals(tagName)) {
AccessoryFilter filter = AccessoryFilter.read(parser);
if (filter.matches(accessory)) {
return true;
}
}
XmlUtils.nextElement(parser);
}
} catch (Exception e) {
Log.w(TAG, "Unable to load component info " + info.toString(), e);
} finally {
if (parser != null) parser.close();
}
return false;
}
private final ArrayList<ResolveInfo> getAccessoryMatchesLocked(
UsbAccessory accessory, Intent intent) {
ArrayList<ResolveInfo> matches = new ArrayList<ResolveInfo>();
PackageManager pm = mContext.getPackageManager();
List<ResolveInfo> resolveInfos = pm.queryIntentActivities(intent,
PackageManager.GET_META_DATA);
int count = resolveInfos.size();
for (int i = 0; i < count; i++) {
ResolveInfo resolveInfo = resolveInfos.get(i);
if (packageMatchesLocked(resolveInfo, intent.getAction(), accessory)) {
matches.add(resolveInfo);
}
}
return matches;
}
public void accessoryAttached(UsbAccessory accessory) {
Intent accessoryIntent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED);
accessoryIntent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
accessoryIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
ArrayList<ResolveInfo> matches;
String defaultPackage;
synchronized (mLock) {
matches = getAccessoryMatchesLocked(accessory, accessoryIntent);
// Launch our default activity directly, if we have one.
// Otherwise we will start the UsbResolverActivity to allow the user to choose.
defaultPackage = mAccessoryPreferenceMap.get(new AccessoryFilter(accessory));
}
if (defaultPackage != null) {
int count = matches.size();
for (int i = 0; i < count; i++) {
ResolveInfo rInfo = matches.get(i);
if (rInfo.activityInfo != null &&
defaultPackage.equals(rInfo.activityInfo.packageName)) {
try {
accessoryIntent.setComponent(new ComponentName(
defaultPackage, rInfo.activityInfo.name));
mContext.startActivity(accessoryIntent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "startActivity failed", e);
}
return;
}
}
}
Intent intent = new Intent(mContext, UsbResolverActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.putExtra(Intent.EXTRA_INTENT, accessoryIntent);
intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches);
try {
mContext.startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.w(TAG, "unable to start UsbResolverActivity");
}
}
public void accessoryDetached(UsbAccessory accessory) {
Intent intent = new Intent(
UsbManager.ACTION_USB_ACCESSORY_DETACHED);
intent.putExtra(UsbManager.EXTRA_ACCESSORY, accessory);
mContext.sendBroadcast(intent);
}
public void checkPermission(UsbAccessory accessory) {
if (accessory == null) return;
synchronized (mLock) {
ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(Binder.getCallingUid());
if (filterList != null) {
int count = filterList.size();
for (int i = 0; i < count; i++) {
AccessoryFilter filter = filterList.get(i);
if (filter.equals(accessory)) {
// permission allowed
return;
}
}
}
}
throw new SecurityException("User has not given permission to accessory " + accessory);
}
public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
AccessoryFilter filter = new AccessoryFilter(accessory);
boolean changed = false;
synchronized (mLock) {
if (packageName == null) {
changed = (mAccessoryPreferenceMap.remove(filter) != null);
} else {
changed = !packageName.equals(mAccessoryPreferenceMap.get(filter));
if (changed) {
mAccessoryPreferenceMap.put(filter, packageName);
}
}
if (changed) {
writeSettingsLocked();
}
}
}
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
synchronized (mLock) {
ArrayList<AccessoryFilter> filterList = mAccessoryPermissionMap.get(uid);
if (filterList == null) {
filterList = new ArrayList<AccessoryFilter>();
mAccessoryPermissionMap.put(uid, filterList);
} else {
int count = filterList.size();
for (int i = 0; i < count; i++) {
if (filterList.get(i).equals(accessory)) return;
}
}
filterList.add(new AccessoryFilter(accessory));
writeSettingsLocked();
}
}
public boolean hasDefaults(String packageName, int uid) {
synchronized (mLock) {
if (mAccessoryPermissionMap.get(uid) != null) return true;
if (mAccessoryPreferenceMap.values().contains(packageName)) return true;
return false;
}
}
public void clearDefaults(String packageName, int uid) {
synchronized (mLock) {
boolean packageCleared = clearPackageDefaultsLocked(packageName);
boolean uidCleared = clearUidDefaultsLocked(uid);
if (packageCleared || uidCleared) {
writeSettingsLocked();
}
}
}
private boolean clearUidDefaultsLocked(int uid) {
boolean cleared = false;
int index = mAccessoryPermissionMap.indexOfKey(uid);
if (index >= 0) {
mAccessoryPermissionMap.removeAt(index);
cleared = true;
}
return cleared;
}
private boolean clearPackageDefaultsLocked(String packageName) {
boolean cleared = false;
synchronized (mLock) {
if (mAccessoryPreferenceMap.containsValue(packageName)) {
// make a copy of the key set to avoid ConcurrentModificationException
Object[] keys = mAccessoryPreferenceMap.keySet().toArray();
for (int i = 0; i < keys.length; i++) {
Object key = keys[i];
if (packageName.equals(mAccessoryPreferenceMap.get(key))) {
mAccessoryPreferenceMap.remove(key);
cleared = true;
}
}
}
return cleared;
}
}
public void dump(FileDescriptor fd, PrintWriter pw) {
synchronized (mLock) {
pw.println(" Accessory permissions:");
int count = mAccessoryPermissionMap.size();
for (int i = 0; i < count; i++) {
int uid = mAccessoryPermissionMap.keyAt(i);
pw.println(" " + "uid " + uid + ":");
ArrayList<AccessoryFilter> filters = mAccessoryPermissionMap.valueAt(i);
for (AccessoryFilter filter : filters) {
pw.println(" " + filter);
}
}
pw.println(" Accessory preferences:");
for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) {
pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter));
}
}
}
}

View File

@ -0,0 +1,91 @@
/*
* Copyright (C) 2011 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 and
* limitations under the License.
*/
package com.android.server.usb;
import com.android.internal.app.ResolverActivity;
import android.content.ActivityNotFoundException;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import java.util.ArrayList;
/* Activity for choosing an application for a USB device or accessory */
public class UsbResolverActivity extends ResolverActivity {
public static final String TAG = "UsbResolverActivity";
public static final String EXTRA_RESOLVE_INFOS = "rlist";
@Override
protected void onCreate(Bundle savedInstanceState) {
Intent intent = getIntent();
Parcelable targetParcelable = intent.getParcelableExtra(Intent.EXTRA_INTENT);
if (!(targetParcelable instanceof Intent)) {
Log.w("UsbResolverActivity", "Target is not an intent: " + targetParcelable);
finish();
return;
}
Intent target = (Intent)targetParcelable;
ArrayList<ResolveInfo> rList = intent.getParcelableArrayListExtra(EXTRA_RESOLVE_INFOS);
Log.d(TAG, "rList.size() " + rList.size());
CharSequence title = getResources().getText(com.android.internal.R.string.chooseUsbActivity);
super.onCreate(savedInstanceState, target, title, null, rList,
true, /* Set alwaysUseOption to true to enable "always use this app" checkbox. */
true /* Set alwaysChoose to display activity when only one choice is available.
This is necessary because this activity is needed for the user to allow
the application permission to access the device */
);
}
protected void onIntentSelected(ResolveInfo ri, Intent intent, boolean alwaysCheck) {
try {
IBinder b = ServiceManager.getService(USB_SERVICE);
IUsbManager service = IUsbManager.Stub.asInterface(b);
int uid = ri.activityInfo.applicationInfo.uid;
String action = intent.getAction();
if (UsbManager.ACTION_USB_ACCESSORY_ATTACHED.equals(action)) {
UsbAccessory accessory = (UsbAccessory)intent.getParcelableExtra(
UsbManager.EXTRA_ACCESSORY);
// grant permission for the accessory
service.grantAccessoryPermission(accessory, uid);
// set or clear default setting
if (alwaysCheck) {
service.setAccessoryPackage(accessory, ri.activityInfo.packageName);
} else {
service.setAccessoryPackage(accessory, null);
}
}
try {
startActivity(intent);
} catch (ActivityNotFoundException e) {
Log.e(TAG, "startActivity failed", e);
}
} catch (RemoteException e) {
Log.e(TAG, "onIntentSelected failed", e);
}
}
}

View File

@ -0,0 +1,429 @@
/*
* Copyright (C) 2010 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 and
* limitations under the License.
*/
package com.android.server.usb;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.hardware.usb.IUsbManager;
import android.hardware.usb.UsbAccessory;
import android.hardware.usb.UsbManager;
import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.UEventObserver;
import android.provider.Settings;
import android.util.Log;
import android.util.Slog;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
/**
* UsbService monitors for changes to USB state.
* This includes code for both USB host support (where the android device is the host)
* as well as USB device support (android device is connected to a USB host).
* Accessory mode is a special case of USB device mode, where the android device is
* connected to a USB host that supports the android accessory protocol.
*/
public class UsbService extends IUsbManager.Stub {
private static final String TAG = UsbService.class.getSimpleName();
private static final boolean LOG = false;
private static final String USB_CONNECTED_MATCH =
"DEVPATH=/devices/virtual/switch/usb_connected";
private static final String USB_CONFIGURATION_MATCH =
"DEVPATH=/devices/virtual/switch/usb_configuration";
private static final String USB_FUNCTIONS_MATCH =
"DEVPATH=/devices/virtual/usb_composite/";
private static final String USB_CONNECTED_PATH =
"/sys/class/switch/usb_connected/state";
private static final String USB_CONFIGURATION_PATH =
"/sys/class/switch/usb_configuration/state";
private static final String USB_COMPOSITE_CLASS_PATH =
"/sys/class/usb_composite";
private static final int MSG_UPDATE_STATE = 0;
private static final int MSG_FUNCTION_ENABLED = 1;
private static final int MSG_FUNCTION_DISABLED = 2;
// Delay for debouncing USB disconnects.
// We often get rapid connect/disconnect events when enabling USB functions,
// which need debouncing.
private static final int UPDATE_DELAY = 1000;
// current connected and configuration state
private int mConnected;
private int mConfiguration;
// last broadcasted connected and configuration state
private int mLastConnected = -1;
private int mLastConfiguration = -1;
// lists of enabled and disabled USB functions (for USB device mode)
private final ArrayList<String> mEnabledFunctions = new ArrayList<String>();
private final ArrayList<String> mDisabledFunctions = new ArrayList<String>();
private boolean mSystemReady;
private UsbAccessory mCurrentAccessory;
// functions to restore after exiting accessory mode
private final ArrayList<String> mAccessoryRestoreFunctions = new ArrayList<String>();
private final Context mContext;
private final Object mLock = new Object();
private final UsbDeviceSettingsManager mDeviceManager;
private final boolean mHasUsbAccessory;
/*
* Handles USB function enable/disable events (device mode)
*/
private final void functionEnabledLocked(String function, boolean enabled) {
boolean enteringAccessoryMode =
(mHasUsbAccessory && enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function));
if (enteringAccessoryMode) {
// keep a list of functions to reenable after exiting accessory mode
mAccessoryRestoreFunctions.clear();
int count = mEnabledFunctions.size();
for (int i = 0; i < count; i++) {
String f = mEnabledFunctions.get(i);
// RNDIS should not be restored and adb is handled automatically
if (!UsbManager.USB_FUNCTION_RNDIS.equals(f) &&
!UsbManager.USB_FUNCTION_ADB.equals(f) &&
!UsbManager.USB_FUNCTION_ACCESSORY.equals(f)) {
mAccessoryRestoreFunctions.add(f);
}
}
}
if (enabled) {
if (!mEnabledFunctions.contains(function)) {
mEnabledFunctions.add(function);
}
mDisabledFunctions.remove(function);
} else {
if (!mDisabledFunctions.contains(function)) {
mDisabledFunctions.add(function);
}
mEnabledFunctions.remove(function);
}
if (enteringAccessoryMode) {
String[] strings = nativeGetAccessoryStrings();
if (strings != null) {
mCurrentAccessory = new UsbAccessory(strings);
Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory);
mDeviceManager.accessoryAttached(mCurrentAccessory);
} else {
Log.e(TAG, "nativeGetAccessoryStrings failed");
}
}
}
/*
* Listens for uevent messages from the kernel to monitor the USB state (device mode)
*/
private final UEventObserver mUEventObserver = new UEventObserver() {
@Override
public void onUEvent(UEventObserver.UEvent event) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.v(TAG, "USB UEVENT: " + event.toString());
}
synchronized (mLock) {
String name = event.get("SWITCH_NAME");
String state = event.get("SWITCH_STATE");
if (name != null && state != null) {
try {
int intState = Integer.parseInt(state);
if ("usb_connected".equals(name)) {
mConnected = intState;
// trigger an Intent broadcast
if (mSystemReady) {
// debounce disconnects to avoid problems bringing up USB tethering
update(mConnected == 0);
}
} else if ("usb_configuration".equals(name)) {
mConfiguration = intState;
// trigger an Intent broadcast
if (mSystemReady) {
update(mConnected == 0);
}
}
} catch (NumberFormatException e) {
Slog.e(TAG, "Could not parse switch state from event " + event);
}
} else {
String function = event.get("FUNCTION");
String enabledStr = event.get("ENABLED");
if (function != null && enabledStr != null) {
// Note: we do not broadcast a change when a function is enabled or disabled.
// We just record the state change for the next broadcast.
int what = ("1".equals(enabledStr) ?
MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED);
Message msg = Message.obtain(mHandler, what);
msg.obj = function;
mHandler.sendMessage(msg);
}
}
}
}
};
public UsbService(Context context) {
mContext = context;
mDeviceManager = new UsbDeviceSettingsManager(context);
PackageManager pm = mContext.getPackageManager();
mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY);
init(); // set initial status
if (mConfiguration >= 0) {
mUEventObserver.startObserving(USB_CONNECTED_MATCH);
mUEventObserver.startObserving(USB_CONFIGURATION_MATCH);
mUEventObserver.startObserving(USB_FUNCTIONS_MATCH);
}
}
private final void init() {
char[] buffer = new char[1024];
// Read initial USB state (device mode)
mConfiguration = -1;
try {
FileReader file = new FileReader(USB_CONNECTED_PATH);
int len = file.read(buffer, 0, 1024);
file.close();
mConnected = Integer.valueOf((new String(buffer, 0, len)).trim());
file = new FileReader(USB_CONFIGURATION_PATH);
len = file.read(buffer, 0, 1024);
file.close();
mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim());
} catch (FileNotFoundException e) {
Slog.i(TAG, "This kernel does not have USB configuration switch support");
} catch (Exception e) {
Slog.e(TAG, "" , e);
}
if (mConfiguration < 0)
return;
// Read initial list of enabled and disabled functions (device mode)
try {
File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles();
for (int i = 0; i < files.length; i++) {
File file = new File(files[i], "enable");
FileReader reader = new FileReader(file);
int len = reader.read(buffer, 0, 1024);
reader.close();
int value = Integer.valueOf((new String(buffer, 0, len)).trim());
String functionName = files[i].getName();
if (value == 1) {
mEnabledFunctions.add(functionName);
} else {
mDisabledFunctions.add(functionName);
}
}
} catch (FileNotFoundException e) {
Slog.w(TAG, "This kernel does not have USB composite class support");
} catch (Exception e) {
Slog.e(TAG, "" , e);
}
}
public void systemReady() {
synchronized (mLock) {
update(false);
mSystemReady = true;
}
}
/*
* Sends a message to update the USB connected and configured state (device mode).
* If delayed is true, then we add a small delay in sending the message to debounce
* the USB connection when enabling USB tethering.
*/
private final void update(boolean delayed) {
mHandler.removeMessages(MSG_UPDATE_STATE);
mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0);
}
/* returns the currently attached USB accessory (device mode) */
public UsbAccessory getCurrentAccessory() {
synchronized (mLock) {
mDeviceManager.checkPermission(mCurrentAccessory);
return mCurrentAccessory;
}
}
/* opens the currently attached USB accessory (device mode) */
public ParcelFileDescriptor openAccessory(UsbAccessory accessory) {
synchronized (mLock) {
if (mCurrentAccessory == null) {
throw new IllegalArgumentException("no accessory attached");
}
if (!mCurrentAccessory.equals(accessory)) {
Log.e(TAG, accessory.toString() + " does not match current accessory "
+ mCurrentAccessory);
throw new IllegalArgumentException("accessory not attached");
}
mDeviceManager.checkPermission(mCurrentAccessory);
return nativeOpenAccessory();
}
}
public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.setAccessoryPackage(accessory, packageName);
}
public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.grantAccessoryPermission(accessory, uid);
}
public boolean hasDefaults(String packageName, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
return mDeviceManager.hasDefaults(packageName, uid);
}
public void clearDefaults(String packageName, int uid) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
mDeviceManager.clearDefaults(packageName, uid);
}
/*
* This handler is for deferred handling of events related to device mode and accessories.
*/
private final Handler mHandler = new Handler() {
private void addEnabledFunctionsLocked(Intent intent) {
// include state of all USB functions in our extras
for (int i = 0; i < mEnabledFunctions.size(); i++) {
intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED);
}
for (int i = 0; i < mDisabledFunctions.size(); i++) {
intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED);
}
}
@Override
public void handleMessage(Message msg) {
synchronized (mLock) {
switch (msg.what) {
case MSG_UPDATE_STATE:
if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) {
if (mConnected == 0 && mCurrentAccessory != null) {
// turn off accessory mode when we are disconnected
if (UsbManager.setFunctionEnabled(
UsbManager.USB_FUNCTION_ACCESSORY, false)) {
Log.d(TAG, "exited USB accessory mode");
// restore previously enabled functions
for (String function : mAccessoryRestoreFunctions) {
if (UsbManager.setFunctionEnabled(function, true)) {
Log.e(TAG, "could not reenable function " + function);
}
}
mAccessoryRestoreFunctions.clear();
mDeviceManager.accessoryDetached(mCurrentAccessory);
mCurrentAccessory = null;
// this will cause an immediate reset of the USB bus,
// so there is no point in sending the
// function disabled broadcast.
return;
} else {
Log.e(TAG, "could not disable USB_FUNCTION_ACCESSORY");
}
}
final ContentResolver cr = mContext.getContentResolver();
if (Settings.Secure.getInt(cr,
Settings.Secure.DEVICE_PROVISIONED, 0) == 0) {
Slog.i(TAG, "Device not provisioned, skipping USB broadcast");
return;
}
mLastConnected = mConnected;
mLastConfiguration = mConfiguration;
// send a sticky broadcast containing current USB state
Intent intent = new Intent(UsbManager.ACTION_USB_STATE);
intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING);
intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0);
intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration);
addEnabledFunctionsLocked(intent);
mContext.sendStickyBroadcast(intent);
}
break;
case MSG_FUNCTION_ENABLED:
case MSG_FUNCTION_DISABLED:
functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED);
break;
}
}
}
};
@Override
public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
pw.println("Permission Denial: can't dump UsbManager from from pid="
+ Binder.getCallingPid()
+ ", uid=" + Binder.getCallingUid());
return;
}
synchronized (mLock) {
pw.println("USB Manager State:");
pw.println(" USB Device State:");
pw.print(" Enabled Functions: ");
for (int i = 0; i < mEnabledFunctions.size(); i++) {
pw.print(mEnabledFunctions.get(i) + " ");
}
pw.println("");
pw.print(" Disabled Functions: ");
for (int i = 0; i < mDisabledFunctions.size(); i++) {
pw.print(mDisabledFunctions.get(i) + " ");
}
pw.println("");
pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration);
pw.println(" mCurrentAccessory: " + mCurrentAccessory);
mDeviceManager.dump(fd, pw);
}
}
// accessory support
private native String[] nativeGetAccessoryStrings();
private native ParcelFileDescriptor nativeOpenAccessory();
}

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2009 The Android Open Source Project * Copyright (C) 2010 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -23,14 +23,119 @@
#include "utils/Vector.h" #include "utils/Vector.h"
#include <stdio.h> #include <stdio.h>
#include <asm/byteorder.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/usb/f_accessory.h>
#define DRIVER_NAME "/dev/usb_accessory"
namespace android namespace android
{ {
static struct file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
jfieldID mDescriptor;
} gFileDescriptorOffsets;
static struct parcel_file_descriptor_offsets_t
{
jclass mClass;
jmethodID mConstructor;
} gParcelFileDescriptorOffsets;
static void checkAndClearExceptionFromCallback(JNIEnv* env, const char* methodName) {
if (env->ExceptionCheck()) {
LOGE("An exception was thrown by callback '%s'.", methodName);
LOGE_EX(env);
env->ExceptionClear();
}
}
static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strArray, int index)
{
char buffer[256];
buffer[0] = 0;
int length = ioctl(fd, cmd, buffer);
if (buffer[0]) {
LOGD("string %d: %s", index, buffer);
jstring obj = env->NewStringUTF(buffer);
env->SetObjectArrayElement(strArray, index, obj);
env->DeleteLocalRef(obj);
}
}
static jobjectArray android_server_UsbService_getAccessoryStrings(JNIEnv *env, jobject thiz)
{
LOGD("getAccessoryStrings");
int fd = open(DRIVER_NAME, O_RDWR);
if (fd < 0) {
LOGE("could not open %s", DRIVER_NAME);
return NULL;
}
jclass stringClass = env->FindClass("java/lang/String");
jobjectArray strArray = env->NewObjectArray(4, stringClass, NULL);
if (!strArray) goto out;
set_accessory_string(env, fd, ACCESSORY_GET_STRING_MANUFACTURER, strArray, 0);
set_accessory_string(env, fd, ACCESSORY_GET_STRING_MODEL, strArray, 1);
set_accessory_string(env, fd, ACCESSORY_GET_STRING_TYPE, strArray, 2);
set_accessory_string(env, fd, ACCESSORY_GET_STRING_VERSION, strArray, 3);
out:
close(fd);
return strArray;
}
static jobject android_server_UsbService_openAccessory(JNIEnv *env, jobject thiz)
{
int fd = open(DRIVER_NAME, O_RDWR);
if (fd < 0) {
LOGE("could not open %s", DRIVER_NAME);
return NULL;
}
jobject fileDescriptor = env->NewObject(gFileDescriptorOffsets.mClass,
gFileDescriptorOffsets.mConstructor);
if (fileDescriptor != NULL) {
env->SetIntField(fileDescriptor, gFileDescriptorOffsets.mDescriptor, fd);
} else {
return NULL;
}
return env->NewObject(gParcelFileDescriptorOffsets.mClass,
gParcelFileDescriptorOffsets.mConstructor, fileDescriptor);
}
static JNINativeMethod method_table[] = {
{ "nativeGetAccessoryStrings", "()[Ljava/lang/String;",
(void*)android_server_UsbService_getAccessoryStrings },
{ "nativeOpenAccessory","()Landroid/os/ParcelFileDescriptor;",
(void*)android_server_UsbService_openAccessory },
};
int register_android_server_UsbService(JNIEnv *env) int register_android_server_UsbService(JNIEnv *env)
{ {
jclass clazz = env->FindClass("java/io/FileDescriptor");
LOG_FATAL_IF(clazz == NULL, "Unable to find class java.io.FileDescriptor");
gFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "()V");
gFileDescriptorOffsets.mDescriptor = env->GetFieldID(clazz, "descriptor", "I");
LOG_FATAL_IF(gFileDescriptorOffsets.mDescriptor == NULL,
"Unable to find descriptor field in java.io.FileDescriptor");
return 0; clazz = env->FindClass("android/os/ParcelFileDescriptor");
LOG_FATAL_IF(clazz == NULL, "Unable to find class android.os.ParcelFileDescriptor");
gParcelFileDescriptorOffsets.mClass = (jclass) env->NewGlobalRef(clazz);
gParcelFileDescriptorOffsets.mConstructor = env->GetMethodID(clazz, "<init>", "(Ljava/io/FileDescriptor;)V");
LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL,
"Unable to find constructor for android.os.ParcelFileDescriptor");
return jniRegisterNativeMethods(env, "com/android/server/usb/UsbService",
method_table, NELEM(method_table));
} }
}; };