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