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