/* * 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 an * limitations under the License. */ package com.android.server.usb; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.pm.PackageManager; import android.content.res.Resources; import android.database.ContentObserver; import android.hardware.usb.UsbAccessory; import android.hardware.usb.UsbManager; import android.media.AudioManager; import android.os.FileUtils; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.os.ParcelFileDescriptor; import android.os.SystemClock; import android.os.SystemProperties; import android.os.UEventObserver; import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.os.storage.StorageVolume; import android.provider.Settings; import android.util.Pair; import android.util.Slog; import com.android.internal.annotations.GuardedBy; import com.android.server.FgThread; import java.io.File; import java.io.FileDescriptor; import java.io.FileNotFoundException; import java.io.IOException; import java.io.PrintWriter; import java.util.Arrays; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Scanner; /** * UsbDeviceManager manages USB state in device mode. */ public class UsbDeviceManager { private static final String TAG = UsbDeviceManager.class.getSimpleName(); private static final boolean DEBUG = false; private static final String USB_STATE_MATCH = "DEVPATH=/devices/virtual/android_usb/android0"; private static final String ACCESSORY_START_MATCH = "DEVPATH=/devices/virtual/misc/usb_accessory"; private static final String FUNCTIONS_PATH = "/sys/class/android_usb/android0/functions"; private static final String STATE_PATH = "/sys/class/android_usb/android0/state"; private static final String MASS_STORAGE_FILE_PATH = "/sys/class/android_usb/android0/f_mass_storage/lun/file"; private static final String RNDIS_ETH_ADDR_PATH = "/sys/class/android_usb/android0/f_rndis/ethaddr"; private static final String AUDIO_SOURCE_PCM_PATH = "/sys/class/android_usb/android0/f_audio_source/pcm"; private static final int MSG_UPDATE_STATE = 0; private static final int MSG_ENABLE_ADB = 1; private static final int MSG_SET_CURRENT_FUNCTIONS = 2; private static final int MSG_SYSTEM_READY = 3; private static final int MSG_BOOT_COMPLETED = 4; private static final int MSG_USER_SWITCHED = 5; private static final int AUDIO_MODE_NONE = 0; private static final int AUDIO_MODE_SOURCE = 1; // Delay for debouncing USB disconnects. // We often get rapid connect/disconnect events when enabling USB functions, // which need debouncing. private static final int UPDATE_DELAY = 1000; // Time we received a request to enter USB accessory mode private long mAccessoryModeRequestTime = 0; // Timeout for entering USB request mode. // Request is cancelled if host does not configure device within 10 seconds. private static final int ACCESSORY_REQUEST_TIMEOUT = 10 * 1000; private static final String BOOT_MODE_PROPERTY = "ro.bootmode"; private UsbHandler mHandler; private boolean mBootCompleted; private final Object mLock = new Object(); private final Context mContext; private final ContentResolver mContentResolver; @GuardedBy("mLock") private UsbSettingsManager mCurrentSettings; private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; private boolean mUseUsbNotification; private boolean mAdbEnabled; private boolean mAudioSourceEnabled; private Map>> mOemModeMap; private String[] mAccessoryStrings; private UsbDebuggingManager mDebuggingManager; private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { super(null); } @Override public void onChange(boolean selfChange) { boolean enable = (Settings.Global.getInt(mContentResolver, Settings.Global.ADB_ENABLED, 0) > 0); mHandler.sendMessage(MSG_ENABLE_ADB, enable); } } /* * Listens for uevent messages from the kernel to monitor the USB state */ private final UEventObserver mUEventObserver = new UEventObserver() { @Override public void onUEvent(UEventObserver.UEvent event) { if (DEBUG) Slog.v(TAG, "USB UEVENT: " + event.toString()); String state = event.get("USB_STATE"); String accessory = event.get("ACCESSORY"); if (state != null) { mHandler.updateState(state); } else if ("START".equals(accessory)) { if (DEBUG) Slog.d(TAG, "got accessory start"); startAccessoryMode(); } } }; public UsbDeviceManager(Context context) { mContext = context; mContentResolver = context.getContentResolver(); PackageManager pm = mContext.getPackageManager(); mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); initRndisAddress(); readOemUsbOverrideConfig(); mHandler = new UsbHandler(FgThread.get().getLooper()); if (nativeIsStartRequested()) { if (DEBUG) Slog.d(TAG, "accessory attached at boot"); startAccessoryMode(); } boolean secureAdbEnabled = SystemProperties.getBoolean("ro.adb.secure", false); boolean dataEncrypted = "1".equals(SystemProperties.get("vold.decrypt")); if (secureAdbEnabled && !dataEncrypted) { mDebuggingManager = new UsbDebuggingManager(context); } } public void setCurrentSettings(UsbSettingsManager settings) { synchronized (mLock) { mCurrentSettings = settings; } } private UsbSettingsManager getCurrentSettings() { synchronized (mLock) { return mCurrentSettings; } } public void systemReady() { if (DEBUG) Slog.d(TAG, "systemReady"); mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE); // We do not show the USB notification if the primary volume supports mass storage. // The legacy mass storage UI will be used instead. boolean massStorageSupported = false; final StorageManager storageManager = StorageManager.from(mContext); final StorageVolume primary = storageManager.getPrimaryVolume(); massStorageSupported = primary != null && primary.allowMassStorage(); mUseUsbNotification = !massStorageSupported; // make sure the ADB_ENABLED setting value matches the current state try { Settings.Global.putInt(mContentResolver, Settings.Global.ADB_ENABLED, mAdbEnabled ? 1 : 0); } catch (SecurityException e) { // If UserManager.DISALLOW_DEBUGGING_FEATURES is on, that this setting can't be changed. Slog.d(TAG, "ADB_ENABLED is restricted."); } mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } private void startAccessoryMode() { if (!mHasUsbAccessory) return; mAccessoryStrings = nativeGetAccessoryStrings(); boolean enableAudio = (nativeGetAudioMode() == AUDIO_MODE_SOURCE); // don't start accessory mode if our mandatory strings have not been set boolean enableAccessory = (mAccessoryStrings != null && mAccessoryStrings[UsbAccessory.MANUFACTURER_STRING] != null && mAccessoryStrings[UsbAccessory.MODEL_STRING] != null); String functions = null; if (enableAccessory && enableAudio) { functions = UsbManager.USB_FUNCTION_ACCESSORY + "," + UsbManager.USB_FUNCTION_AUDIO_SOURCE; } else if (enableAccessory) { functions = UsbManager.USB_FUNCTION_ACCESSORY; } else if (enableAudio) { functions = UsbManager.USB_FUNCTION_AUDIO_SOURCE; } if (functions != null) { mAccessoryModeRequestTime = SystemClock.elapsedRealtime(); setCurrentFunctions(functions, false); } } private static void initRndisAddress() { // configure RNDIS ethernet address based on our serial number using the same algorithm // we had been previously using in kernel board files final int ETH_ALEN = 6; int address[] = new int[ETH_ALEN]; // first byte is 0x02 to signify a locally administered address address[0] = 0x02; String serial = SystemProperties.get("ro.serialno", "1234567890ABCDEF"); int serialLength = serial.length(); // XOR the USB serial across the remaining 5 bytes for (int i = 0; i < serialLength; i++) { address[i % (ETH_ALEN - 1) + 1] ^= (int)serial.charAt(i); } String addrString = String.format(Locale.US, "%02X:%02X:%02X:%02X:%02X:%02X", address[0], address[1], address[2], address[3], address[4], address[5]); try { FileUtils.stringToFile(RNDIS_ETH_ADDR_PATH, addrString); } catch (IOException e) { Slog.e(TAG, "failed to write to " + RNDIS_ETH_ADDR_PATH); } } private static String addFunction(String functions, String function) { if ("none".equals(functions)) { return function; } if (!containsFunction(functions, function)) { if (functions.length() > 0) { functions += ","; } functions += function; } return functions; } private static String removeFunction(String functions, String function) { String[] split = functions.split(","); for (int i = 0; i < split.length; i++) { if (function.equals(split[i])) { split[i] = null; } } if (split.length == 1 && split[0] == null) { return "none"; } StringBuilder builder = new StringBuilder(); for (int i = 0; i < split.length; i++) { String s = split[i]; if (s != null) { if (builder.length() > 0) { builder.append(","); } builder.append(s); } } return builder.toString(); } private static boolean containsFunction(String functions, String function) { return Arrays.asList(functions.split(",")).contains(function); } private final class UsbHandler extends Handler { // current USB state private boolean mConnected; private boolean mConfigured; private String mCurrentFunctions; private String mDefaultFunctions; private UsbAccessory mCurrentAccessory; private int mUsbNotificationId; private boolean mAdbNotificationShown; private int mCurrentUser = UserHandle.USER_NULL; private final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Slog.d(TAG, "boot completed"); mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); } }; private final BroadcastReceiver mUserSwitchedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); mHandler.obtainMessage(MSG_USER_SWITCHED, userId, 0).sendToTarget(); } }; public UsbHandler(Looper looper) { super(looper); try { // persist.sys.usb.config should never be unset. But if it is, set it to "adb" // so we have a chance of debugging what happened. mDefaultFunctions = SystemProperties.get("persist.sys.usb.config", "adb"); // Check if USB mode needs to be overridden depending on OEM specific bootmode. mDefaultFunctions = processOemUsbOverride(mDefaultFunctions); // sanity check the sys.usb.config system property // this may be necessary if we crashed while switching USB configurations String config = SystemProperties.get("sys.usb.config", "none"); if (!config.equals(mDefaultFunctions)) { Slog.w(TAG, "resetting config to persistent property: " + mDefaultFunctions); SystemProperties.set("sys.usb.config", mDefaultFunctions); } mCurrentFunctions = getDefaultFunctions(); String state = FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim(); updateState(state); mAdbEnabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ADB); // Upgrade step for previous versions that used persist.service.adb.enable String value = SystemProperties.get("persist.service.adb.enable", ""); if (value.length() > 0) { char enable = value.charAt(0); if (enable == '1') { setAdbEnabled(true); } else if (enable == '0') { setAdbEnabled(false); } SystemProperties.set("persist.service.adb.enable", ""); } // register observer to listen for settings changes mContentResolver.registerContentObserver( Settings.Global.getUriFor(Settings.Global.ADB_ENABLED), false, new AdbSettingsObserver()); // Watch for USB configuration changes mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); IntentFilter filter = new IntentFilter(Intent.ACTION_BOOT_COMPLETED); filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); mContext.registerReceiver(mBootCompletedReceiver, filter); mContext.registerReceiver( mUserSwitchedReceiver, new IntentFilter(Intent.ACTION_USER_SWITCHED)); } catch (Exception e) { Slog.e(TAG, "Error initializing UsbHandler", e); } } public void sendMessage(int what, boolean arg) { removeMessages(what); Message m = Message.obtain(this, what); m.arg1 = (arg ? 1 : 0); sendMessage(m); } public void sendMessage(int what, Object arg) { removeMessages(what); Message m = Message.obtain(this, what); m.obj = arg; sendMessage(m); } public void sendMessage(int what, Object arg0, boolean arg1) { removeMessages(what); Message m = Message.obtain(this, what); m.obj = arg0; m.arg1 = (arg1 ? 1 : 0); sendMessage(m); } public void updateState(String state) { int connected, configured; if ("DISCONNECTED".equals(state)) { connected = 0; configured = 0; } else if ("CONNECTED".equals(state)) { connected = 1; configured = 0; } else if ("CONFIGURED".equals(state)) { connected = 1; configured = 1; } else { Slog.e(TAG, "unknown state " + state); return; } removeMessages(MSG_UPDATE_STATE); Message msg = Message.obtain(this, MSG_UPDATE_STATE); msg.arg1 = connected; msg.arg2 = configured; // debounce disconnects to avoid problems bringing up USB tethering sendMessageDelayed(msg, (connected == 0) ? UPDATE_DELAY : 0); } private boolean waitForState(String state) { // wait for the transition to complete. // give up after 1 second. for (int i = 0; i < 20; i++) { // State transition is done when sys.usb.state is set to the new configuration if (state.equals(SystemProperties.get("sys.usb.state"))) return true; SystemClock.sleep(50); } Slog.e(TAG, "waitForState(" + state + ") FAILED"); return false; } private boolean setUsbConfig(String config) { if (DEBUG) Slog.d(TAG, "setUsbConfig(" + config + ")"); // set the new configuration SystemProperties.set("sys.usb.config", config); return waitForState(config); } private void setAdbEnabled(boolean enable) { if (DEBUG) Slog.d(TAG, "setAdbEnabled: " + enable); if (enable != mAdbEnabled) { mAdbEnabled = enable; // Due to the persist.sys.usb.config property trigger, changing adb state requires // persisting default function setEnabledFunctions(mDefaultFunctions, true); // After persisting them use the lock-down aware function set setEnabledFunctions(getDefaultFunctions(), false); updateAdbNotification(); } if (mDebuggingManager != null) { mDebuggingManager.setAdbEnabled(mAdbEnabled); } } private void setEnabledFunctions(String functions, boolean makeDefault) { if (DEBUG) Slog.d(TAG, "setEnabledFunctions " + functions + " makeDefault: " + makeDefault); // Do not update persystent.sys.usb.config if the device is booted up // with OEM specific mode. if (functions != null && makeDefault && !needsOemUsbOverride()) { if (mAdbEnabled) { functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); } else { functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); } if (!mDefaultFunctions.equals(functions)) { if (!setUsbConfig("none")) { Slog.e(TAG, "Failed to disable USB"); // revert to previous configuration if we fail setUsbConfig(mCurrentFunctions); return; } // setting this property will also change the current USB state // via a property trigger SystemProperties.set("persist.sys.usb.config", functions); if (waitForState(functions)) { mCurrentFunctions = functions; mDefaultFunctions = functions; } else { Slog.e(TAG, "Failed to switch persistent USB config to " + functions); // revert to previous configuration if we fail SystemProperties.set("persist.sys.usb.config", mDefaultFunctions); } } } else { if (functions == null) { functions = mDefaultFunctions; } // Override with bootmode specific usb mode if needed functions = processOemUsbOverride(functions); if (mAdbEnabled) { functions = addFunction(functions, UsbManager.USB_FUNCTION_ADB); } else { functions = removeFunction(functions, UsbManager.USB_FUNCTION_ADB); } if (!mCurrentFunctions.equals(functions)) { if (!setUsbConfig("none")) { Slog.e(TAG, "Failed to disable USB"); // revert to previous configuration if we fail setUsbConfig(mCurrentFunctions); return; } if (setUsbConfig(functions)) { mCurrentFunctions = functions; } else { Slog.e(TAG, "Failed to switch USB config to " + functions); // revert to previous configuration if we fail setUsbConfig(mCurrentFunctions); } } } } private void updateCurrentAccessory() { // We are entering accessory mode if we have received a request from the host // and the request has not timed out yet. boolean enteringAccessoryMode = mAccessoryModeRequestTime > 0 && SystemClock.elapsedRealtime() < mAccessoryModeRequestTime + ACCESSORY_REQUEST_TIMEOUT; if (mConfigured && enteringAccessoryMode) { // successfully entered accessory mode if (mAccessoryStrings != null) { mCurrentAccessory = new UsbAccessory(mAccessoryStrings); Slog.d(TAG, "entering USB accessory mode: " + mCurrentAccessory); // defer accessoryAttached if system is not ready if (mBootCompleted) { getCurrentSettings().accessoryAttached(mCurrentAccessory); } // else handle in mBootCompletedReceiver } else { Slog.e(TAG, "nativeGetAccessoryStrings failed"); } } else if (!enteringAccessoryMode) { // make sure accessory mode is off // and restore default functions Slog.d(TAG, "exited USB accessory mode"); setEnabledFunctions(getDefaultFunctions(), false); if (mCurrentAccessory != null) { if (mBootCompleted) { getCurrentSettings().accessoryDetached(mCurrentAccessory); } mCurrentAccessory = null; mAccessoryStrings = null; } } } private void updateUsbState() { // send a sticky broadcast containing current USB state Intent intent = new Intent(UsbManager.ACTION_USB_STATE); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.putExtra(UsbManager.USB_CONNECTED, mConnected); intent.putExtra(UsbManager.USB_CONFIGURED, mConfigured); if (mCurrentFunctions != null) { String[] functions = mCurrentFunctions.split(","); for (int i = 0; i < functions.length; i++) { intent.putExtra(functions[i], true); } } if (DEBUG) Slog.d(TAG, "broadcasting " + intent + " connected: " + mConnected + " configured: " + mConfigured); mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); } private void updateAudioSourceFunction() { boolean enabled = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_AUDIO_SOURCE); if (enabled != mAudioSourceEnabled) { // send a sticky broadcast containing current USB state Intent intent = new Intent(AudioManager.ACTION_USB_AUDIO_ACCESSORY_PLUG); intent.addFlags(Intent.FLAG_RECEIVER_REPLACE_PENDING); intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); intent.putExtra("state", (enabled ? 1 : 0)); if (enabled) { Scanner scanner = null; try { scanner = new Scanner(new File(AUDIO_SOURCE_PCM_PATH)); int card = scanner.nextInt(); int device = scanner.nextInt(); intent.putExtra("card", card); intent.putExtra("device", device); } catch (FileNotFoundException e) { Slog.e(TAG, "could not open audio source PCM file", e); } finally { if (scanner != null) { scanner.close(); } } } mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL); mAudioSourceEnabled = enabled; } } @Override public void handleMessage(Message msg) { switch (msg.what) { case MSG_UPDATE_STATE: mConnected = (msg.arg1 == 1); mConfigured = (msg.arg2 == 1); updateUsbNotification(); updateAdbNotification(); if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { updateCurrentAccessory(); } else if (!mConnected) { // restore defaults when USB is disconnected setEnabledFunctions(getDefaultFunctions(), false); } if (mBootCompleted) { updateUsbState(); updateAudioSourceFunction(); } break; case MSG_ENABLE_ADB: setAdbEnabled(msg.arg1 == 1); break; case MSG_SET_CURRENT_FUNCTIONS: String functions = (String)msg.obj; boolean makeDefault = (msg.arg1 == 1); setEnabledFunctions(functions, makeDefault); break; case MSG_SYSTEM_READY: updateUsbNotification(); updateAdbNotification(); updateUsbState(); updateAudioSourceFunction(); break; case MSG_BOOT_COMPLETED: mBootCompleted = true; if (mCurrentAccessory != null) { getCurrentSettings().accessoryAttached(mCurrentAccessory); } if (mDebuggingManager != null) { mDebuggingManager.setAdbEnabled(mAdbEnabled); } break; case MSG_USER_SWITCHED: { UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); UserHandle userHandle = new UserHandle(msg.arg1); if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, userHandle)) { Slog.v(TAG, "Switched to user " + msg.arg1 + " with DISALLOW_USB_FILE_TRANSFER restriction; disabling USB."); setUsbConfig("none"); mCurrentUser = msg.arg1; break; } final boolean mtpActive = containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP) || containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP); if (mtpActive && mCurrentUser != UserHandle.USER_NULL) { Slog.v(TAG, "Current user switched; resetting USB host stack for MTP"); setUsbConfig("none"); setUsbConfig(mCurrentFunctions); } mCurrentUser = msg.arg1; break; } } } public UsbAccessory getCurrentAccessory() { return mCurrentAccessory; } private void updateUsbNotification() { if (mNotificationManager == null || !mUseUsbNotification) return; int id = 0; Resources r = mContext.getResources(); if (mConnected) { if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MTP)) { id = com.android.internal.R.string.usb_mtp_notification_title; } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_PTP)) { id = com.android.internal.R.string.usb_ptp_notification_title; } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_MASS_STORAGE)) { id = com.android.internal.R.string.usb_cd_installer_notification_title; } else if (containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_ACCESSORY)) { id = com.android.internal.R.string.usb_accessory_notification_title; } else { // There is a different notification for USB tethering so we don't need one here //if (!containsFunction(mCurrentFunctions, UsbManager.USB_FUNCTION_RNDIS)) { // Slog.e(TAG, "No known USB function in updateUsbNotification"); //} } } if (id != mUsbNotificationId) { // clear notification if title needs changing if (mUsbNotificationId != 0) { mNotificationManager.cancelAsUser(null, mUsbNotificationId, UserHandle.ALL); mUsbNotificationId = 0; } if (id != 0) { CharSequence message = r.getText( com.android.internal.R.string.usb_notification_message); CharSequence title = r.getText(id); Notification notification = new Notification(); notification.icon = com.android.internal.R.drawable.stat_sys_data_usb; notification.when = 0; notification.flags = Notification.FLAG_ONGOING_EVENT; notification.tickerText = title; notification.defaults = 0; // please be quiet notification.sound = null; notification.vibrate = null; notification.priority = Notification.PRIORITY_MIN; Intent intent = Intent.makeRestartActivityTask( new ComponentName("com.android.settings", "com.android.settings.UsbSettings")); PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); notification.color = mContext.getResources().getColor( com.android.internal.R.color.system_notification_accent_color); notification.setLatestEventInfo(mContext, title, message, pi); notification.visibility = Notification.VISIBILITY_PUBLIC; mNotificationManager.notifyAsUser(null, id, notification, UserHandle.ALL); mUsbNotificationId = id; } } } private void updateAdbNotification() { if (mNotificationManager == null) return; final int id = com.android.internal.R.string.adb_active_notification_title; if (mAdbEnabled && mConnected) { if ("0".equals(SystemProperties.get("persist.adb.notify"))) return; if (!mAdbNotificationShown) { Resources r = mContext.getResources(); CharSequence title = r.getText(id); CharSequence message = r.getText( com.android.internal.R.string.adb_active_notification_message); Notification notification = new Notification(); notification.icon = com.android.internal.R.drawable.stat_sys_adb; notification.when = 0; notification.flags = Notification.FLAG_ONGOING_EVENT; notification.tickerText = title; notification.defaults = 0; // please be quiet notification.sound = null; notification.vibrate = null; notification.priority = Notification.PRIORITY_LOW; Intent intent = Intent.makeRestartActivityTask( new ComponentName("com.android.settings", "com.android.settings.DevelopmentSettings")); PendingIntent pi = PendingIntent.getActivityAsUser(mContext, 0, intent, 0, null, UserHandle.CURRENT); notification.color = mContext.getResources().getColor( com.android.internal.R.color.system_notification_accent_color); notification.setLatestEventInfo(mContext, title, message, pi); notification.visibility = Notification.VISIBILITY_PUBLIC; mAdbNotificationShown = true; mNotificationManager.notifyAsUser(null, id, notification, UserHandle.ALL); } } else if (mAdbNotificationShown) { mAdbNotificationShown = false; mNotificationManager.cancelAsUser(null, id, UserHandle.ALL); } } private String getDefaultFunctions() { UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE); if (userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, new UserHandle(mCurrentUser))) { return "none"; } return mDefaultFunctions; } public void dump(FileDescriptor fd, PrintWriter pw) { pw.println(" USB Device State:"); pw.println(" Current Functions: " + mCurrentFunctions); pw.println(" Default Functions: " + mDefaultFunctions); pw.println(" mConnected: " + mConnected); pw.println(" mConfigured: " + mConfigured); pw.println(" mCurrentAccessory: " + mCurrentAccessory); try { pw.println(" Kernel state: " + FileUtils.readTextFile(new File(STATE_PATH), 0, null).trim()); pw.println(" Kernel function list: " + FileUtils.readTextFile(new File(FUNCTIONS_PATH), 0, null).trim()); pw.println(" Mass storage backing file: " + FileUtils.readTextFile(new File(MASS_STORAGE_FILE_PATH), 0, null).trim()); } catch (IOException e) { pw.println("IOException: " + e); } } } /* returns the currently attached USB accessory */ public UsbAccessory getCurrentAccessory() { return mHandler.getCurrentAccessory(); } /* opens the currently attached USB accessory */ public ParcelFileDescriptor openAccessory(UsbAccessory accessory) { UsbAccessory currentAccessory = mHandler.getCurrentAccessory(); if (currentAccessory == null) { throw new IllegalArgumentException("no accessory attached"); } if (!currentAccessory.equals(accessory)) { String error = accessory.toString() + " does not match current accessory " + currentAccessory; throw new IllegalArgumentException(error); } getCurrentSettings().checkPermission(accessory); return nativeOpenAccessory(); } public void setCurrentFunctions(String functions, boolean makeDefault) { if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ") default: " + makeDefault); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions, makeDefault); } public void setMassStorageBackingFile(String path) { if (path == null) path = ""; try { FileUtils.stringToFile(MASS_STORAGE_FILE_PATH, path); } catch (IOException e) { Slog.e(TAG, "failed to write to " + MASS_STORAGE_FILE_PATH); } } private void readOemUsbOverrideConfig() { String[] configList = mContext.getResources().getStringArray( com.android.internal.R.array.config_oemUsbModeOverride); if (configList != null) { for (String config: configList) { String[] items = config.split(":"); if (items.length == 3) { if (mOemModeMap == null) { mOemModeMap = new HashMap>>(); } List> overrideList = mOemModeMap.get(items[0]); if (overrideList == null) { overrideList = new LinkedList>(); mOemModeMap.put(items[0], overrideList); } overrideList.add(new Pair(items[1], items[2])); } } } } private boolean needsOemUsbOverride() { if (mOemModeMap == null) return false; String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown"); return (mOemModeMap.get(bootMode) != null) ? true : false; } private String processOemUsbOverride(String usbFunctions) { if ((usbFunctions == null) || (mOemModeMap == null)) return usbFunctions; String bootMode = SystemProperties.get(BOOT_MODE_PROPERTY, "unknown"); List> overrides = mOemModeMap.get(bootMode); if (overrides != null) { for (Pair pair: overrides) { if (pair.first.equals(usbFunctions)) { Slog.d(TAG, "OEM USB override: " + pair.first + " ==> " + pair.second); return pair.second; } } } // return passed in functions as is. return usbFunctions; } public void allowUsbDebugging(boolean alwaysAllow, String publicKey) { if (mDebuggingManager != null) { mDebuggingManager.allowUsbDebugging(alwaysAllow, publicKey); } } public void denyUsbDebugging() { if (mDebuggingManager != null) { mDebuggingManager.denyUsbDebugging(); } } public void clearUsbDebuggingKeys() { if (mDebuggingManager != null) { mDebuggingManager.clearUsbDebuggingKeys(); } else { throw new RuntimeException("Cannot clear Usb Debugging keys, " + "UsbDebuggingManager not enabled"); } } public void dump(FileDescriptor fd, PrintWriter pw) { if (mHandler != null) { mHandler.dump(fd, pw); } if (mDebuggingManager != null) { mDebuggingManager.dump(fd, pw); } } private native String[] nativeGetAccessoryStrings(); private native ParcelFileDescriptor nativeOpenAccessory(); private native boolean nativeIsStartRequested(); private native int nativeGetAudioMode(); }