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