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