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 import static android.net.ConnectivityManager.TETHERING_USB; 21 22 import android.content.Context; 23 import android.hardware.usb.UsbManager; 24 import android.net.ConnectivityManager; 25 26 import androidx.annotation.VisibleForTesting; 27 import androidx.preference.PreferenceCategory; 28 import androidx.preference.PreferenceScreen; 29 30 import com.android.settings.R; 31 import com.android.settings.Utils; 32 import com.android.settingslib.widget.RadioButtonPreference; 33 34 import java.util.LinkedHashMap; 35 import java.util.Map; 36 37 /** 38 * This class controls the radio buttons for choosing between different USB functions. 39 */ 40 public class UsbDetailsFunctionsController extends UsbDetailsController 41 implements RadioButtonPreference.OnClickListener { 42 43 static final Map<Long, Integer> FUNCTIONS_MAP = new LinkedHashMap<>(); 44 45 static { FUNCTIONS_MAP.put(UsbManager.FUNCTION_MTP, R.string.usb_use_file_transfers)46 FUNCTIONS_MAP.put(UsbManager.FUNCTION_MTP, R.string.usb_use_file_transfers); FUNCTIONS_MAP.put(UsbManager.FUNCTION_RNDIS, R.string.usb_use_tethering)47 FUNCTIONS_MAP.put(UsbManager.FUNCTION_RNDIS, R.string.usb_use_tethering); FUNCTIONS_MAP.put(UsbManager.FUNCTION_MIDI, R.string.usb_use_MIDI)48 FUNCTIONS_MAP.put(UsbManager.FUNCTION_MIDI, R.string.usb_use_MIDI); FUNCTIONS_MAP.put(UsbManager.FUNCTION_PTP, R.string.usb_use_photo_transfers)49 FUNCTIONS_MAP.put(UsbManager.FUNCTION_PTP, R.string.usb_use_photo_transfers); FUNCTIONS_MAP.put(UsbManager.FUNCTION_NONE, R.string.usb_use_charging_only)50 FUNCTIONS_MAP.put(UsbManager.FUNCTION_NONE, R.string.usb_use_charging_only); 51 } 52 53 private PreferenceCategory mProfilesContainer; 54 private ConnectivityManager mConnectivityManager; 55 @VisibleForTesting 56 OnStartTetheringCallback mOnStartTetheringCallback; 57 @VisibleForTesting 58 long mPreviousFunction; 59 UsbDetailsFunctionsController(Context context, UsbDetailsFragment fragment, UsbBackend backend)60 public UsbDetailsFunctionsController(Context context, UsbDetailsFragment fragment, 61 UsbBackend backend) { 62 super(context, fragment, backend); 63 mConnectivityManager = context.getSystemService(ConnectivityManager.class); 64 mOnStartTetheringCallback = new OnStartTetheringCallback(); 65 mPreviousFunction = mUsbBackend.getCurrentFunctions(); 66 } 67 68 @Override displayPreference(PreferenceScreen screen)69 public void displayPreference(PreferenceScreen screen) { 70 super.displayPreference(screen); 71 mProfilesContainer = screen.findPreference(getPreferenceKey()); 72 } 73 74 /** 75 * Gets a switch preference for the particular option, creating it if needed. 76 */ getProfilePreference(String key, int titleId)77 private RadioButtonPreference getProfilePreference(String key, int titleId) { 78 RadioButtonPreference pref = mProfilesContainer.findPreference(key); 79 if (pref == null) { 80 pref = new RadioButtonPreference(mProfilesContainer.getContext()); 81 pref.setKey(key); 82 pref.setTitle(titleId); 83 pref.setOnClickListener(this); 84 mProfilesContainer.addPreference(pref); 85 } 86 return pref; 87 } 88 89 @Override refresh(boolean connected, long functions, int powerRole, int dataRole)90 protected void refresh(boolean connected, long functions, int powerRole, int dataRole) { 91 if (!connected || dataRole != DATA_ROLE_DEVICE) { 92 mProfilesContainer.setEnabled(false); 93 } else { 94 // Functions are only available in device mode 95 mProfilesContainer.setEnabled(true); 96 } 97 RadioButtonPreference pref; 98 for (long option : FUNCTIONS_MAP.keySet()) { 99 int title = FUNCTIONS_MAP.get(option); 100 pref = getProfilePreference(UsbBackend.usbFunctionsToString(option), title); 101 // Only show supported options 102 if (mUsbBackend.areFunctionsSupported(option)) { 103 pref.setChecked(functions == option); 104 } else { 105 mProfilesContainer.removePreference(pref); 106 } 107 } 108 } 109 110 @Override onRadioButtonClicked(RadioButtonPreference preference)111 public void onRadioButtonClicked(RadioButtonPreference preference) { 112 final long function = UsbBackend.usbFunctionsFromString(preference.getKey()); 113 final long previousFunction = mUsbBackend.getCurrentFunctions(); 114 if (function != previousFunction && !Utils.isMonkeyRunning()) { 115 mPreviousFunction = previousFunction; 116 117 //Update the UI in advance to make it looks smooth 118 final RadioButtonPreference prevPref = 119 (RadioButtonPreference) mProfilesContainer.findPreference( 120 UsbBackend.usbFunctionsToString(mPreviousFunction)); 121 if (prevPref != null) { 122 prevPref.setChecked(false); 123 preference.setChecked(true); 124 } 125 126 if (function == UsbManager.FUNCTION_RNDIS) { 127 // We need to have entitlement check for usb tethering, so use API in 128 // ConnectivityManager. 129 mConnectivityManager.startTethering(TETHERING_USB, true /* showProvisioningUi */, 130 mOnStartTetheringCallback); 131 } else { 132 mUsbBackend.setCurrentFunctions(function); 133 } 134 } 135 } 136 137 @Override isAvailable()138 public boolean isAvailable() { 139 return !Utils.isMonkeyRunning(); 140 } 141 142 @Override getPreferenceKey()143 public String getPreferenceKey() { 144 return "usb_details_functions"; 145 } 146 147 @VisibleForTesting 148 final class OnStartTetheringCallback extends 149 ConnectivityManager.OnStartTetheringCallback { 150 151 @Override onTetheringFailed()152 public void onTetheringFailed() { 153 super.onTetheringFailed(); 154 mUsbBackend.setCurrentFunctions(mPreviousFunction); 155 } 156 } 157 } 158