• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.development;
18 
19 import android.app.Activity;
20 import android.app.Dialog;
21 import android.app.settings.SettingsEnums;
22 import android.content.BroadcastReceiver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.debug.AdbManager;
27 import android.debug.IAdbManager;
28 import android.debug.PairDevice;
29 import android.os.Build;
30 import android.os.Bundle;
31 import android.os.RemoteException;
32 import android.os.ServiceManager;
33 import android.provider.Settings;
34 import android.util.Log;
35 
36 import androidx.preference.Preference;
37 import androidx.preference.PreferenceCategory;
38 
39 import com.android.settings.R;
40 import com.android.settings.SettingsActivity;
41 import com.android.settings.core.SubSettingLauncher;
42 import com.android.settings.dashboard.DashboardFragment;
43 import com.android.settings.search.BaseSearchIndexProvider;
44 import com.android.settings.widget.MainSwitchBarController;
45 import com.android.settings.widget.SettingsMainSwitchBar;
46 import com.android.settingslib.core.AbstractPreferenceController;
47 import com.android.settingslib.core.lifecycle.Lifecycle;
48 import com.android.settingslib.development.DevelopmentSettingsEnabler;
49 import com.android.settingslib.search.SearchIndexable;
50 import com.android.settingslib.widget.FooterPreference;
51 
52 import java.util.ArrayList;
53 import java.util.HashMap;
54 import java.util.List;
55 import java.util.Map;
56 
57 /**
58  * Fragment shown when clicking in the "Wireless Debugging" preference in
59  * the developer options.
60  */
61 @SearchIndexable
62 public class WirelessDebuggingFragment extends DashboardFragment
63         implements WirelessDebuggingEnabler.OnEnabledListener {
64 
65     private static final String TAG = "WirelessDebuggingFrag";
66 
67     // Activity result from clicking on a paired device.
68     static final int PAIRED_DEVICE_REQUEST = 0;
69     static final String PAIRED_DEVICE_REQUEST_TYPE = "request_type";
70     static final int FORGET_ACTION = 0;
71     // Activity result from pairing a device.
72     static final int PAIRING_DEVICE_REQUEST = 1;
73     static final String PAIRING_DEVICE_REQUEST_TYPE = "request_type_pairing";
74     static final int SUCCESS_ACTION = 0;
75     static final int FAIL_ACTION = 1;
76     static final String PAIRED_DEVICE_EXTRA = "paired_device";
77     static final String DEVICE_NAME_EXTRA = "device_name";
78     static final String IP_ADDR_EXTRA = "ip_addr";
79 
80     // UI components
81     private static final String PREF_KEY_ADB_DEVICE_NAME = "adb_device_name_pref";
82     private static final String PREF_KEY_ADB_IP_ADDR = "adb_ip_addr_pref";
83     private static final String PREF_KEY_PAIRING_METHODS_CATEGORY = "adb_pairing_methods_category";
84     private static final String PREF_KEY_ADB_CODE_PAIRING = "adb_pair_method_code_pref";
85     private static final String PREF_KEY_PAIRED_DEVICES_CATEGORY = "adb_paired_devices_category";
86     private static final String PREF_KEY_FOOTER_CATEGORY = "adb_wireless_footer_category";
87     private static AdbIpAddressPreferenceController sAdbIpAddressPreferenceController;
88 
89     private final PairingCodeDialogListener mPairingCodeDialogListener =
90             new PairingCodeDialogListener();
91     private WirelessDebuggingEnabler mWifiDebuggingEnabler;
92     private Preference mDeviceNamePreference;
93     private Preference mIpAddrPreference;
94     private PreferenceCategory mPairingMethodsCategory;
95     private Preference mCodePairingPreference;
96     private PreferenceCategory mPairedDevicesCategory;
97     private PreferenceCategory mFooterCategory;
98     private FooterPreference mOffMessagePreference;
99     // Map of paired devices, with the device GUID is the key
100     private Map<String, AdbPairedDevicePreference> mPairedDevicePreferences;
101     private IAdbManager mAdbManager;
102     private int mConnectionPort;
103     private IntentFilter mIntentFilter;
104     private AdbWirelessDialog mPairingCodeDialog;
105 
106     private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
107         @Override
108         public void onReceive(Context context, Intent intent) {
109             String action = intent.getAction();
110             if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
111                 Map<String, PairDevice> newPairedDevicesList =
112                         (HashMap<String, PairDevice>) intent.getSerializableExtra(
113                             AdbManager.WIRELESS_DEVICES_EXTRA);
114                 updatePairedDevicePreferences(newPairedDevicesList);
115             } else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
116                 int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
117                         AdbManager.WIRELESS_STATUS_DISCONNECTED);
118                 if (status == AdbManager.WIRELESS_STATUS_CONNECTED
119                         || status == AdbManager.WIRELESS_STATUS_DISCONNECTED) {
120                     sAdbIpAddressPreferenceController.updateState(mIpAddrPreference);
121                 }
122             } else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
123                 Integer res = intent.getIntExtra(
124                         AdbManager.WIRELESS_STATUS_EXTRA,
125                         AdbManager.WIRELESS_STATUS_FAIL);
126 
127                 if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
128                     String pairingCode = intent.getStringExtra(
129                                 AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
130                     if (mPairingCodeDialog != null) {
131                         mPairingCodeDialog.getController().setPairingCode(pairingCode);
132                     }
133                 } else if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
134                     removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
135                     mPairingCodeDialog = null;
136                 } else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
137                     removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
138                     mPairingCodeDialog = null;
139                     showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
140                 } else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
141                     int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
142                     Log.i(TAG, "Got pairing code port=" + port);
143                     String ipAddr = sAdbIpAddressPreferenceController.getIpv4Address() + ":" + port;
144                     if (mPairingCodeDialog != null) {
145                         mPairingCodeDialog.getController().setIpAddr(ipAddr);
146                     }
147                 }
148             }
149         }
150     };
151 
152     class PairingCodeDialogListener implements AdbWirelessDialog.AdbWirelessDialogListener {
153         @Override
onDismiss()154         public void onDismiss() {
155             Log.i(TAG, "onDismiss");
156             mPairingCodeDialog = null;
157             try {
158                 mAdbManager.disablePairing();
159             } catch (RemoteException e) {
160                 Log.e(TAG, "Unable to cancel pairing");
161             }
162         }
163     }
164 
165     @Override
onAttach(Context context)166     public void onAttach(Context context) {
167         super.onAttach(context);
168         use(AdbQrCodePreferenceController.class).setParentFragment(this);
169     }
170 
171     @Override
onActivityCreated(Bundle savedInstanceState)172     public void onActivityCreated(Bundle savedInstanceState) {
173         super.onActivityCreated(savedInstanceState);
174         final SettingsActivity activity = (SettingsActivity) getActivity();
175         final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
176         switchBar.setTitle(getContext().getString(R.string.wireless_debugging_main_switch_title));
177 
178         mWifiDebuggingEnabler =  new WirelessDebuggingEnabler(activity,
179                 new MainSwitchBarController(switchBar), this, getSettingsLifecycle());
180     }
181 
182     @Override
onCreate(Bundle icicle)183     public void onCreate(Bundle icicle) {
184         super.onCreate(icicle);
185 
186         addPreferences();
187         mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
188         mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
189         mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
190     }
191 
addPreferences()192     private void addPreferences() {
193         mDeviceNamePreference =
194             (Preference) findPreference(PREF_KEY_ADB_DEVICE_NAME);
195         mIpAddrPreference =
196             (Preference) findPreference(PREF_KEY_ADB_IP_ADDR);
197         mPairingMethodsCategory =
198                 (PreferenceCategory) findPreference(PREF_KEY_PAIRING_METHODS_CATEGORY);
199         mCodePairingPreference =
200                 (Preference) findPreference(PREF_KEY_ADB_CODE_PAIRING);
201         mCodePairingPreference.setOnPreferenceClickListener(preference -> {
202             showDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
203             return true;
204         });
205 
206         mPairedDevicesCategory =
207                 (PreferenceCategory) findPreference(PREF_KEY_PAIRED_DEVICES_CATEGORY);
208         mFooterCategory =
209                 (PreferenceCategory) findPreference(PREF_KEY_FOOTER_CATEGORY);
210 
211         mOffMessagePreference =
212                 new FooterPreference(mFooterCategory.getContext());
213         final CharSequence title = getText(R.string.adb_wireless_list_empty_off);
214         mOffMessagePreference.setTitle(title);
215         mFooterCategory.addPreference(mOffMessagePreference);
216     }
217 
218     @Override
onDestroyView()219     public void onDestroyView() {
220         super.onDestroyView();
221 
222         mWifiDebuggingEnabler.teardownSwitchController();
223     }
224 
225     @Override
onResume()226     public void onResume() {
227         super.onResume();
228 
229         getActivity().registerReceiver(mReceiver, mIntentFilter);
230     }
231 
232     @Override
onPause()233     public void onPause() {
234         super.onPause();
235 
236         removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
237         getActivity().unregisterReceiver(mReceiver);
238     }
239 
240     @Override
onActivityResult(int requestCode, int resultCode, Intent data)241     public void onActivityResult(int requestCode, int resultCode, Intent data) {
242         super.onActivityResult(requestCode, resultCode, data);
243 
244         if (requestCode == PAIRED_DEVICE_REQUEST) {
245             handlePairedDeviceRequest(resultCode, data);
246         } else if (requestCode == PAIRING_DEVICE_REQUEST) {
247             handlePairingDeviceRequest(resultCode, data);
248         }
249     }
250 
251     @Override
getMetricsCategory()252     public int getMetricsCategory() {
253         return SettingsEnums.SETTINGS_ADB_WIRELESS;
254     }
255 
256     @Override
getDialogMetricsCategory(int dialogId)257     public int getDialogMetricsCategory(int dialogId) {
258         return SettingsEnums.ADB_WIRELESS_DEVICE_PAIRING_DIALOG;
259     }
260 
261     @Override
onCreateDialog(int dialogId)262     public Dialog onCreateDialog(int dialogId) {
263         Dialog d = AdbWirelessDialog.createModal(getActivity(),
264                 dialogId == AdbWirelessDialogUiBase.MODE_PAIRING
265                     ? mPairingCodeDialogListener : null, dialogId);
266         if (dialogId == AdbWirelessDialogUiBase.MODE_PAIRING) {
267             mPairingCodeDialog = (AdbWirelessDialog) d;
268             try {
269                 mAdbManager.enablePairingByPairingCode();
270             } catch (RemoteException e) {
271                 Log.e(TAG, "Unable to enable pairing");
272                 mPairingCodeDialog = null;
273                 d = AdbWirelessDialog.createModal(getActivity(), null,
274                         AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
275             }
276         }
277         if (d != null) {
278             return d;
279         }
280         return super.onCreateDialog(dialogId);
281     }
282 
283     @Override
getPreferenceScreenResId()284     protected int getPreferenceScreenResId() {
285         return R.xml.adb_wireless_settings;
286     }
287 
288     @Override
createPreferenceControllers(Context context)289     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
290         return buildPreferenceControllers(context, getActivity(), this /* fragment */,
291                 getSettingsLifecycle());
292     }
293 
buildPreferenceControllers( Context context, Activity activity, WirelessDebuggingFragment fragment, Lifecycle lifecycle)294     private static List<AbstractPreferenceController> buildPreferenceControllers(
295             Context context, Activity activity, WirelessDebuggingFragment fragment,
296             Lifecycle lifecycle) {
297         final List<AbstractPreferenceController> controllers = new ArrayList<>();
298         sAdbIpAddressPreferenceController =
299                 new AdbIpAddressPreferenceController(context, lifecycle);
300         controllers.add(sAdbIpAddressPreferenceController);
301 
302         return controllers;
303     }
304 
305     @Override
getLogTag()306     protected String getLogTag() {
307         return TAG;
308     }
309 
310     @Override
onEnabled(boolean enabled)311     public void onEnabled(boolean enabled) {
312         if (enabled) {
313             showDebuggingPreferences();
314             mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
315                     Context.ADB_SERVICE));
316             try {
317                 Map<String, PairDevice> newList = mAdbManager.getPairedDevices();
318                 updatePairedDevicePreferences(newList);
319                 mConnectionPort = mAdbManager.getAdbWirelessPort();
320                 if (mConnectionPort > 0) {
321                     Log.i(TAG, "onEnabled(): connect_port=" + mConnectionPort);
322                 }
323             } catch (RemoteException e) {
324                 Log.e(TAG, "Unable to request the paired list for Adb wireless");
325             }
326             sAdbIpAddressPreferenceController.updateState(mIpAddrPreference);
327         } else {
328             showOffMessage();
329         }
330     }
331 
showOffMessage()332     private void showOffMessage() {
333         mDeviceNamePreference.setVisible(false);
334         mIpAddrPreference.setVisible(false);
335         mPairingMethodsCategory.setVisible(false);
336         mPairedDevicesCategory.setVisible(false);
337         mFooterCategory.setVisible(true);
338     }
339 
showDebuggingPreferences()340     private void showDebuggingPreferences() {
341         mDeviceNamePreference.setVisible(true);
342         mIpAddrPreference.setVisible(true);
343         mPairingMethodsCategory.setVisible(true);
344         mPairedDevicesCategory.setVisible(true);
345         mFooterCategory.setVisible(false);
346     }
347 
updatePairedDevicePreferences(Map<String, PairDevice> newList)348     private void updatePairedDevicePreferences(Map<String, PairDevice> newList) {
349         // TODO(joshuaduong): Move the non-UI stuff into another thread
350         // as the processing could take some time.
351         if (newList == null) {
352             mPairedDevicesCategory.removeAll();
353             return;
354         }
355         if (mPairedDevicePreferences == null) {
356             mPairedDevicePreferences = new HashMap<String, AdbPairedDevicePreference>();
357         }
358         if (mPairedDevicePreferences.isEmpty()) {
359             for (Map.Entry<String, PairDevice> entry : newList.entrySet()) {
360                 AdbPairedDevicePreference p =
361                         new AdbPairedDevicePreference(entry.getValue(),
362                             mPairedDevicesCategory.getContext());
363                 mPairedDevicePreferences.put(
364                         entry.getKey(),
365                         p);
366                 p.setOnPreferenceClickListener(preference -> {
367                     AdbPairedDevicePreference pref =
368                             (AdbPairedDevicePreference) preference;
369                     launchPairedDeviceDetailsFragment(pref);
370                     return true;
371                 });
372                 mPairedDevicesCategory.addPreference(p);
373             }
374         } else {
375             // Remove any devices no longer on the newList
376             mPairedDevicePreferences.entrySet().removeIf(entry -> {
377                 if (newList.get(entry.getKey()) == null) {
378                     mPairedDevicesCategory.removePreference(entry.getValue());
379                     return true;
380                 } else {
381                     // It is in the newList. Just update the PairDevice value
382                     AdbPairedDevicePreference p =
383                             entry.getValue();
384                     p.setPairedDevice(newList.get(entry.getKey()));
385                     p.refresh();
386                     return false;
387                 }
388             });
389             // Add new devices if any.
390             for (Map.Entry<String, PairDevice> entry :
391                     newList.entrySet()) {
392                 if (mPairedDevicePreferences.get(entry.getKey()) == null) {
393                     AdbPairedDevicePreference p =
394                             new AdbPairedDevicePreference(entry.getValue(),
395                                 mPairedDevicesCategory.getContext());
396                     mPairedDevicePreferences.put(
397                             entry.getKey(),
398                             p);
399                     p.setOnPreferenceClickListener(preference -> {
400                         AdbPairedDevicePreference pref =
401                                 (AdbPairedDevicePreference) preference;
402                         launchPairedDeviceDetailsFragment(pref);
403                         return true;
404                     });
405                     mPairedDevicesCategory.addPreference(p);
406                 }
407             }
408         }
409     }
410 
launchPairedDeviceDetailsFragment(AdbPairedDevicePreference p)411     private void launchPairedDeviceDetailsFragment(AdbPairedDevicePreference p) {
412         // For sending to the device details fragment.
413         p.savePairedDeviceToExtras(p.getExtras());
414         new SubSettingLauncher(getContext())
415                 .setTitleRes(R.string.adb_wireless_device_details_title)
416                 .setDestination(AdbDeviceDetailsFragment.class.getName())
417                 .setArguments(p.getExtras())
418                 .setSourceMetricsCategory(getMetricsCategory())
419                 .setResultListener(this, PAIRED_DEVICE_REQUEST)
420                 .launch();
421     }
422 
handlePairedDeviceRequest(int result, Intent data)423     void handlePairedDeviceRequest(int result, Intent data) {
424         if (result != Activity.RESULT_OK) {
425             return;
426         }
427 
428         Log.i(TAG, "Processing paired device request");
429         int requestType = data.getIntExtra(PAIRED_DEVICE_REQUEST_TYPE, -1);
430 
431         PairDevice p;
432 
433         switch (requestType) {
434             case FORGET_ACTION:
435                 try {
436                     p = (PairDevice) data.getParcelableExtra(PAIRED_DEVICE_EXTRA);
437                     mAdbManager.unpairDevice(p.getGuid());
438                 } catch (RemoteException e) {
439                     Log.e(TAG, "Unable to forget the device");
440                 }
441                 break;
442             default:
443                 break;
444         }
445     }
446 
handlePairingDeviceRequest(int result, Intent data)447     void handlePairingDeviceRequest(int result, Intent data) {
448         if (result != Activity.RESULT_OK) {
449             return;
450         }
451 
452         int requestType = data.getIntExtra(PAIRING_DEVICE_REQUEST_TYPE, -1);
453         switch (requestType) {
454             case FAIL_ACTION:
455                 showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
456                 break;
457             default:
458                 Log.d(TAG, "Successfully paired device");
459                 break;
460         }
461     }
462 
getDeviceName()463     private String getDeviceName() {
464         // Keep device name in sync with Settings > About phone > Device name
465         String deviceName = Settings.Global.getString(getContext().getContentResolver(),
466                 Settings.Global.DEVICE_NAME);
467         if (deviceName == null) {
468             deviceName = Build.MODEL;
469         }
470         return deviceName;
471     }
472 
473     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
474             new BaseSearchIndexProvider(R.xml.adb_wireless_settings) {
475 
476                 @Override
477                 protected boolean isPageSearchEnabled(Context context) {
478                     return DevelopmentSettingsEnabler.isDevelopmentSettingsEnabled(context);
479                 }
480             };
481 }
482