1 /* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.usb; 18 19 import android.app.PendingIntent; 20 import android.content.BroadcastReceiver; 21 import android.content.ContentResolver; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.IntentFilter; 25 import android.content.pm.PackageManager; 26 import android.hardware.usb.IUsbManager; 27 import android.hardware.usb.UsbAccessory; 28 import android.hardware.usb.UsbManager; 29 import android.net.Uri; 30 import android.os.Binder; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.Message; 34 import android.os.Parcelable; 35 import android.os.ParcelFileDescriptor; 36 import android.os.UEventObserver; 37 import android.provider.Settings; 38 import android.util.Log; 39 import android.util.Slog; 40 41 import java.io.File; 42 import java.io.FileDescriptor; 43 import java.io.FileNotFoundException; 44 import java.io.FileReader; 45 import java.io.PrintWriter; 46 import java.util.ArrayList; 47 import java.util.HashMap; 48 import java.util.List; 49 50 /** 51 * UsbService monitors for changes to USB state. 52 * This includes code for both USB host support (where the android device is the host) 53 * as well as USB device support (android device is connected to a USB host). 54 * Accessory mode is a special case of USB device mode, where the android device is 55 * connected to a USB host that supports the android accessory protocol. 56 */ 57 public class UsbService extends IUsbManager.Stub { 58 private static final String TAG = UsbService.class.getSimpleName(); 59 private static final boolean LOG = false; 60 61 private static final String USB_CONNECTED_MATCH = 62 "DEVPATH=/devices/virtual/switch/usb_connected"; 63 private static final String USB_CONFIGURATION_MATCH = 64 "DEVPATH=/devices/virtual/switch/usb_configuration"; 65 private static final String USB_FUNCTIONS_MATCH = 66 "DEVPATH=/devices/virtual/usb_composite/"; 67 private static final String USB_CONNECTED_PATH = 68 "/sys/class/switch/usb_connected/state"; 69 private static final String USB_CONFIGURATION_PATH = 70 "/sys/class/switch/usb_configuration/state"; 71 private static final String USB_COMPOSITE_CLASS_PATH = 72 "/sys/class/usb_composite"; 73 74 private static final int MSG_UPDATE_STATE = 0; 75 private static final int MSG_FUNCTION_ENABLED = 1; 76 private static final int MSG_FUNCTION_DISABLED = 2; 77 78 // Delay for debouncing USB disconnects. 79 // We often get rapid connect/disconnect events when enabling USB functions, 80 // which need debouncing. 81 private static final int UPDATE_DELAY = 1000; 82 83 // current connected and configuration state 84 private int mConnected; 85 private int mConfiguration; 86 87 // last broadcasted connected and configuration state 88 private int mLastConnected = -1; 89 private int mLastConfiguration = -1; 90 91 // lists of enabled and disabled USB functions (for USB device mode) 92 private final ArrayList<String> mEnabledFunctions = new ArrayList<String>(); 93 private final ArrayList<String> mDisabledFunctions = new ArrayList<String>(); 94 95 private boolean mSystemReady; 96 97 private UsbAccessory mCurrentAccessory; 98 // USB functions that are enabled by default, to restore after exiting accessory mode 99 private final ArrayList<String> mDefaultFunctions = new ArrayList<String>(); 100 101 private final Context mContext; 102 private final Object mLock = new Object(); 103 private final UsbDeviceSettingsManager mDeviceManager; 104 private final boolean mHasUsbAccessory; 105 readCurrentAccessoryLocked()106 private final void readCurrentAccessoryLocked() { 107 if (mHasUsbAccessory) { 108 String[] strings = nativeGetAccessoryStrings(); 109 if (strings != null) { 110 mCurrentAccessory = new UsbAccessory(strings); 111 Log.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); 112 if (mSystemReady) { 113 mDeviceManager.accessoryAttached(mCurrentAccessory); 114 } 115 } else { 116 Log.e(TAG, "nativeGetAccessoryStrings failed"); 117 } 118 } 119 } 120 121 /* 122 * Handles USB function enable/disable events (device mode) 123 */ functionEnabledLocked(String function, boolean enabled)124 private final void functionEnabledLocked(String function, boolean enabled) { 125 if (enabled) { 126 if (!mEnabledFunctions.contains(function)) { 127 mEnabledFunctions.add(function); 128 } 129 mDisabledFunctions.remove(function); 130 131 if (UsbManager.USB_FUNCTION_ACCESSORY.equals(function)) { 132 readCurrentAccessoryLocked(); 133 } 134 } else { 135 if (!mDisabledFunctions.contains(function)) { 136 mDisabledFunctions.add(function); 137 } 138 mEnabledFunctions.remove(function); 139 } 140 } 141 142 /* 143 * Listens for uevent messages from the kernel to monitor the USB state (device mode) 144 */ 145 private final UEventObserver mUEventObserver = new UEventObserver() { 146 @Override 147 public void onUEvent(UEventObserver.UEvent event) { 148 if (Log.isLoggable(TAG, Log.VERBOSE)) { 149 Slog.v(TAG, "USB UEVENT: " + event.toString()); 150 } 151 152 synchronized (mLock) { 153 String name = event.get("SWITCH_NAME"); 154 String state = event.get("SWITCH_STATE"); 155 if (name != null && state != null) { 156 try { 157 int intState = Integer.parseInt(state); 158 if ("usb_connected".equals(name)) { 159 mConnected = intState; 160 // trigger an Intent broadcast 161 if (mSystemReady) { 162 // debounce disconnects to avoid problems bringing up USB tethering 163 update(mConnected == 0); 164 } 165 } else if ("usb_configuration".equals(name)) { 166 mConfiguration = intState; 167 // trigger an Intent broadcast 168 if (mSystemReady) { 169 update(mConnected == 0); 170 } 171 } 172 } catch (NumberFormatException e) { 173 Slog.e(TAG, "Could not parse switch state from event " + event); 174 } 175 } else { 176 String function = event.get("FUNCTION"); 177 String enabledStr = event.get("ENABLED"); 178 if (function != null && enabledStr != null) { 179 // Note: we do not broadcast a change when a function is enabled or disabled. 180 // We just record the state change for the next broadcast. 181 int what = ("1".equals(enabledStr) ? 182 MSG_FUNCTION_ENABLED : MSG_FUNCTION_DISABLED); 183 Message msg = Message.obtain(mHandler, what); 184 msg.obj = function; 185 mHandler.sendMessage(msg); 186 } 187 } 188 } 189 } 190 }; 191 192 private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { 193 public void onReceive(Context context, Intent intent) { 194 // handle accessories attached at boot time 195 synchronized (mLock) { 196 if (mCurrentAccessory != null) { 197 mDeviceManager.accessoryAttached(mCurrentAccessory); 198 } 199 } 200 } 201 }; 202 UsbService(Context context)203 public UsbService(Context context) { 204 mContext = context; 205 mDeviceManager = new UsbDeviceSettingsManager(context); 206 PackageManager pm = mContext.getPackageManager(); 207 mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); 208 209 synchronized (mLock) { 210 init(); // set initial status 211 212 // Watch for USB configuration changes 213 if (mConfiguration >= 0) { 214 mUEventObserver.startObserving(USB_CONNECTED_MATCH); 215 mUEventObserver.startObserving(USB_CONFIGURATION_MATCH); 216 mUEventObserver.startObserving(USB_FUNCTIONS_MATCH); 217 } 218 } 219 } 220 init()221 private final void init() { 222 char[] buffer = new char[1024]; 223 boolean inAccessoryMode = false; 224 225 // Read initial USB state (device mode) 226 mConfiguration = -1; 227 try { 228 FileReader file = new FileReader(USB_CONNECTED_PATH); 229 int len = file.read(buffer, 0, 1024); 230 file.close(); 231 mConnected = Integer.valueOf((new String(buffer, 0, len)).trim()); 232 233 file = new FileReader(USB_CONFIGURATION_PATH); 234 len = file.read(buffer, 0, 1024); 235 file.close(); 236 mConfiguration = Integer.valueOf((new String(buffer, 0, len)).trim()); 237 238 } catch (FileNotFoundException e) { 239 Slog.i(TAG, "This kernel does not have USB configuration switch support"); 240 } catch (Exception e) { 241 Slog.e(TAG, "" , e); 242 } 243 if (mConfiguration < 0) { 244 // This may happen in the emulator or devices without USB device mode support 245 return; 246 } 247 248 // Read initial list of enabled and disabled functions (device mode) 249 try { 250 File[] files = new File(USB_COMPOSITE_CLASS_PATH).listFiles(); 251 for (int i = 0; i < files.length; i++) { 252 File file = new File(files[i], "enable"); 253 FileReader reader = new FileReader(file); 254 int len = reader.read(buffer, 0, 1024); 255 reader.close(); 256 int value = Integer.valueOf((new String(buffer, 0, len)).trim()); 257 String functionName = files[i].getName(); 258 if (value == 1) { 259 mEnabledFunctions.add(functionName); 260 if (UsbManager.USB_FUNCTION_ACCESSORY.equals(functionName)) { 261 // The USB accessory driver is on by default, but it might have been 262 // enabled before the USB service has initialized. 263 inAccessoryMode = true; 264 } else if (!UsbManager.USB_FUNCTION_ADB.equals(functionName)) { 265 // adb is enabled/disabled automatically by the adbd daemon, 266 // so don't treat it as a default function. 267 mDefaultFunctions.add(functionName); 268 } 269 } else { 270 mDisabledFunctions.add(functionName); 271 } 272 } 273 } catch (FileNotFoundException e) { 274 Slog.w(TAG, "This kernel does not have USB composite class support"); 275 } catch (Exception e) { 276 Slog.e(TAG, "" , e); 277 } 278 279 // handle the case where an accessory switched the driver to accessory mode 280 // before the framework finished booting 281 if (inAccessoryMode) { 282 readCurrentAccessoryLocked(); 283 284 // FIXME - if we booted in accessory mode, then we have no way to figure out 285 // which functions are enabled by default. 286 // For now, assume that MTP or mass storage are the only possibilities 287 if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MTP)) { 288 mDefaultFunctions.add(UsbManager.USB_FUNCTION_MTP); 289 } else if (mDisabledFunctions.contains(UsbManager.USB_FUNCTION_MASS_STORAGE)) { 290 mDefaultFunctions.add(UsbManager.USB_FUNCTION_MASS_STORAGE); 291 } 292 } 293 } 294 systemReady()295 public void systemReady() { 296 synchronized (mLock) { 297 update(false); 298 if (mCurrentAccessory != null) { 299 Log.d(TAG, "accessoryAttached at systemReady"); 300 // its still too early to handle accessories, so add a BOOT_COMPLETED receiver 301 // to handle this later. 302 mContext.registerReceiver(mBootCompletedReceiver, 303 new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); 304 } 305 mSystemReady = true; 306 } 307 } 308 309 /* 310 * Sends a message to update the USB connected and configured state (device mode). 311 * If delayed is true, then we add a small delay in sending the message to debounce 312 * the USB connection when enabling USB tethering. 313 */ update(boolean delayed)314 private final void update(boolean delayed) { 315 mHandler.removeMessages(MSG_UPDATE_STATE); 316 mHandler.sendEmptyMessageDelayed(MSG_UPDATE_STATE, delayed ? UPDATE_DELAY : 0); 317 } 318 319 /* returns the currently attached USB accessory (device mode) */ getCurrentAccessory()320 public UsbAccessory getCurrentAccessory() { 321 return mCurrentAccessory; 322 } 323 324 /* opens the currently attached USB accessory (device mode) */ openAccessory(UsbAccessory accessory)325 public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { 326 synchronized (mLock) { 327 if (mCurrentAccessory == null) { 328 throw new IllegalArgumentException("no accessory attached"); 329 } 330 if (!mCurrentAccessory.equals(accessory)) { 331 Log.e(TAG, accessory.toString() + " does not match current accessory " 332 + mCurrentAccessory); 333 throw new IllegalArgumentException("accessory not attached"); 334 } 335 mDeviceManager.checkPermission(mCurrentAccessory); 336 return nativeOpenAccessory(); 337 } 338 } 339 setAccessoryPackage(UsbAccessory accessory, String packageName)340 public void setAccessoryPackage(UsbAccessory accessory, String packageName) { 341 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); 342 mDeviceManager.setAccessoryPackage(accessory, packageName); 343 } 344 hasAccessoryPermission(UsbAccessory accessory)345 public boolean hasAccessoryPermission(UsbAccessory accessory) { 346 return mDeviceManager.hasPermission(accessory); 347 } 348 requestAccessoryPermission(UsbAccessory accessory, String packageName, PendingIntent pi)349 public void requestAccessoryPermission(UsbAccessory accessory, String packageName, 350 PendingIntent pi) { 351 mDeviceManager.requestPermission(accessory, packageName, pi); 352 } 353 grantAccessoryPermission(UsbAccessory accessory, int uid)354 public void grantAccessoryPermission(UsbAccessory accessory, int uid) { 355 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); 356 mDeviceManager.grantAccessoryPermission(accessory, uid); 357 } 358 hasDefaults(String packageName)359 public boolean hasDefaults(String packageName) { 360 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); 361 return mDeviceManager.hasDefaults(packageName); 362 } 363 clearDefaults(String packageName)364 public void clearDefaults(String packageName) { 365 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null); 366 mDeviceManager.clearDefaults(packageName); 367 } 368 369 /* 370 * This handler is for deferred handling of events related to device mode and accessories. 371 */ 372 private final Handler mHandler = new Handler() { 373 private void addEnabledFunctionsLocked(Intent intent) { 374 // include state of all USB functions in our extras 375 for (int i = 0; i < mEnabledFunctions.size(); i++) { 376 intent.putExtra(mEnabledFunctions.get(i), UsbManager.USB_FUNCTION_ENABLED); 377 } 378 for (int i = 0; i < mDisabledFunctions.size(); i++) { 379 intent.putExtra(mDisabledFunctions.get(i), UsbManager.USB_FUNCTION_DISABLED); 380 } 381 } 382 383 @Override 384 public void handleMessage(Message msg) { 385 synchronized (mLock) { 386 switch (msg.what) { 387 case MSG_UPDATE_STATE: 388 if (mConnected != mLastConnected || mConfiguration != mLastConfiguration) { 389 if (mConnected == 0) { 390 if (UsbManager.isFunctionEnabled( 391 UsbManager.USB_FUNCTION_ACCESSORY)) { 392 // make sure accessory mode is off, and restore default functions 393 Log.d(TAG, "exited USB accessory mode"); 394 if (!UsbManager.setFunctionEnabled 395 (UsbManager.USB_FUNCTION_ACCESSORY, false)) { 396 Log.e(TAG, "could not disable accessory function"); 397 } 398 int count = mDefaultFunctions.size(); 399 for (int i = 0; i < count; i++) { 400 String function = mDefaultFunctions.get(i); 401 if (!UsbManager.setFunctionEnabled(function, true)) { 402 Log.e(TAG, "could not reenable function " + function); 403 } 404 } 405 406 if (mCurrentAccessory != null) { 407 mDeviceManager.accessoryDetached(mCurrentAccessory); 408 mCurrentAccessory = null; 409 } 410 } 411 } 412 413 final ContentResolver cr = mContext.getContentResolver(); 414 if (Settings.Secure.getInt(cr, 415 Settings.Secure.DEVICE_PROVISIONED, 0) == 0) { 416 Slog.i(TAG, "Device not provisioned, skipping USB broadcast"); 417 return; 418 } 419 420 mLastConnected = mConnected; 421 mLastConfiguration = mConfiguration; 422 423 // send a sticky broadcast containing current USB state 424 Intent intent = new Intent(UsbManager.ACTION_USB_STATE); 425 intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); 426 intent.putExtra(UsbManager.USB_CONNECTED, mConnected != 0); 427 intent.putExtra(UsbManager.USB_CONFIGURATION, mConfiguration); 428 addEnabledFunctionsLocked(intent); 429 mContext.sendStickyBroadcast(intent); 430 } 431 break; 432 case MSG_FUNCTION_ENABLED: 433 case MSG_FUNCTION_DISABLED: 434 functionEnabledLocked((String)msg.obj, msg.what == MSG_FUNCTION_ENABLED); 435 break; 436 } 437 } 438 } 439 }; 440 441 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)442 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 443 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 444 != PackageManager.PERMISSION_GRANTED) { 445 pw.println("Permission Denial: can't dump UsbManager from from pid=" 446 + Binder.getCallingPid() 447 + ", uid=" + Binder.getCallingUid()); 448 return; 449 } 450 451 synchronized (mLock) { 452 pw.println("USB Manager State:"); 453 454 pw.println(" USB Device State:"); 455 pw.print(" Enabled Functions: "); 456 for (int i = 0; i < mEnabledFunctions.size(); i++) { 457 pw.print(mEnabledFunctions.get(i) + " "); 458 } 459 pw.println(""); 460 pw.print(" Disabled Functions: "); 461 for (int i = 0; i < mDisabledFunctions.size(); i++) { 462 pw.print(mDisabledFunctions.get(i) + " "); 463 } 464 pw.println(""); 465 pw.print(" Default Functions: "); 466 for (int i = 0; i < mDefaultFunctions.size(); i++) { 467 pw.print(mDefaultFunctions.get(i) + " "); 468 } 469 pw.println(""); 470 pw.println(" mConnected: " + mConnected + ", mConfiguration: " + mConfiguration); 471 pw.println(" mCurrentAccessory: " + mCurrentAccessory); 472 473 mDeviceManager.dump(fd, pw); 474 } 475 } 476 477 // accessory support nativeGetAccessoryStrings()478 private native String[] nativeGetAccessoryStrings(); nativeOpenAccessory()479 private native ParcelFileDescriptor nativeOpenAccessory(); 480 } 481