Android USB默认连接模式为MTP

作者: Nearby杨 | 来源:发表于2017-06-14 09:37 被阅读2158次

    很多时候,手机项目开发,客户都要求配置USB的默认连接方式,但是在Android 6.0以及之后的版本就直接配置USB连接模式,看到的USB连接模式还是仅充电,而这是google的默认设计。那么对于这个问题,也看了很多网上的一些解法,如:Android 5.0可以直接配置默认值,6.0就不可行了。另外,还有在USB连接的时候进行设置,当连接之后就执行一次设置USB连接模式,将MTP设置为当前连接模式,这样的做法在7.0上面也是可以的。

    事实上,当仅充电的时候,查看当前USB模式的配置,就算是mtp,但是还是没有显示SD卡和内部存储器,主要是一个标志的问题。

    现有的一种解法

    那么这里先说一下在连接USB的情况下,执行一次USB连接模式的设置,这里有一位大牛的方法,并且提到Android 6.0以前的做法,Android 6.0 USB连接模式默认选为MTP ,大家去参考学习一下,那么我说一下这位大牛的改法,在Android 6.0和7.0上面修改后不同的一个地方:

    Android版本 在锁屏的情况下 在解锁的情况下
    6.0 仅充电 MTP
    7.0 MTP MTP

    对于这样的解法,我们的测试就提了一个不安全的bug,没有解锁就能直接访问SD卡、内部存储器的数据,这是不安全的。那好像也说得有道理,那么就改。但是这个不解锁能访问存储器的功能,可能又符合某些公司的客户需求。

    公司中6.0的解法

    对于上面的解法不怎么像6.0的行为,接着我就去看了一下,我们公司以前Android 6.0是怎么修改的,是直接修改了值,将这个值修改后,就能显示SD卡、内部存储器了。

    修改文件frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

    public class UsbDeviceManager {
                      ···省略很多代码···
            private boolean mConfigured;
            //modified in 2017-05-22 start
            //解锁数据,连接电脑,就能看到默认连接模式为MTP
            private boolean mUsbDataUnlocked = true;
            //modified in 2017-05-22 end
            private String mCurrentFunctions;
            private String mDefaultFunctions;
            
                    ······
                    
             @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_UPDATE_STATE:
                        mConnected = (msg.arg1 == 1);
                        mConfigured = (msg.arg2 == 1);
                        mUsbConfigured = mConfigured;
                        if (!mConnected) {
                            //modified in 2017-05-22 start
                            //default usb connect mode as mtp
                            // When a disconnect occurs, relock access to sensitive user data
                            //断开连接的时候,保持当前连接模式,下次连接的时候还是MTP
                            mUsbDataUnlocked = true;
                            //modified in 2017-05-22 end
                        }
                        updateUsbNotification();
                        updateAdbNotification();
                        if (UsbManager.containsFunction(mCurrentFunctions,
                                UsbManager.USB_FUNCTION_ACCESSORY)) {
                            updateCurrentAccessory();
                        } else if (!mConnected) {
                            //modified  in 2017-05-22 start
                            //change default usb connect mode as mtp,do not restore
                            // 这里不恢复默认连接方式,保持保持当前的连接模式
                            setEnabledFunctions(null, true);
                            //modified  in 2017-05-22 end
                        }
                        if (mBootCompleted) {
                            updateUsbStateBroadcastIfNeeded();
                            updateUsbFunctions();
                        }
                          ······
        }
    
    

    上面的这种修改方式比较简单,在android6.0和7.0上都是可以的,但是还是有差异:

    Android版本 在锁屏的情况下 在解锁的情况下
    6.0 仅充电 MTP
    7.0 MTP MTP

    这中修改方式的最终结果还是跟上面那位大牛的修改方式表现结果一致。那么应该说是Google在7.0上又修改了这一部分的代码,使用公司以前6.0的改法,还是不行。

    现在Android 7.0的解法

    但是还是对不上公司测试提的问题,那还是需要接续修改。那就仔细看看这类,再上网了解一下USB连接模式这块,这一块还是很深的...,涉及硬件的,都是稍微复杂一点,还要跟底层通信什么的,有一点大概的了解之后,再回来解决一下这个问题,那么从切换USB连接模式的上层实现,那么就是修改mUsbDataUnlocked的值,应该说是在适当的时候修改mUsbDataUnlocked的值,那就看到USB连接模式是MTP,那么下面分析一下有几种情况需要改变:

    1. 手机是锁屏情况下连接USB,连接模式是仅充电。
    2. 手机是解锁的情况下连接USB,连接模式是MTP。
    3. 手机熄屏的情况下断开USB,连接方式要更新为仅充电。
    4. 手机解锁的情况下断开USB,连接方式要更新为仅充电。

    应该就是这四种情况,那么下面主要要解决的问题是:

    1. 用户解锁了,通过主测广播:Intent.ACTION_USER_PRESENT判断是否解锁后用户到前台。
    2. USB连接状态有对应的回调,并且这个时候更新USB连接模式。
    3. 熄屏了,通过注册广播Intent.ACTION_SCREEN_OFF判断,还需要注册广播Intent.ACTION_SCREEN_ON,作为屏幕状态值的切换。

    在UsbService.java类中注册以上说到的广播接收者,再对UsbDeviceManager.java类中的标识进行更新。

    1.修改frameworks/base/services/usb/java/com/android/server/usb/UsbService.java

    public class UsbService extends IUsbManager.Stub {
                    ······
            final IntentFilter filter = new IntentFilter();
            filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
            filter.addAction(Intent.ACTION_USER_SWITCHED);
            filter.addAction(Intent.ACTION_USER_STOPPED);
            //add by xx in 2017-06-12 for bug 169853 start
            //注册三个广播
            filter.addAction(Intent.ACTION_USER_PRESENT);
            filter.addAction(Intent.ACTION_SCREEN_OFF);
            filter.addAction(Intent.ACTION_SCREEN_ON);
            //add by xx in 2017-06-12 for bug 169853 end
            filter.addAction(DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
            mContext.registerReceiver(mReceiver, filter, null, null);
            
                    ······
    
        private BroadcastReceiver mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
                final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
                final String action = intent.getAction();
                if (Intent.ACTION_USER_SWITCHED.equals(action)) {
                    setCurrentUser(userId);
                } else if (Intent.ACTION_USER_STOPPED.equals(action)) {
                    synchronized (mLock) {
                        mSettingsByUser.remove(userId);
                    }
                } else if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED
                        .equals(action)) {
                    if (mDeviceManager != null) {
                        mDeviceManager.updateUserRestrictions();
                    }
                //add by xx in 2017-06-12 for bug 169853 start
                //处理三个广播
                }else if (Intent.ACTION_USER_PRESENT.equals(action)) {
    
                    if (mDeviceManager != null) {
                        mDeviceManager.usbDataUnlocked(true);//这个方法系统有的
                        mDeviceManager.setUserPresent();//这个方法是添加的
                    }
                } else if (Intent.ACTION_SCREEN_OFF.equals(action)) {
                    if (mDeviceManager != null) {
                        mDeviceManager.updateScreentSate(true);//这个方法是添加的,为了更新屏幕状态
                    }
                } else if (Intent.ACTION_SCREEN_ON.equals(action)) {
                  if (mDeviceManager != null) {
                        mDeviceManager.updateScreentSate(false);//这个方法是添加的,为了更新屏幕状态
                    }
                //add by xx in 2017-06-12 for bug 169853 end
                }
            }
        };
    }
    

    2.接下来修改frameworks/base/services/usb/java/com/android/server/usb/UsbDeviceManager.java

    public class UsbDeviceManager {
            ·····
        //添加两个全局变量,作为标识
        //modified by xx in 2017-06-12 for bug 169853 start
        private boolean mUserPresent = false;//用户是否结果到前台
        private boolean screenOff = false;//屏幕是否是熄屏
        private boolean changeByUser = false;//切换USB模式的时候,是这里代码切换的,还是用户点击切换的。
        //modified by xx in 2017-06-12 for bug 169853 end
        
                ·····
        //添加这两个方法,在UsbService.java中用到,更新这边的状态
        //modified by xx in 2017-06-12 for bug 169853 start
        public void setUserPresent(){
            mUserPresent = true;
        }
    
        public void updateScreentSate(boolean state){
            screenOff = state;
            if(!screenOff) {
                 mUserPresent = false;
            }
            if(mHandler != null){
                mHandler.updateUsbMode();
            }
        }
    
        public void usbDataUnlocked(){
            changeByUser  = false;
            mHandler.sendMessage(MSG_SET_USB_DATA_UNLOCKED, true);
        }
        //modified by xx in 2017-06-12 for bug 169853 end
        
                ·····
          
          //更新USB连接状态
          public void updateState(String state) {
                int connected, configured;
    
                if ("HWDISCONNECTED".equals(state)) {
                    connected = 0;
                    configured = 0;
                    mHwDisconnected = true;
                    //add by xx in 2017-08-03 for swtich usb mode start
                    changeByUser = false;
                    //add by xx in 2017-08-03 for swtich usb mode end
                } else if ("DISCONNECTED".equals(state)) {
                    connected = 0;
                    configured = 0;
                    mHwDisconnected = false;
    
                    //modified by xx in 2017-06-12 for bug 169853 start
                    //当熄屏的情况下,更新用户不在前台的标识
                    if(screenOff){
                        mUserPresent = false;
                     }
                     //modified by xxx in 2017-06-12 for bug 169853 end
    
                } else if ("CONNECTED".equals(state)) {
                    connected = 1;
                    configured = 0;
                    
                        ·····
            
            
            private final class UsbHandler extends Handler { 
            
                            ·····
                            
            //在收到更新USB状态的消息之后,更新USB模式,当然要根据用户是否在前台进行判断
             @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_UPDATE_STATE:
                        mConnected = (msg.arg1 == 1);
                        mConfigured = (msg.arg2 == 1);
                        mUsbConfigured = mConfigured;
                        //modified by xx in 2017-06-12 for bug 169853 start
                        //update usb state first
                        updateUsbMode();
                        if (mUserPresent && !changeByUser) {   //用户在前台的
                            mUsbDataUnlocked = true;//解锁数据,那么连接电脑就能看到连接模式为MTP了
                        }
                        //modified by xx in 2017-06-12 for bug 169853 end
    
                        if (!mConnected) {
                            // When a disconnect occurs, relock access to sensitive user data
                            mUsbDataUnlocked = false;
                        }
                        updateUsbNotification();
                        updateAdbNotification();
                        if (UsbManager.containsFunction(mCurrentFunctions,
                                UsbManager.USB_FUNCTION_ACCESSORY)) {
                            updateCurrentAccessory();
                        } else if (!mConnected) {
                        }
                                ·····//省略代码
                    case MSG_SET_USB_DATA_UNLOCKED:
                        //add by xx in 2017-08-03 for swtich usb mode start
                        if(!changeByUser && mUsbDataUnlocked) return;
                        //add by xx in 2017-08-03 for swtich usb mode end
                        setUsbDataUnlocked(msg.arg1 == 1);
                        break;
                    }
                                ·····
                //在UsbHandler类中的方法,主要是因为用到USB状态值:mConnected
                //add by xx in 2017-06-12 for bug 169853 start
                private void updateUsbMode(){
                    if(!mConnected && screenOff){
                        mUserPresent = false;
                    }
                }
                //add by xx in 2017-06-12 for bug 169853 end
            }
                    ·····
       }
       //省略其他代码
       public void setCurrentFunctions(String functions) {
            if (DEBUG) Slog.d(TAG, "setCurrentFunctions(" + functions + ")");
            //add by xx in 2017-08-03 for swtich usb mode start
            changeByUser = true;
            //add by xx in 2017-08-03 for swtich usb mode end
            mHandler.sendMessage(MSG_SET_CURRENT_FUNCTIONS, functions);
        }
        //省略其他代码
    }
    

    主要修改上面两个类,实现以下情况:

    Android版本 在锁屏的情况下 在解锁的情况下
    7.0 仅充电 MTP

    那么这样的行为就跟Android 6.0 的表现一样了,问题可以说已经解决了。
    像这些修改一个默认值的问题还是比较简单的。事实上,对USB完全没有接触,还是要多看,有那么多巨人,就借个肩膀来站站呗,就像本文一开始提到的那位大神一样,在这里表示感谢。

    感谢 SymphonyZhang 的提醒,在Android 7.1中“只要persist.sys.usb.config这个属性的值设为mtp,后面不要加上adb那些什么的,就可以了”

    相关文章

      网友评论

        本文标题:Android USB默认连接模式为MTP

        本文链接:https://www.haomeiwen.com/subject/ppcsqxtx.html