Android 广播监听USB插拔
16lz
2021-01-23
- AndroidManifest.xml
<uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" /><receiver android:name=".receiver.UsbReceiver"> <intent-filter> <action android:name="android.intent.action.MEDIA_MOUNTED" /> <action android:name="android.intent.action.MEDIA_UNMOUNTED" /> <data android:scheme="file" /> </intent-filter></receiver>
- UsbReceiver.java
public class UsbReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { UsbManager.getInstance().usbObserveReceive(context, intent, Intent.ACTION_MEDIA_MOUNTED.equals(intent.getAction()) ? Constants.USB_TYPE_ATTACH : Constants.USB_TYPE_DETACH); }}
- UsbInfo.java
public class UsbInfo { // uuid public String uuid; // uri public Uri uri; // u盘类型 public String fsType; // u盘名称 public String fsLabel; // u盘路径 public String path; // u盘路径 public String internalPath; // 已格式化显示的可用空间大小 public String availableSize; // 已格式化显示的总空间大小 public String totalSize; @Override public String toString() { return "UsbInfo{" + "uuid='" + uuid + '\'' + ", uri=" + uri + ", fsType='" + fsType + '\'' + ", fsLabel='" + fsLabel + '\'' + ", path='" + path + '\'' + ", internalPath='" + internalPath + '\'' + ", availableSize='" + availableSize + '\'' + ", totalSize='" + totalSize + '\'' + '}'; }}
- UsbObserveType .java
public class Constants {public static final int USB_TYPE_ATTACH = 0x3001;public static final int USB_TYPE_DETACH = 0x3002;}@IntDef(flag = true, value = { Constants.USB_TYPE_ATTACH, Constants.USB_TYPE_DETACH})public @interface UsbObserveType {}
- UsbManager.java
public class UsbManager { private static final String TAG = "UsbManager"; private Set<UsbInfo> mUsbInfos = new LinkedHashSet<>(); private List<OnUsbReceiveListener> mOnUsbReceiveListeners = new LinkedList<>(); private static class UsbManagerHolder { private static final UsbManager INSTANCE = new UsbManager(); } public static UsbManager getInstance() { return UsbManagerHolder.INSTANCE; } public Set<UsbInfo> getUsbInfos(Context context) { if (mUsbInfos.size() == 0) return queryUsbInfos(context); return mUsbInfos; } public void registerUsbReceiveListener(OnUsbReceiveListener listener) { mOnUsbReceiveListeners.add(listener); } public void unregisterUsbReceiveListener(OnUsbReceiveListener listener) { mOnUsbReceiveListeners.remove(listener); } public void usbObserveReceive(Context context, Intent intent, @UsbObserveType int usbObserveType) { Set<UsbInfo> tUsbInfos = queryUsbInfos(context); UsbInfo attachUsbInfo = new UsbInfo(); if (usbObserveType == Constants.USB_TYPE_ATTACH) { for (UsbInfo usbInfo : tUsbInfos) { if (intent.getData().getPath().equals(usbInfo.path)) { usbInfo.uri = intent.getData(); attachUsbInfo = usbInfo; mUsbInfos.add(attachUsbInfo); break; } } } else { for (UsbInfo usbInfo : mUsbInfos) { if (usbInfo.uri.equals(intent.getData())) { attachUsbInfo = usbInfo; mUsbInfos.remove(attachUsbInfo); break; } } } Log.i(TAG, "intent = " + intent + ", usbInfos = " + mUsbInfos); for (OnUsbReceiveListener listener : mOnUsbReceiveListeners) { if (listener != null) { listener.onUsbReceive(usbObserveType, mUsbInfos, attachUsbInfo); } } } public Set<UsbInfo> queryUsbInfos(Context context) { Set<UsbInfo> usbInfos = new LinkedHashSet<>(); try { StorageManager sm = (StorageManager) context.getSystemService(Context.STORAGE_SERVICE); Class clazz = Class.forName("android.os.storage.StorageManager"); Method methodGetVolumes = clazz.getMethod("getVolumes"); List<?> volumeInfos = (List<?>) methodGetVolumes.invoke(sm); @SuppressLint("PrivateApi") Class volumeInfoClazz = Class.forName("android.os.storage.VolumeInfo"); Method methodGetFsUUID = volumeInfoClazz.getMethod("getFsUuid"); Field fieldFsType = volumeInfoClazz.getDeclaredField("fsType"); Field fieldFsLabelField = volumeInfoClazz.getDeclaredField("fsLabel"); Field fieldPath = volumeInfoClazz.getDeclaredField("path"); Field fieldInternalPath = volumeInfoClazz.getDeclaredField("internalPath"); if (volumeInfos != null) { for (Object volumeInfo : volumeInfos) { String uuid = (String) methodGetFsUUID.invoke(volumeInfo); if (uuid != null) { UsbInfo usbInfo = new UsbInfo(); usbInfo.uuid = uuid; usbInfo.fsType = (String) fieldFsType.get(volumeInfo); usbInfo.fsLabel = (String) fieldFsLabelField.get(volumeInfo); usbInfo.path = (String) fieldPath.get(volumeInfo); usbInfo.internalPath = (String) fieldInternalPath.get(volumeInfo); try { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { StatFs statFs = new StatFs(usbInfo.path); usbInfo.availableSize = Formatter.formatFileSize(context, statFs.getAvailableBytes()); usbInfo.totalSize = Formatter.formatFileSize(context, statFs.getTotalBytes()); } } catch (IllegalArgumentException e) { e.printStackTrace(); } usbInfos.add(usbInfo); } } } } catch (Exception e) { e.printStackTrace(); } return usbInfos; } public interface OnUsbReceiveListener { void onUsbReceive(@UsbObserveType int usbObserveType, Set<UsbInfo> usbInfos, UsbInfo currUsbInfo); }}
- 使用
public class TestActivity extends AppCompatActivity implements UsbManager.OnUsbReceiveListener { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); UsbManager.getInstance().registerUsbReceiveListener(this); } @Override protected void onDestroy() { UsbManager.getInstance().unregisterUsbReceiveListener(this); super.onDestroy(); }@Override public void onUsbReceive(int usbObserveType, Set<UsbInfo> usbInfos, UsbInfo currUsbInfo) { // 接收处理 }}
注意事项:
在实际项目开发过程中,比如在TV机顶盒开发,使用以上方法是可以较为准确的监听到USB的插拔,但是如果尝试将数据写入USB,将会出现无法写入提示 permission denied
,这个时候需要去修改源码。
从源码中定位到 frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
,搜索到 systemReady()
这个方法,加入以下几句代码:
StorageManagerInternal StorageManagerInternal = LocalServices.getService(StorageManagerInternal.class);StorageManagerInternal.addExternalStoragePolicy( new StorageManagerInternal.ExternalStorageMountPolicy() { @Override public int getMountMode(int uid, String packageName) { if (Process.isIsolated(uid)) { return Zygote.MOUNT_EXTERNAL_NONE; } // 添加允许写权限---开始if (checkUidPermission(WRITE_MEDIA_STORAGE, uid) == PERMISSION_GRANTED) { return Zygote.MOUNT_EXTERNAL_DEFAULT; } // 添加允许写权限---结束 if (checkUidPermission(READ_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) { return Zygote.MOUNT_EXTERNAL_DEFAULT; } if (checkUidPermission(WRITE_EXTERNAL_STORAGE, uid) == PERMISSION_DENIED) { return Zygote.MOUNT_EXTERNAL_READ; } return Zygote.MOUNT_EXTERNAL_WRITE; } @Override public boolean hasExternalStorage(int uid, String packageName) { return true; }});
然后在app的 AndroidManifest.xml
加入权限即可:
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE" />
更多相关文章
- Android 权限管理框架
- Android之SharedPreferences权限
- I2C-Tools 4.0使用说明及android平台移植,提供源码下载路径
- android 获取屏幕尺寸大小
- Android PopupWindow显示位置和显示大小
- 基于Android系统软件管理器源码
- android仿苹果Iphone桌面源码
- PointerLocationView 源码分析
- Android 开发艺术探索 源码地址