編輯:關於Android編程
最近做一個項目,主要通過usb完成pc與Android端的數據傳輸。但是根據api提供的無法監聽usb的插拔,有解釋為不同版本會存在BUG。本打算放棄跳過監聽usb,改為在連上usb後pc點擊按鈕發出一個廣播來主動打開Android端的應用程序,然後通過socket完成數據交互。這裡主要通過UEventObserver,而該類位於/frameworks/base/core/java/android/os/UEventObserver.java中,位於framework框架。需要手動導入編譯好的frameworks,下載frameworks.jar。
在工程的Build Path 中,Add External JARs中選擇frameworks.jar,然後需要最好配置Order中將frameworks.jar置前,對於定制的frameworks也可以這樣導入。
public class MyService extends Service { private final static String TAG = SMSService.class.getSimpleName(); private UsbUEventObserver usbObserver = null; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); LogUtil.d(TAG, onCreate()); usbObserver = new UsbUEventObserver(); usbObserver.startObserving(USB_STATE_MATCH); usbObserver.startObserving(ACCESSORY_START_MATCH); } public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null) { // } return super.onStartCommand(intent, flags, startId); } @Override public void onDestroy() { LogUtil.d(TAG, onDestroy()); if (usbObserver != null) { usbObserver.stopObserving(); } super.onDestroy(); } 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 class UsbUEventObserver extends UEventObserver { @Override public void onUEvent(final UEventObserver.UEvent event) { LogUtil.i(TAG, usb: + event.toString()); final String state = event.get(USB_STATE); final String accessory = event.get(ACCESSORY); if (state != null) { // mHandler.updateState(state); if (state.equalsIgnoreCase(CONNECTED)) { Intent intent2 = new Intent(getApplicationContext(), MainActivity.class); intent2.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); getApplicationContext().startActivity(intent2); BroadUtil.sendBroadcastConnected(getApplicationContext(), Constants.ConnectedType.USB_CONNECTED); } else if (state.toUpperCase().contains(DISCONNECTED)) { BroadUtil.sendBroadcastConnected(getApplicationContext(), Constants.ConnectedType.USB_DISCONNECTED); } } else if (START.equals(accessory)) { // startAccessoryMode(); } } }; }
/** * 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 int MSG_UPDATE_STATE = 0; private static final int MSG_ENABLE_ADB = 1; private static final int MSG_SET_CURRENT_FUNCTION = 2; private static final int MSG_SYSTEM_READY = 3; private static final int MSG_BOOT_COMPLETED = 4; // 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; private UsbHandler mHandler; private boolean mBootCompleted; private final Context mContext; private final ContentResolver mContentResolver; private final UsbSettingsManager mSettingsManager; private NotificationManager mNotificationManager; private final boolean mHasUsbAccessory; private boolean mUseUsbNotification; private boolean mAdbEnabled; private class AdbSettingsObserver extends ContentObserver { public AdbSettingsObserver() { super(null); } @Override public void onChange(boolean selfChange) { boolean enable = (Settings.Secure.getInt(mContentResolver, Settings.Secure.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); setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); } } }; public UsbDeviceManager(Context context, UsbSettingsManager settingsManager) { mContext = context; mContentResolver = context.getContentResolver(); mSettingsManager = settingsManager; PackageManager pm = mContext.getPackageManager(); mHasUsbAccessory = pm.hasSystemFeature(PackageManager.FEATURE_USB_ACCESSORY); initRndisAddress(); // create a thread for our Handler HandlerThread thread = new HandlerThread(UsbDeviceManager, Process.THREAD_PRIORITY_BACKGROUND); thread.start(); mHandler = new UsbHandler(thread.getLooper()); if (nativeIsStartRequested()) { if (DEBUG) Slog.d(TAG, accessory attached at boot); setCurrentFunction(UsbManager.USB_FUNCTION_ACCESSORY, false); } } 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; StorageManager storageManager = (StorageManager) mContext.getSystemService(Context.STORAGE_SERVICE); StorageVolume[] volumes = storageManager.getVolumeList(); if (volumes.length > 0) { massStorageSupported = volumes[0].allowMassStorage(); } mUseUsbNotification = !massStorageSupported; // make sure the ADB_ENABLED setting value matches the current state Settings.Secure.putInt(mContentResolver, Settings.Secure.ADB_ENABLED, mAdbEnabled ? 1 : 0); mHandler.sendEmptyMessage(MSG_SYSTEM_READY); } 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(%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 (!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; } } 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) { int index = functions.indexOf(function); if (index < 0) return false; if (index > 0 && functions.charAt(index - 1) != ',') return false; int charAfter = index + function.length(); if (charAfter < functions.length() && functions.charAt(charAfter) != ',') return false; return true; } 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 final BroadcastReceiver mBootCompletedReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { if (DEBUG) Slog.d(TAG, boot completed); mHandler.sendEmptyMessage(MSG_BOOT_COMPLETED); } }; 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); // 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 = mDefaultFunctions; 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.Secure.getUriFor(Settings.Secure.ADB_ENABLED), false, new AdbSettingsObserver()); // Watch for USB configuration changes mUEventObserver.startObserving(USB_STATE_MATCH); mUEventObserver.startObserving(ACCESSORY_START_MATCH); mContext.registerReceiver(mBootCompletedReceiver, new IntentFilter(Intent.ACTION_BOOT_COMPLETED)); } 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; try { // try again in 50ms Thread.sleep(50); } catch (InterruptedException e) { } } 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 // switching to default function setEnabledFunctions(mDefaultFunctions, true); updateAdbNotification(); } } private void setEnabledFunctions(String functions, boolean makeDefault) { if (functions != null && makeDefault) { 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; } 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() { if (!mHasUsbAccessory) return; if (mConfigured) { String[] strings = nativeGetAccessoryStrings(); if (strings != null) { mCurrentAccessory = new UsbAccessory(strings); Slog.d(TAG, entering USB accessory mode: + mCurrentAccessory); // defer accessoryAttached if system is not ready if (mBootCompleted) { mSettingsManager.accessoryAttached(mCurrentAccessory); } // else handle in mBootCompletedReceiver } else { Slog.e(TAG, nativeGetAccessoryStrings failed); } } else if (!mConnected) { // make sure accessory mode is off // and restore default functions Slog.d(TAG, exited USB accessory mode); setEnabledFunctions(mDefaultFunctions, false); if (mCurrentAccessory != null) { if (mBootCompleted) { mSettingsManager.accessoryDetached(mCurrentAccessory); } mCurrentAccessory = 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); } } mContext.sendStickyBroadcast(intent); } @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(); } if (!mConnected) { // restore defaults when USB is disconnected setEnabledFunctions(mDefaultFunctions, false); } if (mBootCompleted) { updateUsbState(); } break; case MSG_ENABLE_ADB: setAdbEnabled(msg.arg1 == 1); break; case MSG_SET_CURRENT_FUNCTION: String function = (String)msg.obj; boolean makeDefault = (msg.arg1 == 1); setEnabledFunctions(function, makeDefault); break; case MSG_SYSTEM_READY: updateUsbNotification(); updateAdbNotification(); updateUsbState(); break; case MSG_BOOT_COMPLETED: mBootCompleted = true; if (mCurrentAccessory != null) { mSettingsManager.accessoryAttached(mCurrentAccessory); } 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.cancel(mUsbNotificationId); 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; Intent intent = Intent.makeRestartActivityTask( new ComponentName(com.android.settings, com.android.settings.UsbSettings)); PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); notification.setLatestEventInfo(mContext, title, message, pi); mNotificationManager.notify(id, notification); 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; Intent intent = Intent.makeRestartActivityTask( new ComponentName(com.android.settings, com.android.settings.DevelopmentSettings)); PendingIntent pi = PendingIntent.getActivity(mContext, 0, intent, 0); notification.setLatestEventInfo(mContext, title, message, pi); mAdbNotificationShown = true; mNotificationManager.notify(id, notification); } } else if (mAdbNotificationShown) { mAdbNotificationShown = false; mNotificationManager.cancel(id); } } 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); } mSettingsManager.checkPermission(accessory); return nativeOpenAccessory(); } public void setCurrentFunction(String function, boolean makeDefault) { if (DEBUG) Slog.d(TAG, setCurrentFunction( + function + ) default: + makeDefault); mHandler.sendMessage(MSG_SET_CURRENT_FUNCTION, function, 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); } } public void dump(FileDescriptor fd, PrintWriter pw) { if (mHandler != null) { mHandler.dump(fd, pw); } } private native String[] nativeGetAccessoryStrings(); private native ParcelFileDescriptor nativeOpenAccessory(); private native boolean nativeIsStartRequested(); }
在《Android Handler之消息循環的深入解析》中談到了Handler是用於操作線程內部的消息隊列,所以Handler可以用來線程間通信ITC,這種方式更加安全和
前言:RecyclerView出現已經有一段時間了,我們肯定不陌生了,可以通過導入support-v7對其進行使用。根據官方的文檔,該控件用於在有限的窗口中展示大量數據集
1 基於Socket的Android聊天室Socket通信是網絡通信中最常用的技術之一,通過Socket建立的可靠連接,可以讓多個終端與服務器保持通信,最典型的應用是建立
Android Studio + Gradle的組合用起來非常方便,很多第三方開源項目也早都遷移到了Studio,為此今天就來介紹下查看、編譯並導入第三方開源項目的方法。