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.net.TetheringManager.TETHERING_USB; 20 21 import android.app.settings.SettingsEnums; 22 import android.content.Context; 23 import android.graphics.drawable.Drawable; 24 import android.hardware.usb.UsbManager; 25 import android.net.TetheringManager; 26 import android.os.Bundle; 27 import android.os.Handler; 28 import android.os.HandlerExecutor; 29 import android.util.Log; 30 31 import androidx.annotation.VisibleForTesting; 32 import androidx.preference.PreferenceScreen; 33 34 import com.android.settings.R; 35 import com.android.settings.Utils; 36 import com.android.settings.widget.RadioButtonPickerFragment; 37 import com.android.settingslib.widget.CandidateInfo; 38 import com.android.settingslib.widget.FooterPreference; 39 import com.android.settingslib.widget.RadioButtonPreference; 40 41 import com.google.android.collect.Lists; 42 43 import java.util.List; 44 45 /** 46 * Provides options for selecting the default USB mode. 47 */ 48 public class UsbDefaultFragment extends RadioButtonPickerFragment { 49 50 private static final String TAG = "UsbDefaultFragment"; 51 52 @VisibleForTesting 53 UsbBackend mUsbBackend; 54 @VisibleForTesting 55 TetheringManager mTetheringManager; 56 @VisibleForTesting 57 OnStartTetheringCallback mOnStartTetheringCallback = new OnStartTetheringCallback(); 58 @VisibleForTesting 59 long mPreviousFunctions; 60 @VisibleForTesting 61 long mCurrentFunctions; 62 @VisibleForTesting 63 boolean mIsStartTethering = false; 64 @VisibleForTesting 65 Handler mHandler; 66 67 private UsbConnectionBroadcastReceiver mUsbReceiver; 68 private boolean mIsConnected = false; 69 70 @VisibleForTesting 71 UsbConnectionBroadcastReceiver.UsbConnectionListener mUsbConnectionListener = 72 (connected, functions, powerRole, dataRole) -> { 73 final long defaultFunctions = mUsbBackend.getDefaultUsbFunctions(); 74 Log.d(TAG, "UsbConnectionListener() connected : " + connected + ", functions : " 75 + functions + ", defaultFunctions : " + defaultFunctions 76 + ", mIsStartTethering : " + mIsStartTethering); 77 if (connected && !mIsConnected && (defaultFunctions == UsbManager.FUNCTION_RNDIS 78 || defaultFunctions == UsbManager.FUNCTION_NCM) 79 && !mIsStartTethering) { 80 mCurrentFunctions = defaultFunctions; 81 startTethering(); 82 } 83 84 if (mIsStartTethering && connected) { 85 mCurrentFunctions = functions; 86 refresh(functions); 87 mUsbBackend.setDefaultUsbFunctions(functions); 88 mIsStartTethering = false; 89 } 90 mIsConnected = connected; 91 }; 92 93 @Override onAttach(Context context)94 public void onAttach(Context context) { 95 super.onAttach(context); 96 mUsbBackend = new UsbBackend(context); 97 mTetheringManager = context.getSystemService(TetheringManager.class); 98 mUsbReceiver = new UsbConnectionBroadcastReceiver(context, mUsbConnectionListener, 99 mUsbBackend); 100 mHandler = new Handler(context.getMainLooper()); 101 getSettingsLifecycle().addObserver(mUsbReceiver); 102 mCurrentFunctions = mUsbBackend.getDefaultUsbFunctions(); 103 } 104 105 @Override onCreatePreferences(Bundle savedInstanceState, String rootKey)106 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 107 super.onCreatePreferences(savedInstanceState, rootKey); 108 getPreferenceScreen().addPreference(new FooterPreference.Builder(getActivity()).setTitle( 109 R.string.usb_default_info).build()); 110 } 111 112 @Override getMetricsCategory()113 public int getMetricsCategory() { 114 return SettingsEnums.USB_DEFAULT; 115 } 116 117 @Override getPreferenceScreenResId()118 protected int getPreferenceScreenResId() { 119 return R.xml.usb_default_fragment; 120 } 121 122 @Override getCandidates()123 protected List<? extends CandidateInfo> getCandidates() { 124 List<CandidateInfo> ret = Lists.newArrayList(); 125 for (final long option : UsbDetailsFunctionsController.FUNCTIONS_MAP.keySet()) { 126 final String title = getContext().getString( 127 UsbDetailsFunctionsController.FUNCTIONS_MAP.get(option)); 128 final String key = UsbBackend.usbFunctionsToString(option); 129 130 // Only show supported functions 131 if (mUsbBackend.areFunctionsSupported(option)) { 132 ret.add(new CandidateInfo(true /* enabled */) { 133 @Override 134 public CharSequence loadLabel() { 135 return title; 136 } 137 138 @Override 139 public Drawable loadIcon() { 140 return null; 141 } 142 143 @Override 144 public String getKey() { 145 return key; 146 } 147 }); 148 } 149 } 150 return ret; 151 } 152 153 @Override getDefaultKey()154 protected String getDefaultKey() { 155 long defaultUsbFunctions = mUsbBackend.getDefaultUsbFunctions(); 156 // Because we didn't have an option for NCM, so make FUNCTION_NCM corresponding to 157 // FUNCTION_RNDIS for initializing the UI. 158 return UsbBackend.usbFunctionsToString(defaultUsbFunctions == UsbManager.FUNCTION_NCM 159 ? UsbManager.FUNCTION_RNDIS : defaultUsbFunctions); 160 } 161 162 @Override setDefaultKey(String key)163 protected boolean setDefaultKey(String key) { 164 long functions = UsbBackend.usbFunctionsFromString(key); 165 mPreviousFunctions = mUsbBackend.getCurrentFunctions(); 166 if (!Utils.isMonkeyRunning()) { 167 if (functions == UsbManager.FUNCTION_RNDIS || functions == UsbManager.FUNCTION_NCM) { 168 // We need to have entitlement check for usb tethering, so use API in 169 // TetheringManager. 170 mCurrentFunctions = functions; 171 startTethering(); 172 } else { 173 mIsStartTethering = false; 174 mCurrentFunctions = functions; 175 mUsbBackend.setDefaultUsbFunctions(functions); 176 } 177 178 } 179 return true; 180 } 181 startTethering()182 private void startTethering() { 183 Log.d(TAG, "startTethering()"); 184 mIsStartTethering = true; 185 mTetheringManager.startTethering(TETHERING_USB, new HandlerExecutor(mHandler), 186 mOnStartTetheringCallback); 187 } 188 189 @Override onPause()190 public void onPause() { 191 super.onPause(); 192 mUsbBackend.setDefaultUsbFunctions(mCurrentFunctions); 193 } 194 195 @VisibleForTesting 196 final class OnStartTetheringCallback implements 197 TetheringManager.StartTetheringCallback { 198 199 @Override onTetheringStarted()200 public void onTetheringStarted() { 201 // Set default usb functions again to make internal data persistent 202 mCurrentFunctions = mUsbBackend.getCurrentFunctions(); 203 Log.d(TAG, "onTetheringStarted() : mCurrentFunctions " + mCurrentFunctions); 204 mUsbBackend.setDefaultUsbFunctions(mCurrentFunctions); 205 } 206 207 @Override onTetheringFailed(int error)208 public void onTetheringFailed(int error) { 209 Log.w(TAG, "onTetheringFailed() error : " + error); 210 mUsbBackend.setDefaultUsbFunctions(mPreviousFunctions); 211 updateCandidates(); 212 } 213 } 214 refresh(long functions)215 private void refresh(long functions) { 216 final PreferenceScreen screen = getPreferenceScreen(); 217 for (long option : UsbDetailsFunctionsController.FUNCTIONS_MAP.keySet()) { 218 final RadioButtonPreference pref = 219 screen.findPreference(UsbBackend.usbFunctionsToString(option)); 220 if (pref != null) { 221 final boolean isSupported = mUsbBackend.areFunctionsSupported(option); 222 pref.setEnabled(isSupported); 223 if (isSupported) { 224 if (functions == UsbManager.FUNCTION_NCM) { 225 pref.setChecked(UsbManager.FUNCTION_RNDIS == option); 226 } else { 227 pref.setChecked(functions == option); 228 } 229 } 230 } 231 } 232 } 233 }