• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.settings.connecteddevice.usb;
18 
19 import static android.hardware.usb.UsbPortStatus.DATA_ROLE_DEVICE;
20 
21 import android.content.Context;
22 import android.hardware.usb.UsbManager;
23 import android.net.TetheringManager;
24 import android.os.Handler;
25 import android.os.HandlerExecutor;
26 import android.util.Log;
27 
28 import androidx.annotation.VisibleForTesting;
29 import androidx.preference.PreferenceCategory;
30 import androidx.preference.PreferenceScreen;
31 
32 import com.android.settings.R;
33 import com.android.settings.Utils;
34 import com.android.settingslib.widget.RadioButtonPreference;
35 
36 import java.util.LinkedHashMap;
37 import java.util.Map;
38 
39 /**
40  * This class controls the radio buttons for choosing between different USB functions.
41  */
42 public class UsbDetailsFunctionsController extends UsbDetailsController
43         implements RadioButtonPreference.OnClickListener {
44 
45     private static final String TAG = "UsbFunctionsCtrl";
46     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
47 
48     static final Map<Long, Integer> FUNCTIONS_MAP = new LinkedHashMap<>();
49 
50     static {
FUNCTIONS_MAP.put(UsbManager.FUNCTION_MTP, R.string.usb_use_file_transfers)51         FUNCTIONS_MAP.put(UsbManager.FUNCTION_MTP, R.string.usb_use_file_transfers);
FUNCTIONS_MAP.put(UsbManager.FUNCTION_RNDIS, R.string.usb_use_tethering)52         FUNCTIONS_MAP.put(UsbManager.FUNCTION_RNDIS, R.string.usb_use_tethering);
FUNCTIONS_MAP.put(UsbManager.FUNCTION_MIDI, R.string.usb_use_MIDI)53         FUNCTIONS_MAP.put(UsbManager.FUNCTION_MIDI, R.string.usb_use_MIDI);
FUNCTIONS_MAP.put(UsbManager.FUNCTION_PTP, R.string.usb_use_photo_transfers)54         FUNCTIONS_MAP.put(UsbManager.FUNCTION_PTP, R.string.usb_use_photo_transfers);
FUNCTIONS_MAP.put(UsbManager.FUNCTION_NONE, R.string.usb_use_charging_only)55         FUNCTIONS_MAP.put(UsbManager.FUNCTION_NONE, R.string.usb_use_charging_only);
56     }
57 
58     private PreferenceCategory mProfilesContainer;
59     private TetheringManager mTetheringManager;
60     private Handler mHandler;
61     @VisibleForTesting
62     OnStartTetheringCallback mOnStartTetheringCallback;
63     @VisibleForTesting
64     long mPreviousFunction;
65 
UsbDetailsFunctionsController(Context context, UsbDetailsFragment fragment, UsbBackend backend)66     public UsbDetailsFunctionsController(Context context, UsbDetailsFragment fragment,
67             UsbBackend backend) {
68         super(context, fragment, backend);
69         mTetheringManager = context.getSystemService(TetheringManager.class);
70         mOnStartTetheringCallback = new OnStartTetheringCallback();
71         mPreviousFunction = mUsbBackend.getCurrentFunctions();
72         mHandler = new Handler(context.getMainLooper());
73     }
74 
75     @Override
displayPreference(PreferenceScreen screen)76     public void displayPreference(PreferenceScreen screen) {
77         super.displayPreference(screen);
78         mProfilesContainer = screen.findPreference(getPreferenceKey());
79         refresh(/* connected */ false, /* functions */ mUsbBackend.getDefaultUsbFunctions(),
80                 /* powerRole */ 0, /* dataRole */ 0);
81     }
82 
83     /**
84      * Gets a switch preference for the particular option, creating it if needed.
85      */
getProfilePreference(String key, int titleId)86     private RadioButtonPreference getProfilePreference(String key, int titleId) {
87         RadioButtonPreference pref = mProfilesContainer.findPreference(key);
88         if (pref == null) {
89             pref = new RadioButtonPreference(mProfilesContainer.getContext());
90             pref.setKey(key);
91             pref.setTitle(titleId);
92             pref.setSingleLineTitle(false);
93             pref.setOnClickListener(this);
94             mProfilesContainer.addPreference(pref);
95         }
96         return pref;
97     }
98 
99     @Override
refresh(boolean connected, long functions, int powerRole, int dataRole)100     protected void refresh(boolean connected, long functions, int powerRole, int dataRole) {
101         if (DEBUG) {
102             Log.d(TAG, "refresh() connected : " + connected + ", functions : " + functions
103                     + ", powerRole : " + powerRole + ", dataRole : " + dataRole);
104         }
105         if (!connected || dataRole != DATA_ROLE_DEVICE) {
106             mProfilesContainer.setEnabled(false);
107         } else {
108             // Functions are only available in device mode
109             mProfilesContainer.setEnabled(true);
110         }
111         RadioButtonPreference pref;
112         for (long option : FUNCTIONS_MAP.keySet()) {
113             int title = FUNCTIONS_MAP.get(option);
114             pref = getProfilePreference(UsbBackend.usbFunctionsToString(option), title);
115             // Only show supported options
116             if (mUsbBackend.areFunctionsSupported(option)) {
117                 if (isAccessoryMode(functions)) {
118                     pref.setChecked(UsbManager.FUNCTION_MTP == option);
119                 } else if (functions == UsbManager.FUNCTION_NCM) {
120                     pref.setChecked(UsbManager.FUNCTION_RNDIS == option);
121                 } else {
122                     pref.setChecked(functions == option);
123                 }
124             } else {
125                 mProfilesContainer.removePreference(pref);
126             }
127         }
128     }
129 
130     @Override
onRadioButtonClicked(RadioButtonPreference preference)131     public void onRadioButtonClicked(RadioButtonPreference preference) {
132         final long function = UsbBackend.usbFunctionsFromString(preference.getKey());
133         final long previousFunction = mUsbBackend.getCurrentFunctions();
134         if (DEBUG) {
135             Log.d(TAG, "onRadioButtonClicked() function : " + function + ", toString() : "
136                     + UsbManager.usbFunctionsToString(function) + ", previousFunction : "
137                     + previousFunction + ", toString() : "
138                     + UsbManager.usbFunctionsToString(previousFunction));
139         }
140         if (function != previousFunction && !Utils.isMonkeyRunning()
141                 && !isClickEventIgnored(function, previousFunction)) {
142             mPreviousFunction = previousFunction;
143 
144             //Update the UI in advance to make it looks smooth
145             final RadioButtonPreference prevPref =
146                     (RadioButtonPreference) mProfilesContainer.findPreference(
147                             UsbBackend.usbFunctionsToString(mPreviousFunction));
148             if (prevPref != null) {
149                 prevPref.setChecked(false);
150                 preference.setChecked(true);
151             }
152 
153             if (function == UsbManager.FUNCTION_RNDIS || function == UsbManager.FUNCTION_NCM) {
154                 // We need to have entitlement check for usb tethering, so use API in
155                 // TetheringManager.
156                 mTetheringManager.startTethering(
157                         TetheringManager.TETHERING_USB, new HandlerExecutor(mHandler),
158                         mOnStartTetheringCallback);
159             } else {
160                 mUsbBackend.setCurrentFunctions(function);
161             }
162         }
163     }
164 
isClickEventIgnored(long function, long previousFunction)165     private boolean isClickEventIgnored(long function, long previousFunction) {
166         return isAccessoryMode(previousFunction) && function == UsbManager.FUNCTION_MTP;
167     }
168 
isAccessoryMode(long function)169     private boolean isAccessoryMode(long function) {
170         return (function & UsbManager.FUNCTION_ACCESSORY) != 0;
171     }
172 
173     @Override
isAvailable()174     public boolean isAvailable() {
175         return !Utils.isMonkeyRunning();
176     }
177 
178     @Override
getPreferenceKey()179     public String getPreferenceKey() {
180         return "usb_details_functions";
181     }
182 
183     @VisibleForTesting
184     final class OnStartTetheringCallback implements TetheringManager.StartTetheringCallback {
185 
186         @Override
onTetheringFailed(int error)187         public void onTetheringFailed(int error) {
188             Log.w(TAG, "onTetheringFailed() error : " + error);
189             mUsbBackend.setCurrentFunctions(mPreviousFunction);
190         }
191     }
192 }
193