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
17package com.android.server.usb;
18
19import android.app.PendingIntent;
20import android.content.BroadcastReceiver;
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
26import android.hardware.usb.IUsbManager;
27import android.hardware.usb.UsbAccessory;
28import android.hardware.usb.UsbManager;
29import android.net.Uri;
30import android.os.Binder;
31import android.os.Bundle;
32import android.os.Handler;
33import android.os.Message;
34import android.os.Parcelable;
35import android.os.ParcelFileDescriptor;
36import android.os.UEventObserver;
37import android.provider.Settings;
38import android.util.Log;
39import android.util.Slog;
40
41import java.io.File;
42import java.io.FileDescriptor;
43import java.io.FileNotFoundException;
44import java.io.FileReader;
45import java.io.PrintWriter;
46import java.util.ArrayList;
47import java.util.HashMap;
48import 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 */
57public 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
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     */
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
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
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
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     */
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) */
320    public UsbAccessory getCurrentAccessory() {
321        return mCurrentAccessory;
322    }
323
324    /* opens the currently attached USB accessory (device mode) */
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
340    public void setAccessoryPackage(UsbAccessory accessory, String packageName) {
341        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
342        mDeviceManager.setAccessoryPackage(accessory, packageName);
343    }
344
345    public boolean hasAccessoryPermission(UsbAccessory accessory) {
346        return mDeviceManager.hasPermission(accessory);
347    }
348
349    public void requestAccessoryPermission(UsbAccessory accessory, String packageName,
350            PendingIntent pi) {
351        mDeviceManager.requestPermission(accessory, packageName, pi);
352    }
353
354    public void grantAccessoryPermission(UsbAccessory accessory, int uid) {
355        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
356        mDeviceManager.grantAccessoryPermission(accessory, uid);
357    }
358
359    public boolean hasDefaults(String packageName) {
360        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.MANAGE_USB, null);
361        return mDeviceManager.hasDefaults(packageName);
362    }
363
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
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
478    private native String[] nativeGetAccessoryStrings();
479    private native ParcelFileDescriptor nativeOpenAccessory();
480}
481