diff options
Diffstat (limited to 'services')
17 files changed, 1221 insertions, 98 deletions
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 704da72..a07ebfc 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -1629,6 +1629,7 @@ uint32_t AudioFlinger::MixerThread::prepareTracks_l(const SortedVector< wp<Track track->mState = TrackBase::ACTIVE; param = AudioMixer::RAMP_VOLUME; } + mAudioMixer->setParameter(AudioMixer::RESAMPLE, AudioMixer::RESET, NULL); } else if (cblk->server != 0) { // If the track is stopped before the first frame was mixed, // do not apply ramp @@ -3855,9 +3856,12 @@ status_t AudioFlinger::RecordThread::start(RecordThread::RecordTrack* recordTrac mActiveTrack.clear(); return status; } - mActiveTrack->mState = TrackBase::RESUMING; mRsmpInIndex = mFrameCount; mBytesRead = 0; + if (mResampler != NULL) { + mResampler->reset(); + } + mActiveTrack->mState = TrackBase::RESUMING; // signal thread to start LOGV("Signal record thread"); mWaitWorkCV.signal(); diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index 433f1f7..50dcda7 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -220,6 +220,12 @@ status_t AudioMixer::setParameter(int target, int name, void *value) return NO_ERROR; } } + if (name == RESET) { + track_t& track = mState.tracks[ mActiveTrack ]; + track.resetResampler(); + invalidateState(1<<mActiveTrack); + return NO_ERROR; + } break; case RAMP_VOLUME: case VOLUME: @@ -289,6 +295,13 @@ bool AudioMixer::track_t::doesResample() const return resampler != 0; } +void AudioMixer::track_t::resetResampler() +{ + if (resampler != 0) { + resampler->reset(); + } +} + inline void AudioMixer::track_t::adjustVolumeRamp(bool aux) { diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index aee3e17..88408a7 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -67,6 +67,7 @@ public: AUX_BUFFER = 0x4003, // for TARGET RESAMPLE SAMPLE_RATE = 0x4100, + RESET = 0x4101, // for TARGET VOLUME (8 channels max) VOLUME0 = 0x4200, VOLUME1 = 0x4201, @@ -163,6 +164,7 @@ private: bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); bool doesResample() const; + void resetResampler(); void adjustVolumeRamp(bool aux); }; diff --git a/services/audioflinger/AudioPolicyManagerBase.cpp b/services/audioflinger/AudioPolicyManagerBase.cpp index 3082d45..bfc80db 100644 --- a/services/audioflinger/AudioPolicyManagerBase.cpp +++ b/services/audioflinger/AudioPolicyManagerBase.cpp @@ -1889,7 +1889,15 @@ void AudioPolicyManagerBase::initializeVolumeCurves() { mStreams[i].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f; } - // TODO add modifications for music to have finer steps below knee1 and above knee2 + // Modification for music: more attenuation for lower volumes, finer steps at high volumes + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLMIN] = 1; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLMIN] = -58.0f; + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLKNEE1] = 20; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLKNEE1] = -40.0f; + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLKNEE2] = 60; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLKNEE2] = -17.0f; + mStreams[AudioSystem::MUSIC].mVolIndex[StreamDescriptor::VOLMAX] = 100; + mStreams[AudioSystem::MUSIC].mVolDbAtt[StreamDescriptor::VOLMAX] = 0.0f; } float AudioPolicyManagerBase::computeVolume(int stream, int index, audio_io_handle_t output, uint32_t device) diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index 5dabacb..5c3b43fc 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -148,6 +148,12 @@ void AudioResampler::setVolume(int16_t left, int16_t right) { mVolume[1] = right; } +void AudioResampler::reset() { + mInputIndex = 0; + mPhaseFraction = 0; + mBuffer.frameCount = 0; +} + // ---------------------------------------------------------------------------- void AudioResamplerOrder1::resample(int32_t* out, size_t outFrameCount, diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index 2dfac76..9f06c1c 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -53,6 +53,8 @@ public: virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider) = 0; + virtual void reset(); + protected: // number of bits for phase fraction - 30 bits allows nearly 2x downsampling static const int kNumPhaseBits = 30; diff --git a/services/java/com/android/server/AppWidgetService.java b/services/java/com/android/server/AppWidgetService.java index afa8d69..f28e2b1 100644 --- a/services/java/com/android/server/AppWidgetService.java +++ b/services/java/com/android/server/AppWidgetService.java @@ -1029,7 +1029,7 @@ class AppWidgetService extends IAppWidgetService.Stub com.android.internal.R.styleable.AppWidgetProviderInfo_previewImage, 0); info.autoAdvanceViewId = sa.getResourceId( com.android.internal.R.styleable.AppWidgetProviderInfo_autoAdvanceViewId, -1); - info.resizableMode = sa.getInt( + info.resizeMode = sa.getInt( com.android.internal.R.styleable.AppWidgetProviderInfo_resizeMode, AppWidgetProviderInfo.RESIZE_NONE); diff --git a/services/java/com/android/server/NotificationManagerService.java b/services/java/com/android/server/NotificationManagerService.java index 47dce41..e738145 100755 --- a/services/java/com/android/server/NotificationManagerService.java +++ b/services/java/com/android/server/NotificationManagerService.java @@ -37,7 +37,7 @@ import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.database.ContentObserver; -import android.hardware.UsbManager; +import android.hardware.usb.UsbManager; import android.media.AudioManager; import android.net.Uri; import android.os.Binder; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 52c47e1..d160963 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -17,6 +17,7 @@ package com.android.server; import com.android.server.am.ActivityManagerService; +import com.android.server.usb.UsbService; import com.android.server.wm.WindowManagerService; import com.android.internal.app.ShutdownThread; import com.android.internal.os.BinderInternal; diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java index f9b94a3..adc49ae 100644 --- a/services/java/com/android/server/WifiService.java +++ b/services/java/com/android/server/WifiService.java @@ -144,9 +144,6 @@ public class WifiService extends IWifiManager.Stub { private static final String ACTION_DEVICE_IDLE = "com.android.server.WifiManager.action.DEVICE_IDLE"; - private static final int CMD_ENABLE_TRAFFIC_STATS_POLL = 1; - private static final int CMD_TRAFFIC_STATS_POLL = 2; - private boolean mIsReceiverRegistered = false; @@ -237,24 +234,45 @@ public class WifiService extends IWifiManager.Stub { ac.connect(mContext, this, msg.replyTo); break; } - case CMD_ENABLE_TRAFFIC_STATS_POLL: { + case WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL: { mEnableTrafficStatsPoll = (msg.arg1 == 1); mTrafficStatsPollToken++; if (mEnableTrafficStatsPoll) { notifyOnDataActivity(); - sendMessageDelayed(Message.obtain(this, CMD_TRAFFIC_STATS_POLL, + sendMessageDelayed(Message.obtain(this, WifiManager.CMD_TRAFFIC_STATS_POLL, mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); } break; } - case CMD_TRAFFIC_STATS_POLL: { + case WifiManager.CMD_TRAFFIC_STATS_POLL: { if (msg.arg1 == mTrafficStatsPollToken) { notifyOnDataActivity(); - sendMessageDelayed(Message.obtain(this, CMD_TRAFFIC_STATS_POLL, + sendMessageDelayed(Message.obtain(this, WifiManager.CMD_TRAFFIC_STATS_POLL, mTrafficStatsPollToken, 0), POLL_TRAFFIC_STATS_INTERVAL_MSECS); } break; } + case WifiManager.CMD_CONNECT_NETWORK: { + if (msg.obj != null) { + mWifiStateMachine.connectNetwork((WifiConfiguration)msg.obj); + } else { + mWifiStateMachine.connectNetwork(msg.arg1); + } + break; + } + case WifiManager.CMD_SAVE_NETWORK: { + mWifiStateMachine.saveNetwork((WifiConfiguration)msg.obj); + break; + } + case WifiManager.CMD_FORGET_NETWORK: { + mWifiStateMachine.forgetNetwork(msg.arg1); + break; + } + case WifiManager.CMD_START_WPS: { + //replyTo has the original source + mWifiStateMachine.startWps(msg.replyTo, (WpsConfiguration)msg.obj); + break; + } default: { Slog.d(TAG, "WifiServicehandler.handleMessage ignoring msg=" + msg); break; @@ -844,42 +862,19 @@ public class WifiService extends IWifiManager.Stub { mWifiStateMachine.clearBlacklist(); } - public void connectNetworkWithId(int networkId) { - enforceChangePermission(); - mWifiStateMachine.connectNetwork(networkId); - } - - public void connectNetworkWithConfig(WifiConfiguration config) { - enforceChangePermission(); - mWifiStateMachine.connectNetwork(config); - } - - public void saveNetwork(WifiConfiguration config) { - enforceChangePermission(); - mWifiStateMachine.saveNetwork(config); - } - public void forgetNetwork(int netId) { - enforceChangePermission(); - mWifiStateMachine.forgetNetwork(netId); - } - - public WpsResult startWps(WpsConfiguration config) { - enforceChangePermission(); - if (mWifiStateMachineChannel != null) { - return mWifiStateMachine.startWps(mWifiStateMachineChannel, config); - } else { - Slog.e(TAG, "mWifiStateMachineChannel is not initialized"); - return new WpsResult(WpsResult.Status.FAILURE); - } - } /** * Get a reference to handler. This is used by a client to establish * an AsyncChannel communication with WifiService */ public Messenger getMessenger() { + /* Enforce the highest permissions + TODO: when we consider exposing the asynchronous API, think about + how to provide both access and change permissions seperately + */ enforceAccessPermission(); + enforceChangePermission(); return new Messenger(mAsyncServiceHandler); } @@ -1530,9 +1525,11 @@ public class WifiService extends IWifiManager.Stub { private void evaluateTrafficStatsPolling() { Message msg; if (mNetworkInfo.getDetailedState() == DetailedState.CONNECTED && !mScreenOff) { - msg = Message.obtain(mAsyncServiceHandler, CMD_ENABLE_TRAFFIC_STATS_POLL, 1, 0); + msg = Message.obtain(mAsyncServiceHandler, + WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL, 1, 0); } else { - msg = Message.obtain(mAsyncServiceHandler, CMD_ENABLE_TRAFFIC_STATS_POLL, 0, 0); + msg = Message.obtain(mAsyncServiceHandler, + WifiManager.CMD_ENABLE_TRAFFIC_STATS_POLL, 0, 0); } msg.sendToTarget(); } diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 8d30868..ea49661 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -9114,15 +9114,17 @@ public final class ActivityManagerService extends ActivityManagerNative ServiceRecord.StartItem si = r.pendingStarts.remove(0); if (DEBUG_SERVICE) Slog.v(TAG, "Sending arguments to: " + r + " " + r.intent + " args=" + si.intent); - if (si.intent == null) { - // If somehow we got a dummy start at the front, then - // just drop it here. + if (si.intent == null && N > 1) { + // If somehow we got a dummy null intent in the middle, + // then skip it. DO NOT skip a null intent when it is + // the only one in the list -- this is to support the + // onStartCommand(null) case. continue; } si.deliveredTime = SystemClock.uptimeMillis(); r.deliveredStarts.add(si); si.deliveryCount++; - if (si.targetPermissionUid >= 0) { + if (si.targetPermissionUid >= 0 && si.intent != null) { grantUriPermissionUncheckedFromIntentLocked(si.targetPermissionUid, r.packageName, si.intent, si.getUriPermissionsLocked()); } diff --git a/services/java/com/android/server/connectivity/Tethering.java b/services/java/com/android/server/connectivity/Tethering.java index f24f96c..1eb5141 100644 --- a/services/java/com/android/server/connectivity/Tethering.java +++ b/services/java/com/android/server/connectivity/Tethering.java @@ -26,7 +26,7 @@ import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; -import android.hardware.UsbManager; +import android.hardware.usb.UsbManager; import android.net.ConnectivityManager; import android.net.InterfaceConfiguration; import android.net.IConnectivityManager; diff --git a/services/java/com/android/server/usb/UsbDeviceSettingsManager.java b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java new file mode 100644 index 0000000..9a96e7f --- /dev/null +++ b/services/java/com/android/server/usb/UsbDeviceSettingsManager.java @@ -0,0 +1,908 @@ +/* + * 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.UsbDevice; +import android.hardware.usb.UsbInterface; +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 devices + private final SparseArray<ArrayList<DeviceFilter>> mDevicePermissionMap = + new SparseArray<ArrayList<DeviceFilter>>(); + // maps UID to user approved USB accessories + private final SparseArray<ArrayList<AccessoryFilter>> mAccessoryPermissionMap = + new SparseArray<ArrayList<AccessoryFilter>>(); + // Maps DeviceFilter to user preferred application package + private final HashMap<DeviceFilter, String> mDevicePreferenceMap = + new HashMap<DeviceFilter, String>(); + // Maps DeviceFilter 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 device. + // 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 DeviceFilter { + // USB Vendor ID (or -1 for unspecified) + public final int mVendorId; + // USB Product ID (or -1 for unspecified) + public final int mProductId; + // USB device or interface class (or -1 for unspecified) + public final int mClass; + // USB device subclass (or -1 for unspecified) + public final int mSubclass; + // USB device protocol (or -1 for unspecified) + public final int mProtocol; + + public DeviceFilter(int vid, int pid, int clasz, int subclass, int protocol) { + mVendorId = vid; + mProductId = pid; + mClass = clasz; + mSubclass = subclass; + mProtocol = protocol; + } + + public DeviceFilter(UsbDevice device) { + mVendorId = device.getVendorId(); + mProductId = device.getProductId(); + mClass = device.getDeviceClass(); + mSubclass = device.getDeviceSubclass(); + mProtocol = device.getDeviceProtocol(); + } + + public static DeviceFilter read(XmlPullParser parser) + throws XmlPullParserException, IOException { + int vendorId = -1; + int productId = -1; + int deviceClass = -1; + int deviceSubclass = -1; + int deviceProtocol = -1; + + int count = parser.getAttributeCount(); + for (int i = 0; i < count; i++) { + String name = parser.getAttributeName(i); + // All attribute values are ints + int value = Integer.parseInt(parser.getAttributeValue(i)); + + if ("vendor-id".equals(name)) { + vendorId = value; + } else if ("product-id".equals(name)) { + productId = value; + } else if ("class".equals(name)) { + deviceClass = value; + } else if ("subclass".equals(name)) { + deviceSubclass = value; + } else if ("protocol".equals(name)) { + deviceProtocol = value; + } + } + return new DeviceFilter(vendorId, productId, + deviceClass, deviceSubclass, deviceProtocol); + } + + public void write(XmlSerializer serializer) throws IOException { + serializer.startTag(null, "usb-device"); + if (mVendorId != -1) { + serializer.attribute(null, "vendor-id", Integer.toString(mVendorId)); + } + if (mProductId != -1) { + serializer.attribute(null, "product-id", Integer.toString(mProductId)); + } + if (mClass != -1) { + serializer.attribute(null, "class", Integer.toString(mClass)); + } + if (mSubclass != -1) { + serializer.attribute(null, "subclass", Integer.toString(mSubclass)); + } + if (mProtocol != -1) { + serializer.attribute(null, "protocol", Integer.toString(mProtocol)); + } + serializer.endTag(null, "usb-device"); + } + + private boolean matches(int clasz, int subclass, int protocol) { + return ((mClass == -1 || clasz == mClass) && + (mSubclass == -1 || subclass == mSubclass) && + (mProtocol == -1 || protocol == mProtocol)); + } + + public boolean matches(UsbDevice device) { + if (mVendorId != -1 && device.getVendorId() != mVendorId) return false; + if (mProductId != -1 && device.getProductId() != mProductId) return false; + + // check device class/subclass/protocol + if (matches(device.getDeviceClass(), device.getDeviceSubclass(), + device.getDeviceProtocol())) return true; + + // if device doesn't match, check the interfaces + int count = device.getInterfaceCount(); + for (int i = 0; i < count; i++) { + UsbInterface intf = device.getInterface(i); + if (matches(intf.getInterfaceClass(), intf.getInterfaceSubclass(), + intf.getInterfaceProtocol())) return true; + } + + return false; + } + + @Override + public boolean equals(Object obj) { + // can't compare if we have wildcard strings + if (mVendorId == -1 || mProductId == -1 || + mClass == -1 || mSubclass == -1 || mProtocol == -1) { + return false; + } + if (obj instanceof DeviceFilter) { + DeviceFilter filter = (DeviceFilter)obj; + return (filter.mVendorId == mVendorId && + filter.mProductId == mProductId && + filter.mClass == mClass && + filter.mSubclass == mSubclass && + filter.mProtocol == mProtocol); + } + if (obj instanceof UsbDevice) { + UsbDevice device = (UsbDevice)obj; + return (device.getVendorId() == mVendorId && + device.getProductId() == mProductId && + device.getDeviceClass() == mClass && + device.getDeviceSubclass() == mSubclass && + device.getDeviceProtocol() == mProtocol); + } + return false; + } + + @Override + public int hashCode() { + return (((mVendorId << 16) | mProductId) ^ + ((mClass << 16) | (mSubclass << 8) | mProtocol)); + } + + @Override + public String toString() { + return "DeviceFilter[mVendorId=" + mVendorId + ",mProductId=" + mProductId + + ",mClass=" + mClass + ",mSubclass=" + mSubclass + + ",mProtocol=" + mProtocol + "]"; + } + } + + // 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 readDevicePermission(XmlPullParser parser) + throws XmlPullParserException, IOException { + int uid = -1; + ArrayList<DeviceFilter> filters = new ArrayList<DeviceFilter>(); + 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-device".equals(parser.getName())) { + filters.add(DeviceFilter.read(parser)); + XmlUtils.nextElement(parser); + } + mDevicePermissionMap.put(uid, filters); + } + + 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-device".equals(parser.getName())) { + DeviceFilter filter = DeviceFilter.read(parser); + mDevicePreferenceMap.put(filter, packageName); + } else 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 ("device-permission".equals(tagName)) { + readDevicePermission(parser); + } else 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 = mDevicePermissionMap.size(); + for (int i = 0; i < count; i++) { + int uid = mDevicePermissionMap.keyAt(i); + ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i); + serializer.startTag(null, "device-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, "device-permission"); + } + + 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 (DeviceFilter filter : mDevicePreferenceMap.keySet()) { + serializer.startTag(null, "preference"); + serializer.attribute(null, "package", mDevicePreferenceMap.get(filter)); + filter.write(serializer); + serializer.endTag(null, "preference"); + } + + 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 a device or accessory. + // Only one of device and accessory should be non-null. + private boolean packageMatchesLocked(ResolveInfo info, String metaDataName, + UsbDevice device, 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 (device != null && "usb-device".equals(tagName)) { + DeviceFilter filter = DeviceFilter.read(parser); + if (filter.matches(device)) { + return true; + } + } + else 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> getDeviceMatchesLocked(UsbDevice device, 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(), device, null)) { + matches.add(resolveInfo); + } + } + return matches; + } + + 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(), null, accessory)) { + matches.add(resolveInfo); + } + } + return matches; + } + + public void deviceAttached(UsbDevice device) { + Intent deviceIntent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); + deviceIntent.putExtra(UsbManager.EXTRA_DEVICE, device); + deviceIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + + ArrayList<ResolveInfo> matches; + String defaultPackage; + synchronized (mLock) { + matches = getDeviceMatchesLocked(device, deviceIntent); + // Launch our default activity directly, if we have one. + // Otherwise we will start the UsbResolverActivity to allow the user to choose. + defaultPackage = mDevicePreferenceMap.get(new DeviceFilter(device)); + } + + 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 { + deviceIntent.setComponent(new ComponentName( + defaultPackage, rInfo.activityInfo.name)); + mContext.startActivity(deviceIntent); + } 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, deviceIntent); + intent.putParcelableArrayListExtra(UsbResolverActivity.EXTRA_RESOLVE_INFOS, matches); + try { + mContext.startActivity(intent); + } catch (ActivityNotFoundException e) { + Log.w(TAG, "unable to start UsbResolverActivity"); + } + } + + public void deviceDetached(UsbDevice device) { + Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); + intent.putExtra(UsbManager.EXTRA_DEVICE, device); + Log.d(TAG, "usbDeviceRemoved, sending " + intent); + mContext.sendBroadcast(intent); + } + + 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(UsbDevice device) { + if (device == null) return; + synchronized (mLock) { + ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(Binder.getCallingUid()); + if (filterList != null) { + int count = filterList.size(); + for (int i = 0; i < count; i++) { + DeviceFilter filter = filterList.get(i); + if (filter.equals(device)) { + // permission allowed + return; + } + } + } + } + throw new SecurityException("User has not given permission to device " + device); + } + + 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 setDevicePackage(UsbDevice device, String packageName) { + DeviceFilter filter = new DeviceFilter(device); + boolean changed = false; + synchronized (mLock) { + if (packageName == null) { + changed = (mDevicePreferenceMap.remove(filter) != null); + } else { + changed = !packageName.equals(mDevicePreferenceMap.get(filter)); + if (changed) { + mDevicePreferenceMap.put(filter, packageName); + } + } + if (changed) { + writeSettingsLocked(); + } + } + } + + 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 grantDevicePermission(UsbDevice device, int uid) { + synchronized (mLock) { + ArrayList<DeviceFilter> filterList = mDevicePermissionMap.get(uid); + if (filterList == null) { + filterList = new ArrayList<DeviceFilter>(); + mDevicePermissionMap.put(uid, filterList); + } else { + int count = filterList.size(); + for (int i = 0; i < count; i++) { + if (filterList.get(i).equals(device)) return; + } + } + filterList.add(new DeviceFilter(device)); + 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 (mDevicePermissionMap.get(uid) != null) return true; + if (mAccessoryPermissionMap.get(uid) != null) return true; + if (mDevicePreferenceMap.values().contains(packageName)) 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 = mDevicePermissionMap.indexOfKey(uid); + if (index >= 0) { + mDevicePermissionMap.removeAt(index); + cleared = true; + } + 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 (mDevicePreferenceMap.containsValue(packageName)) { + // make a copy of the key set to avoid ConcurrentModificationException + Object[] keys = mDevicePreferenceMap.keySet().toArray(); + for (int i = 0; i < keys.length; i++) { + Object key = keys[i]; + if (packageName.equals(mDevicePreferenceMap.get(key))) { + mDevicePreferenceMap.remove(key); + cleared = true; + } + } + } + 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(" Device permissions:"); + int count = mDevicePermissionMap.size(); + for (int i = 0; i < count; i++) { + int uid = mDevicePermissionMap.keyAt(i); + pw.println(" " + "uid " + uid + ":"); + ArrayList<DeviceFilter> filters = mDevicePermissionMap.valueAt(i); + for (DeviceFilter filter : filters) { + pw.println(" " + filter); + } + } + pw.println(" Accessory permissions:"); + 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(" Device preferences:"); + for (DeviceFilter filter : mDevicePreferenceMap.keySet()) { + pw.println(" " + filter + ": " + mDevicePreferenceMap.get(filter)); + } + pw.println(" Accessory preferences:"); + for (AccessoryFilter filter : mAccessoryPreferenceMap.keySet()) { + pw.println(" " + filter + ": " + mAccessoryPreferenceMap.get(filter)); + } + } + } +} diff --git a/services/java/com/android/server/usb/UsbResolverActivity.java b/services/java/com/android/server/usb/UsbResolverActivity.java new file mode 100644 index 0000000..e8a09a5 --- /dev/null +++ b/services/java/com/android/server/usb/UsbResolverActivity.java @@ -0,0 +1,102 @@ +/* + * 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.UsbDevice; +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_DEVICE_ATTACHED.equals(action)) { + UsbDevice device = (UsbDevice)intent.getParcelableExtra(UsbManager.EXTRA_DEVICE); + // grant permission for the device + service.grantDevicePermission(device, uid); + // set or clear default setting + if (alwaysCheck) { + service.setDevicePackage(device, ri.activityInfo.packageName); + } else { + service.setDevicePackage(device, null); + } + } else 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); + } + } +} diff --git a/services/java/com/android/server/UsbService.java b/services/java/com/android/server/usb/UsbService.java index 2f84713..94c25e9 100644 --- a/services/java/com/android/server/UsbService.java +++ b/services/java/com/android/server/usb/UsbService.java @@ -14,19 +14,21 @@ * limitations under the License. */ -package com.android.server; +package com.android.server.usb; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; -import android.hardware.IUsbManager; -import android.hardware.UsbAccessory; -import android.hardware.UsbConstants; -import android.hardware.UsbDevice; -import android.hardware.UsbEndpoint; -import android.hardware.UsbInterface; -import android.hardware.UsbManager; +import android.content.pm.PackageManager; +import android.hardware.usb.IUsbManager; +import android.hardware.usb.UsbAccessory; +import android.hardware.usb.UsbConstants; +import android.hardware.usb.UsbDevice; +import android.hardware.usb.UsbEndpoint; +import android.hardware.usb.UsbInterface; +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; @@ -38,10 +40,13 @@ 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. @@ -50,7 +55,7 @@ import java.util.HashMap; * 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. */ -class UsbService extends IUsbManager.Stub { +public class UsbService extends IUsbManager.Stub { private static final String TAG = UsbService.class.getSimpleName(); private static final boolean LOG = false; @@ -102,13 +107,16 @@ class UsbService extends IUsbManager.Stub { private final Context mContext; private final Object mLock = new Object(); + private final UsbDeviceSettingsManager mDeviceManager; + private final boolean mHasUsbHost; + private final boolean mHasUsbAccessory; /* * Handles USB function enable/disable events (device mode) */ private final void functionEnabledLocked(String function, boolean enabled) { boolean enteringAccessoryMode = - (enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function)); + (mHasUsbAccessory && enabled && UsbManager.USB_FUNCTION_ACCESSORY.equals(function)); if (enteringAccessoryMode) { // keep a list of functions to reenable after exiting accessory mode @@ -139,11 +147,9 @@ class UsbService extends IUsbManager.Stub { if (enteringAccessoryMode) { String[] strings = nativeGetAccessoryStrings(); if (strings != null) { - Log.d(TAG, "entering USB accessory mode"); mCurrentAccessory = new UsbAccessory(strings); - Intent intent = new Intent(UsbManager.ACTION_USB_ACCESSORY_ATTACHED); - intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory); - mContext.sendBroadcast(intent); + Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); + mDeviceManager.accessoryAttached(mCurrentAccessory); } else { Log.e(TAG, "nativeGetAccessoryStrings failed"); } @@ -170,7 +176,7 @@ class UsbService extends IUsbManager.Stub { mConnected = intState; // trigger an Intent broadcast if (mSystemReady) { - // debounce disconnects + // debounce disconnects to avoid problems bringing up USB tethering update(mConnected == 0); } } else if ("usb_configuration".equals(name)) { @@ -202,6 +208,11 @@ class UsbService extends IUsbManager.Stub { public UsbService(Context context) { mContext = context; + mDeviceManager = new UsbDeviceSettingsManager(context); + PackageManager pm = mContext.getPackageManager(); + mHasUsbHost = pm.hasSystemFeature(PackageManager.FEATURE_USB_HOST); + mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); + mHostBlacklist = context.getResources().getStringArray( com.android.internal.R.array.config_usbHostBlacklist); @@ -345,11 +356,7 @@ class UsbService extends IUsbManager.Stub { UsbDevice device = new UsbDevice(deviceName, vendorID, productID, deviceClass, deviceSubclass, deviceProtocol, interfaces); mDevices.put(deviceName, device); - - Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_ATTACHED); - intent.putExtra(UsbManager.EXTRA_DEVICE, device); - Log.d(TAG, "usbDeviceAdded, sending " + intent); - mContext.sendBroadcast(intent); + mDeviceManager.deviceAttached(device); } } @@ -358,10 +365,7 @@ class UsbService extends IUsbManager.Stub { synchronized (mLock) { UsbDevice device = mDevices.remove(deviceName); if (device != null) { - Intent intent = new Intent(UsbManager.ACTION_USB_DEVICE_DETACHED); - intent.putExtra(UsbManager.EXTRA_DEVICE, device); - Log.d(TAG, "usbDeviceRemoved, sending " + intent); - mContext.sendBroadcast(intent); + mDeviceManager.deviceDetached(device); } } } @@ -377,10 +381,9 @@ class UsbService extends IUsbManager.Stub { new Thread(null, runnable, "UsbService host thread").start(); } - void systemReady() { + public void systemReady() { synchronized (mLock) { - if (mContext.getResources().getBoolean( - com.android.internal.R.bool.config_hasUsbHostSupport)) { + if (mHasUsbHost) { // start monitoring for connected USB devices initHostSupport(); } @@ -402,7 +405,6 @@ class UsbService extends IUsbManager.Stub { /* Returns a list of all currently attached USB devices (host mdoe) */ public void getDeviceList(Bundle devices) { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); synchronized (mLock) { for (String name : mDevices.keySet()) { devices.putParcelable(name, mDevices.get(name)); @@ -412,28 +414,73 @@ class UsbService extends IUsbManager.Stub { /* Opens the specified USB device (host mode) */ public ParcelFileDescriptor openDevice(String deviceName) { - if (isBlackListed(deviceName)) { - throw new SecurityException("USB device is on a restricted bus"); - } - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); - if (mDevices.get(deviceName) == null) { - // if it is not in mDevices, it either does not exist or is blacklisted - throw new IllegalArgumentException( - "device " + deviceName + " does not exist or is restricted"); + synchronized (mLock) { + if (isBlackListed(deviceName)) { + throw new SecurityException("USB device is on a restricted bus"); + } + UsbDevice device = mDevices.get(deviceName); + if (device == null) { + // if it is not in mDevices, it either does not exist or is blacklisted + throw new IllegalArgumentException( + "device " + deviceName + " does not exist or is restricted"); + } + mDeviceManager.checkPermission(device); + return nativeOpenDevice(deviceName); } - return nativeOpenDevice(deviceName); } /* returns the currently attached USB accessory (device mode) */ public UsbAccessory getCurrentAccessory() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); - return mCurrentAccessory; + synchronized (mLock) { + mDeviceManager.checkPermission(mCurrentAccessory); + return mCurrentAccessory; + } } /* opens the currently attached USB accessory (device mode) */ - public ParcelFileDescriptor openAccessory() { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.ACCESS_USB, null); - return nativeOpenAccessory(); + 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 setDevicePackage(UsbDevice device, String packageName) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.setDevicePackage(device, packageName); + } + + public void setAccessoryPackage(UsbAccessory accessory, String packageName) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.setAccessoryPackage(accessory, packageName); + } + + public void grantDevicePermission(UsbDevice device, int uid) { + mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); + mDeviceManager.grantDevicePermission(device, uid); + } + + 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); } /* @@ -470,10 +517,7 @@ class UsbService extends IUsbManager.Stub { } mAccessoryRestoreFunctions.clear(); - Intent intent = new Intent( - UsbManager.ACTION_USB_ACCESSORY_DETACHED); - intent.putExtra(UsbManager.EXTRA_ACCESSORY, mCurrentAccessory); - mContext.sendBroadcast(intent); + mDeviceManager.accessoryDetached(mCurrentAccessory); mCurrentAccessory = null; // this will cause an immediate reset of the USB bus, @@ -513,6 +557,42 @@ class UsbService extends IUsbManager.Stub { } }; + @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(" USB Host State:"); + for (String name : mDevices.keySet()) { + pw.println(" " + name + ": " + mDevices.get(name)); + } + pw.println(" mCurrentAccessory: " + mCurrentAccessory); + + mDeviceManager.dump(fd, pw); + } + } + // host support private native void monitorUsbHostBus(); private native ParcelFileDescriptor nativeOpenDevice(String deviceName); diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index b7cc324..e3218c8 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -5812,8 +5812,7 @@ public class WindowManagerService extends IWindowManager.Stub mDisplay = wm.getDefaultDisplay(); mInitialDisplayWidth = mDisplay.getWidth(); mInitialDisplayHeight = mDisplay.getHeight(); - mInputManager.setDisplaySize(0, Display.unmapDisplaySize(mInitialDisplayWidth), - Display.unmapDisplaySize(mInitialDisplayHeight)); + mInputManager.setDisplaySize(0, mDisplay.getRealWidth(), mDisplay.getRealHeight()); } try { diff --git a/services/jni/com_android_server_UsbService.cpp b/services/jni/com_android_server_UsbService.cpp index 192daaf..3c49e54 100644 --- a/services/jni/com_android_server_UsbService.cpp +++ b/services/jni/com_android_server_UsbService.cpp @@ -177,7 +177,6 @@ static void set_accessory_string(JNIEnv *env, int fd, int cmd, jobjectArray strA buffer[0] = 0; int length = ioctl(fd, cmd, buffer); - LOGD("ioctl returned %d", length); if (buffer[0]) { jstring obj = env->NewStringUTF(buffer); env->SetObjectArrayElement(strArray, index, obj); @@ -236,9 +235,9 @@ static JNINativeMethod method_table[] = { int register_android_server_UsbService(JNIEnv *env) { - jclass clazz = env->FindClass("com/android/server/UsbService"); + jclass clazz = env->FindClass("com/android/server/usb/UsbService"); if (clazz == NULL) { - LOGE("Can't find com/android/server/UsbService"); + LOGE("Can't find com/android/server/usb/UsbService"); return -1; } method_usbDeviceAdded = env->GetMethodID(clazz, "usbDeviceAdded", "(Ljava/lang/String;IIIII[I[I)V"); @@ -267,7 +266,7 @@ int register_android_server_UsbService(JNIEnv *env) LOG_FATAL_IF(gParcelFileDescriptorOffsets.mConstructor == NULL, "Unable to find constructor for android.os.ParcelFileDescriptor"); - return jniRegisterNativeMethods(env, "com/android/server/UsbService", + return jniRegisterNativeMethods(env, "com/android/server/usb/UsbService", method_table, NELEM(method_table)); } |