• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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