<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>
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);
}
}
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 + '\'' +
'}';
}
}
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 {
}
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" />