[adb-wireless] Add Wireless Debugging Switch in Developer options.

Bug: 111434128
Bug: 119492574

Test: make RunSettingsRoboTests ROBOTEST_FILTER=WirelessDebugging
Change-Id: I188badb43035172642cf235bb27e56d3a1dea169
Merged-In: I188badb43035172642cf235bb27e56d3a1dea169
This commit is contained in:
Joshua Duong
2018-11-08 06:56:45 -08:00
parent 9579a76913
commit 0110afcc46
25 changed files with 2817 additions and 0 deletions

View File

@@ -3,3 +3,4 @@ checkcolor_hook = ${REPO_ROOT}/prebuilts/checkcolor/checkcolor.py -p .
strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}

View File

@@ -0,0 +1,94 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/camera_layout">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center_horizontal">
<include layout="@layout/wifi_dpp_fragment_header"/>
<ProgressBar
android:id="@+id/indeterminate_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:layout_marginBottom="8dp"
style="?android:attr/progressBarStyleHorizontal"/>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clipChildren="true">
<TextureView
android:id="@+id/preview_view"
android:layout_width="match_parent"
android:layout_height="@dimen/qrcode_preview_size"/>
<com.android.settings.wifi.qrcode.QrDecorateView
android:id="@+id/decorate_view"
android:layout_width="match_parent"
android:layout_height="@dimen/qrcode_preview_size"/>
</FrameLayout>
<TextView
android:id="@+id/error_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:textAlignment="center"
android:textColor="?android:attr/colorError"
android:visibility="invisible"/>
</LinearLayout>
</ScrollView>
<!--
The spinner indicating that the device is waiting for pairing
after getting valid QR code
-->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/verifying_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
android:visibility="gone">
<ProgressBar
android:id="@+id/verifying_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_progress_text"
android:text="@string/adb_wireless_verifying_qrcode_text"/>
</LinearLayout>
</LinearLayout>

View File

@@ -0,0 +1,112 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_scrollview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fadeScrollbars="false"
android:scrollIndicators="top|bottom">
<LinearLayout
android:id="@+id/l_adbwirelessdialog"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="8dip">
<LinearLayout android:id="@+id/l_pairing_six_digit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/adb_wireless_section"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/adb_wireless_item" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_label"
android:text="@string/adb_pairing_device_dialog_pairing_code_label"
android:textDirection="locale" />
<TextView
android:id="@+id/pairing_code"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_content" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_label"
android:text="@string/adb_wireless_ip_addr_preference_title"
android:textDirection="locale"
android:paddingTop="8dip"/>
<TextView
android:id="@+id/ip_addr"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_label"
android:text="@string/summary_placeholder"
android:textDirection="locale" />
</LinearLayout>
</LinearLayout>
<LinearLayout android:id="@+id/l_pairing_failed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/adb_wireless_section"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/adb_wireless_item" >
<TextView
android:id="@+id/pairing_failed_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_label"
android:textDirection="locale" />
</LinearLayout>
</LinearLayout>
<LinearLayout android:id="@+id/l_qrcode_pairing_failed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/adb_wireless_section"
android:visibility="gone">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/adb_wireless_item" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/adb_wireless_item_label"
android:textDirection="locale"
android:text="@string/adb_qrcode_pairing_device_failed_msg"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</ScrollView>

View File

@@ -153,6 +153,44 @@
<item name="android:orientation">vertical</item>
</style>
<style name="adb_wireless_item">
<item name="android:layout_marginTop">8dp</item>
<item name="android:layout_marginStart">8dp</item>
<item name="android:layout_marginEnd">8dp</item>
<item name="android:paddingStart">8dp</item>
<item name="android:paddingEnd">8dp</item>
<item name="android:orientation">vertical</item>
<item name="android:gravity">start</item>
</style>
<style name="adb_wireless_item_label">
<item name="android:paddingStart">8dp</item>
<item name="android:textSize">14sp</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
<item name="android:textColor">?android:attr/textColorSecondary</item>
</style>
<style name="adb_wireless_item_content">
<item name="android:paddingStart">8dp</item>
<item name="android:textSize">24sp</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="adb_wireless_item_progress_text">
<item name="android:paddingTop">16dp</item>
<item name="android:textSize">18sp</item>
<item name="android:textAlignment">viewStart</item>
<item name="android:textAppearance">@android:style/TextAppearance.Material.Body1</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="adb_wireless_section">
<item name="android:orientation">vertical</item>
</style>
<style name="ConfirmDeviceCredentialsAnimationStyle"
parent="@*android:style/Animation.Material.Activity">
<item name="android:activityOpenEnterAnimation">@anim/confirm_credential_open_enter</item>

View File

@@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/device_details_title"
settings:initialExpandedChildrenCount="3">
<com.android.settingslib.widget.LayoutPreference
android:key="adb_device_header"
android:layout="@layout/settings_entity_header"
android:selectable="false" />
<!-- Buttons -->
<com.android.settingslib.widget.ActionButtonsPreference
android:key="buttons"
android:selectable="false" />
<!-- Device Fingerprint Details -->
<PreferenceCategory
android:key="fingerprint_category"
android:layout="@layout/preference_category_no_label">
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -0,0 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:settings="http://schemas.android.com/apk/res-auto"
android:title="@string/adb_wireless_settings">
<PreferenceCategory
android:layout="@layout/preference_category_no_label">
<!-- ADB device name -->
<Preference
android:key="adb_device_name_pref"
android:title="@string/my_device_info_device_name_preference_title"
android:summary="@string/summary_placeholder"
android:selectable="false"
settings:controller="com.android.settings.development.AdbDeviceNamePreferenceController"
settings:enableCopying="true"/>
<!-- IP address & port -->
<Preference
android:key="adb_ip_addr_pref"
android:title="@string/adb_wireless_ip_addr_preference_title"
android:summary="@string/summary_placeholder"
android:selectable="false"/>
</PreferenceCategory>
<!-- Pairing methods category -->
<PreferenceCategory
android:key="adb_pairing_methods_category"
android:layout="@layout/preference_category_no_label">
<!-- qrcode scanner -->
<Preference
android:key="adb_pair_method_qrcode_pref"
android:icon="@drawable/ic_scan_24dp"
android:title="@string/adb_pair_method_qrcode_title"
android:summary="@string/adb_pair_method_qrcode_summary"/>
<Preference
android:key="adb_pair_method_code_pref"
android:title="@string/adb_pair_method_code_title"
android:summary="@string/adb_pair_method_code_summary"/>
</PreferenceCategory>
<!-- Paired devices list -->
<PreferenceCategory
android:key="adb_paired_devices_category"
android:title="@string/adb_paired_devices_title"/>
<!-- Off message: Shown only in the off state -->
<PreferenceCategory
android:key="adb_wireless_footer_category"
android:layout="@layout/preference_category_no_label"
settings:allowDividerAbove="false"/>
</PreferenceScreen>

View File

@@ -145,6 +145,13 @@
<Preference android:key="clear_adb_keys"
android:title="@string/clear_adb_keys" />
<com.android.settings.widget.MasterSwitchPreference
android:fragment="com.android.settings.development.WirelessDebuggingFragment"
android:key="toggle_adb_wireless"
android:title="@string/enable_adb_wireless"
android:summary="@string/enable_adb_wireless_summary"
settings:keywords="@string/keywords_adb_wireless" />
<SwitchPreference
android:key="enable_terminal"
android:title="@string/enable_terminal_title"

View File

@@ -0,0 +1,92 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.debug.PairDevice;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.ActionButtonsPreference;
/**
* Controller for logic pertaining to displaying adb device information for the
* {@link AdbDeviceDetailsFragment}.
*/
public class AdbDeviceDetailsActionController extends AbstractPreferenceController {
private static final String TAG = "AdbDeviceDetailsAction";
@VisibleForTesting
static final String KEY_BUTTONS_PREF = "buttons";
private PairDevice mPairedDevice;
private final Fragment mFragment;
private ActionButtonsPreference mButtonsPref;
public AdbDeviceDetailsActionController(
PairDevice pairedDevice,
Context context,
Fragment fragment) {
super(context);
mPairedDevice = pairedDevice;
mFragment = fragment;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_BUTTONS_PREF;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mButtonsPref = ((ActionButtonsPreference) screen.findPreference(getPreferenceKey()))
.setButton1Visible(false)
.setButton2Icon(R.drawable.ic_settings_delete)
.setButton2Text(R.string.adb_device_forget)
.setButton2OnClickListener(view -> forgetDevice());
}
/**
* Forgets the device.
*/
private void forgetDevice() {
Intent intent = new Intent();
intent.putExtra(
WirelessDebuggingFragment.PAIRED_DEVICE_REQUEST_TYPE,
WirelessDebuggingFragment.FORGET_ACTION);
intent.putExtra(
WirelessDebuggingFragment.PAIRED_DEVICE_EXTRA,
mPairedDevice);
mFragment.getActivity().setResult(Activity.RESULT_OK, intent);
mFragment.getActivity().finish();
}
}

View File

@@ -0,0 +1,80 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.debug.PairDevice;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.widget.FooterPreference;
/**
* Controller for logic pertaining to displaying adb device information for the
* {@link AdbDeviceDetailsFragment}.
*/
public class AdbDeviceDetailsFingerprintController extends AbstractPreferenceController {
private static final String TAG = "AdbDeviceDetailsFinger";
@VisibleForTesting
static final String KEY_FINGERPRINT_CATEGORY = "fingerprint_category";
private PairDevice mPairedDevice;
private final Fragment mFragment;
private PreferenceCategory mFingerprintCategory;
private FooterPreference mFingerprintPref;
public AdbDeviceDetailsFingerprintController(
PairDevice pairedDevice,
Context context,
Fragment fragment) {
super(context);
mPairedDevice = pairedDevice;
mFragment = fragment;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_FINGERPRINT_CATEGORY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mFingerprintCategory = (PreferenceCategory) screen.findPreference(getPreferenceKey());
mFingerprintPref = new FooterPreference(mFingerprintCategory.getContext());
final CharSequence titleFormat = mContext.getText(
R.string.adb_device_fingerprint_title_format);
mFingerprintPref.setTitle(String.format(
titleFormat.toString(), mPairedDevice.getGuid()));
mFingerprintCategory.addPreference(mFingerprintPref);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.app.settings.SettingsEnums;
import android.content.Context;
import android.debug.PairDevice;
import android.os.Bundle;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.core.AbstractPreferenceController;
import java.util.ArrayList;
import java.util.List;
/**
* Fragment shown when clicking on a paired device in the Wireless
* Debugging fragment.
*/
public class AdbDeviceDetailsFragment extends DashboardFragment {
private static final String TAG = "AdbDeviceDetailsFrag";
private PairDevice mPairedDevice;
public AdbDeviceDetailsFragment() {
super();
}
@Override
public void onAttach(Context context) {
// Get the paired device stored in the extras
Bundle bundle = getArguments();
if (bundle.containsKey(AdbPairedDevicePreference.PAIRED_DEVICE_EXTRA)) {
mPairedDevice = (PairDevice) bundle.getParcelable(
AdbPairedDevicePreference.PAIRED_DEVICE_EXTRA);
}
super.onAttach(context);
}
@Override
public int getMetricsCategory() {
return SettingsEnums.ADB_WIRELESS_DEVICE_DETAILS;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.adb_device_details_fragment;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
controllers.add(new AdbDeviceDetailsHeaderController(mPairedDevice, context, this));
controllers.add(new AdbDeviceDetailsActionController(mPairedDevice, context, this));
controllers.add(new AdbDeviceDetailsFingerprintController(mPairedDevice, context, this));
return controllers;
}
}

View File

@@ -0,0 +1,88 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.debug.PairDevice;
import androidx.annotation.VisibleForTesting;
import androidx.fragment.app.Fragment;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.EntityHeaderController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.widget.LayoutPreference;
/**
* Controller for logic pertaining to displaying adb device information for the
* {@link AdbDeviceDetailsFragment}.
*/
public class AdbDeviceDetailsHeaderController extends AbstractPreferenceController
implements PreferenceControllerMixin, LifecycleObserver {
private static final String TAG = "AdbDeviceDetailsHeader";
@VisibleForTesting
static final String KEY_HEADER = "adb_device_header";
private PairDevice mPairedDevice;
private final Fragment mFragment;
private EntityHeaderController mEntityHeaderController;
public AdbDeviceDetailsHeaderController(
PairDevice pairedDevice,
Context context,
Fragment fragment) {
super(context);
mPairedDevice = pairedDevice;
mFragment = fragment;
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return KEY_HEADER;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
setupEntityHeader(screen);
}
private void setupEntityHeader(PreferenceScreen screen) {
LayoutPreference headerPref = (LayoutPreference) screen.findPreference(KEY_HEADER);
mEntityHeaderController =
EntityHeaderController.newInstance(
mFragment.getActivity(), mFragment,
headerPref.findViewById(R.id.entity_header));
mEntityHeaderController
.setIcon(mContext.getDrawable(com.android.internal.R.drawable.ic_bt_laptop))
.setLabel(mPairedDevice.getDeviceName())
.done(mFragment.getActivity(), true);
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import static android.content.Context.CLIPBOARD_SERVICE;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.os.Build;
import android.provider.Settings;
import android.widget.Toast;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
/**
* Controller for the device name preference in the Wireless debugging
* fragment.
*/
public class AdbDeviceNamePreferenceController extends BasePreferenceController {
private static final String TAG = "AdbDeviceNamePrefCtrl";
private String mDeviceName;
public AdbDeviceNamePreferenceController(Context context, String key) {
super(context, key);
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
// Keep device name in sync with Settings > About phone > Device name
mDeviceName = Settings.Global.getString(mContext.getContentResolver(),
Settings.Global.DEVICE_NAME);
if (mDeviceName == null) {
mDeviceName = Build.MODEL;
}
}
@Override
public CharSequence getSummary() {
return mDeviceName;
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE;
}
@Override
public void copy() {
final ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(
CLIPBOARD_SERVICE);
clipboard.setPrimaryClip(ClipData.newPlainText("text", mDeviceName));
final String toast = mContext.getString(R.string.copyable_slice_toast,
mDeviceName);
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
}

View File

@@ -0,0 +1,156 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.debug.IAdbManager;
import android.net.ConnectivityManager;
import android.net.LinkProperties;
import android.net.wifi.WifiManager;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.deviceinfo.AbstractConnectivityPreferenceController;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.util.Iterator;
/**
* Controller for the ip address preference in the Wireless debugging
* fragment.
*/
public class AdbIpAddressPreferenceController extends AbstractConnectivityPreferenceController {
private static final String TAG = "AdbIpAddrPrefCtrl";
private static final String[] CONNECTIVITY_INTENTS = {
ConnectivityManager.CONNECTIVITY_ACTION,
WifiManager.LINK_CONFIGURATION_CHANGED_ACTION,
WifiManager.NETWORK_STATE_CHANGED_ACTION,
};
private static final String PREF_KEY = "adb_ip_addr_pref";
private Preference mAdbIpAddrPref;
private int mPort;
private final ConnectivityManager mCM;
private IAdbManager mAdbManager;
public AdbIpAddressPreferenceController(Context context, Lifecycle lifecycle) {
super(context, lifecycle);
mCM = context.getSystemService(ConnectivityManager.class);
mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
Context.ADB_SERVICE));
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public String getPreferenceKey() {
return PREF_KEY;
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
mAdbIpAddrPref = screen.findPreference(PREF_KEY);
updateConnectivity();
}
@Override
public void updateState(Preference preference) {
super.updateState(preference);
updateConnectivity();
}
@Override
protected String[] getConnectivityIntents() {
return CONNECTIVITY_INTENTS;
}
protected int getPort() {
try {
return mAdbManager.getAdbWirelessPort();
} catch (RemoteException e) {
Log.e(TAG, "Unable to get the adbwifi port");
}
return 0;
}
public String getIpv4Address() {
return getDefaultIpAddresses(mCM);
}
@Override
protected void updateConnectivity() {
String ipAddress = getDefaultIpAddresses(mCM);
if (ipAddress != null) {
int port = getPort();
if (port <= 0) {
mAdbIpAddrPref.setSummary(R.string.status_unavailable);
} else {
ipAddress += ":" + port;
}
mAdbIpAddrPref.setSummary(ipAddress);
} else {
mAdbIpAddrPref.setSummary(R.string.status_unavailable);
}
}
/**
* Returns the default link's IP addresses, if any, taking into account IPv4 and IPv6 style
* addresses.
* @param cm ConnectivityManager
* @return the formatted and newline-separated IP addresses, or null if none.
*/
private static String getDefaultIpAddresses(ConnectivityManager cm) {
LinkProperties prop = cm.getActiveLinkProperties();
return formatIpAddresses(prop);
}
private static String formatIpAddresses(LinkProperties prop) {
if (prop == null) {
return null;
}
Iterator<InetAddress> iter = prop.getAllAddresses().iterator();
// If there are no entries, return null
if (!iter.hasNext()) {
return null;
}
// Concatenate all available addresses, newline separated
StringBuilder addresses = new StringBuilder();
while (iter.hasNext()) {
InetAddress addr = iter.next();
if (addr instanceof Inet4Address) {
// adb only supports ipv4 at the moment
addresses.append(addr.getHostAddress());
break;
}
}
return addresses.toString();
}
}

View File

@@ -0,0 +1,95 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.debug.PairDevice;
import android.os.Bundle;
import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceViewHolder;
import com.android.settings.R;
/**
* An AP preference for the currently connected AP
*/
public class AdbPairedDevicePreference extends Preference {
private static final String TAG = "AdbPairedDevicePref";
private PairDevice mPairedDevice;
// Extract using getSerializable(PAIRED_DEVICE_EXTRA)
public static final String PAIRED_DEVICE_EXTRA = "paired_device";
public AdbPairedDevicePreference(PairDevice pairedDevice, Context context) {
super(context);
mPairedDevice = pairedDevice;
setWidgetLayoutResource(getWidgetLayoutResourceId());
refresh();
}
protected int getWidgetLayoutResourceId() {
return R.layout.preference_widget_gear_optional_background;
}
/**
* Refreshes the preference bound to the paired device previously passed in.
*/
public void refresh() {
setTitle(this, mPairedDevice);
}
public void setPairedDevice(PairDevice pairedDevice) {
mPairedDevice = pairedDevice;
}
public PairDevice getPairedDevice() {
return mPairedDevice;
}
@Override
public void onBindViewHolder(PreferenceViewHolder holder) {
super.onBindViewHolder(holder);
final View gear = holder.findViewById(R.id.settings_button);
final View gearNoBg = holder.findViewById(R.id.settings_button_no_background);
gear.setVisibility(View.INVISIBLE);
gearNoBg.setVisibility(View.VISIBLE);
}
static void setTitle(AdbPairedDevicePreference preference,
PairDevice pairedDevice) {
preference.setTitle(pairedDevice.getDeviceName());
preference.setSummary(pairedDevice.isConnected()
? preference.getContext().getText(R.string.adb_wireless_device_connected_summary)
: "");
}
/**
* Writes the paired devices bound to this preference to the bundle.
*
* @param bundle the bundle to write the paired device to
*/
public void savePairedDeviceToExtras(Bundle bundle) {
bundle.putParcelable(PAIRED_DEVICE_EXTRA, mPairedDevice);
}
}

View File

@@ -0,0 +1,351 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.debug.AdbManager;
import android.debug.IAdbManager;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.SurfaceTexture;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.view.LayoutInflater;
import android.view.TextureView;
import android.view.TextureView.SurfaceTextureListener;
import android.view.View;
import android.view.ViewGroup;
import android.view.accessibility.AccessibilityEvent;
import android.widget.ProgressBar;
import android.widget.TextView;
import androidx.annotation.StringRes;
import com.android.settings.R;
import com.android.settings.wifi.dpp.WifiDppQrCodeBaseFragment;
import com.android.settings.wifi.dpp.WifiNetworkConfig;
import com.android.settings.wifi.dpp.WifiQrCode;
import com.android.settings.wifi.qrcode.QrCamera;
import com.android.settings.wifi.qrcode.QrDecorateView;
/**
* Fragment shown when clicking on the "Pair by QR code" preference in
* the Wireless Debugging fragment.
*/
public class AdbQrcodeScannerFragment extends WifiDppQrCodeBaseFragment implements
SurfaceTextureListener,
QrCamera.ScannerCallback {
private static final String TAG = "AdbQrcodeScannerFrag";
/** Message sent to hide error message */
private static final int MESSAGE_HIDE_ERROR_MESSAGE = 1;
/** Message sent to show error message */
private static final int MESSAGE_SHOW_ERROR_MESSAGE = 2;
private static final long SHOW_ERROR_MESSAGE_INTERVAL = 10000;
private static final long SHOW_SUCCESS_SQUARE_INTERVAL = 1000;
private ProgressBar mProgressBar;
private QrCamera mCamera;
private TextureView mTextureView;
private QrDecorateView mDecorateView;
private View mQrCameraView;
private View mVerifyingView;
private TextView mErrorMessage;
/** QR code data scanned by camera */
private WifiQrCode mAdbQrCode;
private WifiNetworkConfig mAdbConfig;
private IAdbManager mAdbManager;
private IntentFilter mIntentFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
Integer res = intent.getIntExtra(
AdbManager.WIRELESS_STATUS_EXTRA,
AdbManager.WIRELESS_STATUS_FAIL);
if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
Intent i = new Intent();
i.putExtra(
WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
WirelessDebuggingFragment.SUCCESS_ACTION);
getActivity().setResult(Activity.RESULT_OK, i);
getActivity().finish();
} else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
Intent i = new Intent();
i.putExtra(
WirelessDebuggingFragment.PAIRING_DEVICE_REQUEST_TYPE,
WirelessDebuggingFragment.FAIL_ACTION);
getActivity().setResult(Activity.RESULT_OK, i);
getActivity().finish();
} else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
Log.i(TAG, "Got Qr pairing code port=" + port);
}
}
}
};
private final Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case MESSAGE_HIDE_ERROR_MESSAGE:
mErrorMessage.setVisibility(View.INVISIBLE);
break;
case MESSAGE_SHOW_ERROR_MESSAGE:
final String errorMessage = (String) msg.obj;
mErrorMessage.setVisibility(View.VISIBLE);
mErrorMessage.setText(errorMessage);
mErrorMessage.sendAccessibilityEvent(
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
// Cancel any pending messages to hide error view and requeue the message so
// user has time to see error
removeMessages(MESSAGE_HIDE_ERROR_MESSAGE);
sendEmptyMessageDelayed(MESSAGE_HIDE_ERROR_MESSAGE,
SHOW_ERROR_MESSAGE_INTERVAL);
break;
default:
return;
}
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
}
@Override
public final View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.adb_qrcode_scanner_fragment, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mTextureView = (TextureView) view.findViewById(R.id.preview_view);
mTextureView.setSurfaceTextureListener(this);
mDecorateView = (QrDecorateView) view.findViewById(R.id.decorate_view);
setHeaderIconImageResource(R.drawable.ic_scan_24dp);
mProgressBar = view.findViewById(R.id.indeterminate_bar);
mProgressBar.setVisibility(View.INVISIBLE);
mQrCameraView = view.findViewById(R.id.camera_layout);
mVerifyingView = view.findViewById(R.id.verifying_layout);
mTitle.setText(R.string.wifi_dpp_scan_qr_code);
mSummary.setText(R.string.adb_wireless_qrcode_pairing_description);
mErrorMessage = view.findViewById(R.id.error_message);
}
@Override
public void onResume() {
super.onResume();
mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
getActivity().registerReceiver(mReceiver, mIntentFilter);
}
@Override
public void onPause() {
super.onPause();
getActivity().unregisterReceiver(mReceiver);
try {
mAdbManager.disablePairing();
} catch (RemoteException e) {
Log.e(TAG, "Unable to cancel pairing");
}
getActivity().finish();
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
// Do nothing
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
}
@Override
public int getMetricsCategory() {
return 0;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
initCamera(surface);
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
// Do nothing
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
destroyCamera();
return true;
}
@Override
public Size getViewSize() {
return new Size(mTextureView.getWidth(), mTextureView.getHeight());
}
@Override
public void setTransform(Matrix transform) {
mTextureView.setTransform(transform);
}
@Override
public Rect getFramePosition(Size previewSize, int cameraOrientation) {
return new Rect(0, 0, previewSize.getHeight(), previewSize.getHeight());
}
@Override
public boolean isValid(String qrCode) {
try {
// WIFI:T:ADB;S:myname;P:mypass;;
mAdbQrCode = new WifiQrCode(qrCode);
} catch (IllegalArgumentException e) {
showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
return false;
}
// Only accept the zxing format.
if (!WifiQrCode.SCHEME_ZXING_WIFI_NETWORK_CONFIG.equals(mAdbQrCode.getScheme())) {
showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
Log.w(TAG, "DPP format not supported for ADB QR code");
return false;
}
mAdbConfig = mAdbQrCode.getWifiNetworkConfig();
if (!WifiQrCode.SECURITY_ADB.equals(mAdbConfig.getSecurity())) {
showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
Log.w(TAG, "Invalid security type");
return false;
}
if (TextUtils.isEmpty(mAdbConfig.getSsid())) {
showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
Log.w(TAG, "Empty password");
return false;
}
if (TextUtils.isEmpty(mAdbConfig.getPreSharedKey())) {
showErrorMessage(R.string.wifi_dpp_could_not_detect_valid_qr_code);
Log.w(TAG, "Empty password");
return false;
}
return true;
}
@Override
public void handleSuccessfulResult(String qrCode) {
destroyCamera();
mDecorateView.setFocused(true);
mQrCameraView.setVisibility(View.GONE);
mVerifyingView.setVisibility(View.VISIBLE);
try {
mAdbManager.enablePairingByQrCode(mAdbConfig.getSsid(),
mAdbConfig.getPreSharedKey());
} catch (RemoteException e) {
Log.e(TAG, "Unable to enable QR code pairing");
getActivity().finish();
}
}
@Override
public void handleCameraFailure() {
destroyCamera();
}
private void initCamera(SurfaceTexture surface) {
// Check if the camera has alread been created.
if (mCamera == null) {
mCamera = new QrCamera(getContext(), this);
mCamera.start(surface);
}
}
/**
* To resume camera decoding task after handshake fail or Wi-Fi connection fail.
*/
private void restartCamera() {
if (mCamera == null) {
Log.d(TAG, "mCamera is not available for restarting camera");
return;
}
if (mCamera.isDecodeTaskAlive()) {
mCamera.stop();
}
final SurfaceTexture surfaceTexture = mTextureView.getSurfaceTexture();
if (surfaceTexture == null) {
throw new IllegalStateException("SurfaceTexture is not ready for restarting camera");
}
mCamera.start(surfaceTexture);
}
private void destroyCamera() {
if (mCamera != null) {
mCamera.stop();
mCamera = null;
}
}
private void showErrorMessage(@StringRes int messageResId) {
final Message message = mHandler.obtainMessage(MESSAGE_SHOW_ERROR_MESSAGE,
getString(messageResId));
message.sendToTarget();
}
}

View File

@@ -0,0 +1,157 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
/**
* Class to show a variety of dialogs for the Wireless debugging
* fragment.
*/
public class AdbWirelessDialog extends AlertDialog implements
AdbWirelessDialogUiBase,
DialogInterface.OnClickListener {
/**
* Interface for subscribers to implement in order to listen
* to AdbWirelessDialog events.
*/
public interface AdbWirelessDialogListener {
/**
* Called when the dialog was closed by clicking a negative button.
*/
default void onCancel() {
}
/**
* Called when the dialog was closed by clicking a positive button.
*
* @param dialog the dialog that was closed.
*/
default void onSubmit(AdbWirelessDialog dialog) {
}
/**
* Called when the dialog was dismissed.
*/
default void onDismiss() {
}
}
private static final String TAG = "AdbWirelessDialog";
private static final int BUTTON_CANCEL = DialogInterface.BUTTON_NEGATIVE;
private static final int BUTTON_SUBMIT = DialogInterface.BUTTON_POSITIVE;
private final AdbWirelessDialogListener mListener;
private final int mMode;
private View mView;
private AdbWirelessDialogController mController;
/**
* Creates a AdbWirelessDialog with no additional style. It displays as a dialog above the
* current view.
*/
public static AdbWirelessDialog createModal(
Context context,
AdbWirelessDialogListener listener,
int mode) {
return new AdbWirelessDialog(context, listener, mode);
}
AdbWirelessDialog(Context context, AdbWirelessDialogListener listener, int mode) {
super(context);
mListener = listener;
mMode = mode;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
mView = getLayoutInflater().inflate(R.layout.adb_wireless_dialog, null);
setView(mView);
mController = new AdbWirelessDialogController(this, mView, mMode);
super.onCreate(savedInstanceState);
}
@Override
protected void onStop() {
super.onStop();
dismiss();
if (mListener != null) {
mListener.onDismiss();
}
}
@Override
public void onClick(DialogInterface dialogInterface, int id) {
if (mListener != null) {
switch (id) {
case BUTTON_CANCEL:
mListener.onCancel();
break;
}
}
}
@Override
public AdbWirelessDialogController getController() {
return mController;
}
@Override
public void dispatchSubmit() {
if (mListener != null) {
mListener.onSubmit(this);
}
dismiss();
}
@Override
public int getMode() {
return mMode;
}
@Override
public Button getSubmitButton() {
return getButton(BUTTON_SUBMIT);
}
@Override
public Button getCancelButton() {
return getButton(BUTTON_NEGATIVE);
}
@Override
public void setSubmitButton(CharSequence text) {
setButton(BUTTON_SUBMIT, text, this);
}
@Override
public void setCancelButton(CharSequence text) {
setButton(BUTTON_NEGATIVE, text, this);
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.content.res.Resources;
import android.view.View;
import android.widget.TextView;
import com.android.settings.R;
/**
* The class for allowing UIs like {@link AdbWirelessDialog} and {@link AdbWirelessDialogUiBase} to
* share the logic for controlling buttons, text fields, etc.
*/
public class AdbWirelessDialogController {
private static final String TAG = "AdbWirelessDialogCtrl";
private final AdbWirelessDialogUiBase mUi;
private final View mView;
private int mMode;
// The dialog for showing the six-digit code
private TextView mPairingCodeTitle;
private TextView mSixDigitCode;
private TextView mIpAddr;
// The dialog for showing pairing failed message
private TextView mFailedMsg;
private Context mContext;
public AdbWirelessDialogController(AdbWirelessDialogUiBase parent, View view,
int mode) {
mUi = parent;
mView = view;
mMode = mode;
mContext = mUi.getContext();
final Resources res = mContext.getResources();
mSixDigitCode = mView.findViewById(R.id.pairing_code);
mIpAddr = mView.findViewById(R.id.ip_addr);
switch (mMode) {
case AdbWirelessDialogUiBase.MODE_PAIRING:
String title = res.getString(R.string.adb_pairing_device_dialog_title);
mUi.setTitle(title);
mView.findViewById(R.id.l_pairing_six_digit).setVisibility(View.VISIBLE);
mUi.setCancelButton(res.getString(R.string.cancel));
mUi.setCanceledOnTouchOutside(false);
break;
case AdbWirelessDialogUiBase.MODE_PAIRING_FAILED:
String msg = res.getString(R.string.adb_pairing_device_dialog_failed_msg);
mUi.setTitle(R.string.adb_pairing_device_dialog_failed_title);
mView.findViewById(R.id.l_pairing_failed).setVisibility(View.VISIBLE);
mFailedMsg = (TextView) mView.findViewById(R.id.pairing_failed_label);
mFailedMsg.setText(msg);
mUi.setSubmitButton(res.getString(R.string.okay));
break;
case AdbWirelessDialogUiBase.MODE_QRCODE_FAILED:
mUi.setTitle(R.string.adb_pairing_device_dialog_failed_title);
mView.findViewById(R.id.l_qrcode_pairing_failed).setVisibility(View.VISIBLE);
mUi.setSubmitButton(res.getString(R.string.okay));
break;
}
// After done view show and hide, request focus from parent view
mView.findViewById(R.id.l_adbwirelessdialog).requestFocus();
}
/**
* Set the pairing code UI text field to code.
*
* @param code the pairing code string
*/
public void setPairingCode(String code) {
mSixDigitCode.setText(code);
}
/**
* Set the Ip address UI text field to ipAddr.
*
* @param ipAddr the ip address string
*/
public void setIpAddr(String ipAddr) {
mIpAddr.setText(ipAddr);
}
}

View File

@@ -0,0 +1,126 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.Context;
import android.view.LayoutInflater;
import android.widget.Button;
/**
* Foundation interface glues between Activities and UIs like {@link AdbWirelessDialog}.
*/
public interface AdbWirelessDialogUiBase {
/**
* Dialog shown when pairing a device via six-digit code.
*/
int MODE_PAIRING = 0;
/**
* Dialog shown when connecting to a paired device failed.
*/
int MODE_CONNECTION_FAILED = 1;
/**
* Dialog shown when pairing failed.
*/
int MODE_PAIRING_FAILED = 2;
/**
* Dialog shown when QR code pairing failed.
*/
int MODE_QRCODE_FAILED = 3;
/**
* Gets the context for the dialog.
*
* @return the context for the dialog
*/
Context getContext();
/**
* Gets the controller for the dialog.
*
* @return the controller for the dialog.
*/
AdbWirelessDialogController getController();
/**
* Gets the layout for the dialog.
*
* @return the {@link LayoutInflater} for the dialog
*/
LayoutInflater getLayoutInflater();
/**
* Gets the dialog mode/ID.
*
* @return the mode of the dialog
*/
int getMode();
/**
* Sends a submit command to the dialog.
*/
void dispatchSubmit();
/**
* Enables if user can cancel a dialog by clicking outside of the dialog.
*
* @param cancel The flag indicating if can cancel by clicking outside
*/
void setCanceledOnTouchOutside(boolean cancel);
/**
* Sets the title of the dialog.
*
* @param id the string id
*/
void setTitle(int id);
/**
* Sets the title of the dialog.
*
* @param title the title string
*/
void setTitle(CharSequence title);
/**
* Sets the text for the submit button.
*
* @param text the submit text
*/
void setSubmitButton(CharSequence text);
/**
* Sets the text for the cancel button.
*
* @param text the cancel text
*/
void setCancelButton(CharSequence text);
/**
* Gets the button widget for the submit button.
*
* @return the submit {@link Button} widget
*/
Button getSubmitButton();
/**
* Gets the button widget for the cancel button.
*
* @return the cancel {@link Button} widget
*/
Button getCancelButton();
}

View File

@@ -417,6 +417,7 @@ public class DevelopmentSettingsDashboardFragment extends RestrictedDashboardFra
controllers.add(new DisableAutomaticUpdatesPreferenceController(context));
controllers.add(new AdbPreferenceController(context, fragment));
controllers.add(new ClearAdbKeysPreferenceController(context, fragment));
controllers.add(new WirelessDebuggingPreferenceController(context, lifecycle));
controllers.add(new LocalTerminalPreferenceController(context));
controllers.add(new BugReportInPowerPreferenceController(context));
controllers.add(new AutomaticSystemServerHeapDumpPreferenceController(context));

View File

@@ -0,0 +1,140 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.provider.Settings;
import android.util.Log;
import com.android.settings.widget.SwitchWidgetController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
/**
* Class to control the switch bar in the wireless debugging fragment.
*/
public class WirelessDebuggingEnabler implements SwitchWidgetController.OnSwitchChangeListener,
LifecycleObserver, OnResume, OnPause {
private static final String TAG = "WirelessDebuggingEnabler";
private final SwitchWidgetController mSwitchWidget;
private Context mContext;
private boolean mListeningToOnSwitchChange = false;
private OnEnabledListener mListener;
private final ContentResolver mContentResolver;
private final ContentObserver mSettingsObserver;
private final Handler mHandler = new Handler(Looper.getMainLooper());
public WirelessDebuggingEnabler(Context context, SwitchWidgetController switchWidget,
OnEnabledListener listener, Lifecycle lifecycle) {
mContext = context;
mSwitchWidget = switchWidget;
mSwitchWidget.setListener(this);
mSwitchWidget.setupView();
mListener = listener;
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mContentResolver = context.getContentResolver();
mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
Log.i(TAG, "ADB_WIFI_ENABLED=" + isAdbWifiEnabled());
onWirelessDebuggingEnabled(isAdbWifiEnabled());
}
};
}
private boolean isAdbWifiEnabled() {
return Settings.Global.getInt(mContentResolver, Settings.Global.ADB_WIFI_ENABLED,
AdbPreferenceController.ADB_SETTING_OFF)
!= AdbPreferenceController.ADB_SETTING_OFF;
}
/**
* Tears down the switch controller for the wireless debugging switch.
*/
public void teardownSwitchController() {
if (mListeningToOnSwitchChange) {
mSwitchWidget.stopListening();
mListeningToOnSwitchChange = false;
}
mSwitchWidget.teardownView();
}
@Override
public void onResume() {
if (!mListeningToOnSwitchChange) {
mSwitchWidget.startListening();
mListeningToOnSwitchChange = true;
}
onWirelessDebuggingEnabled(isAdbWifiEnabled());
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
mSettingsObserver);
}
@Override
public void onPause() {
if (mListeningToOnSwitchChange) {
mSwitchWidget.stopListening();
mListeningToOnSwitchChange = false;
}
mContentResolver.unregisterContentObserver(mSettingsObserver);
}
private void onWirelessDebuggingEnabled(boolean enabled) {
mSwitchWidget.setChecked(enabled);
if (mListener != null) {
mListener.onEnabled(enabled);
}
}
protected void writeAdbWifiSetting(boolean enabled) {
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_WIFI_ENABLED, enabled ? AdbPreferenceController.ADB_SETTING_ON
: AdbPreferenceController.ADB_SETTING_OFF);
}
@Override
public boolean onSwitchToggled(boolean isChecked) {
writeAdbWifiSetting(isChecked);
return true;
}
/**
* Interface for subscribers to implement in order to listen for
* wireless debugging state changes.
*/
public interface OnEnabledListener {
/**
* Called when wireless debugging state changes.
*
* @param enabled the state of wireless debugging
*/
void onEnabled(boolean enabled);
}
}

View File

@@ -0,0 +1,486 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.app.Activity;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.debug.AdbManager;
import android.debug.IAdbManager;
import android.debug.PairDevice;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceCategory;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.core.SubSettingLauncher;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.Indexable;
import com.android.settings.widget.SwitchBarController;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.FooterPreference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Fragment shown when clicking in the "Wireless Debugging" preference in
* the developer options.
*/
@SearchIndexable
public class WirelessDebuggingFragment extends DashboardFragment
implements Indexable, WirelessDebuggingEnabler.OnEnabledListener {
private static final String TAG = "WirelessDebuggingFrag";
// Activity result from clicking on a paired device.
private static final int PAIRED_DEVICE_REQUEST = 0;
public static final String PAIRED_DEVICE_REQUEST_TYPE = "request_type";
public static final int FORGET_ACTION = 0;
// Activity result from pairing a device.
private static final int PAIRING_DEVICE_REQUEST = 1;
public static final String PAIRING_DEVICE_REQUEST_TYPE = "request_type_pairing";
public static final int SUCCESS_ACTION = 0;
public static final int FAIL_ACTION = 1;
public static final String PAIRED_DEVICE_EXTRA = "paired_device";
public static final String DEVICE_NAME_EXTRA = "device_name";
public static final String IP_ADDR_EXTRA = "ip_addr";
private WirelessDebuggingEnabler mWifiDebuggingEnabler;
private static AdbIpAddressPreferenceController sAdbIpAddressPreferenceController;
// UI components
private static final String PREF_KEY_ADB_DEVICE_NAME = "adb_device_name_pref";
private static final String PREF_KEY_ADB_IP_ADDR = "adb_ip_addr_pref";
private static final String PREF_KEY_PAIRING_METHODS_CATEGORY = "adb_pairing_methods_category";
private static final String PREF_KEY_ADB_QRCODE_PAIRING = "adb_pair_method_qrcode_pref";
private static final String PREF_KEY_ADB_CODE_PAIRING = "adb_pair_method_code_pref";
private static final String PREF_KEY_PAIRED_DEVICES_CATEGORY = "adb_paired_devices_category";
private static final String PREF_KEY_FOOTER_CATEGORY = "adb_wireless_footer_category";
private Preference mDeviceNamePreference;
private Preference mIpAddrPreference;
private PreferenceCategory mPairingMethodsCategory;
private Preference mQrcodePairingPreference;
private Preference mCodePairingPreference;
private PreferenceCategory mPairedDevicesCategory;
private PreferenceCategory mFooterCategory;
private FooterPreference mOffMessagePreference;
// Map of paired devices, with the device GUID is the key
private Map<String, AdbPairedDevicePreference> mPairedDevicePreferences;
private IAdbManager mAdbManager;
private int mConnectionPort;
class PairingCodeDialogListener implements AdbWirelessDialog.AdbWirelessDialogListener {
@Override
public void onDismiss() {
Log.i(TAG, "onDismiss");
mPairingCodeDialog = null;
try {
mAdbManager.disablePairing();
} catch (RemoteException e) {
Log.e(TAG, "Unable to cancel pairing");
}
}
}
final PairingCodeDialogListener mPairingCodeDialogListener = new PairingCodeDialogListener();
AdbWirelessDialog mPairingCodeDialog;
private IntentFilter mIntentFilter;
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION.equals(action)) {
Map<String, PairDevice> newPairedDevicesList =
(HashMap<String, PairDevice>) intent.getSerializableExtra(
AdbManager.WIRELESS_DEVICES_EXTRA);
updatePairedDevicePreferences(newPairedDevicesList);
} else if (AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION.equals(action)) {
int status = intent.getIntExtra(AdbManager.WIRELESS_STATUS_EXTRA,
AdbManager.WIRELESS_STATUS_DISCONNECTED);
if (status == AdbManager.WIRELESS_STATUS_CONNECTED) {
int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
Log.i(TAG, "Got adbwifi port=" + port);
} else {
Log.i(TAG, "adbwifi server disconnected");
}
} else if (AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION.equals(action)) {
Integer res = intent.getIntExtra(
AdbManager.WIRELESS_STATUS_EXTRA,
AdbManager.WIRELESS_STATUS_FAIL);
if (res.equals(AdbManager.WIRELESS_STATUS_PAIRING_CODE)) {
String pairingCode = intent.getStringExtra(
AdbManager.WIRELESS_PAIRING_CODE_EXTRA);
if (mPairingCodeDialog != null) {
mPairingCodeDialog.getController().setPairingCode(pairingCode);
}
} else if (res.equals(AdbManager.WIRELESS_STATUS_SUCCESS)) {
removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
mPairingCodeDialog = null;
} else if (res.equals(AdbManager.WIRELESS_STATUS_FAIL)) {
removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
mPairingCodeDialog = null;
showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
} else if (res.equals(AdbManager.WIRELESS_STATUS_CONNECTED)) {
int port = intent.getIntExtra(AdbManager.WIRELESS_DEBUG_PORT_EXTRA, 0);
Log.i(TAG, "Got pairing code port=" + port);
String ipAddr = sAdbIpAddressPreferenceController.getIpv4Address() + ":" + port;
if (mPairingCodeDialog != null) {
mPairingCodeDialog.getController().setIpAddr(ipAddr);
}
}
}
}
};
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final SettingsActivity activity = (SettingsActivity) getActivity();
mWifiDebuggingEnabler = new WirelessDebuggingEnabler(activity,
new SwitchBarController(activity.getSwitchBar()), this,
getSettingsLifecycle());
}
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
addPreferences();
mIntentFilter = new IntentFilter(AdbManager.WIRELESS_DEBUG_PAIRED_DEVICES_ACTION);
mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_STATE_CHANGED_ACTION);
mIntentFilter.addAction(AdbManager.WIRELESS_DEBUG_PAIRING_RESULT_ACTION);
}
private void addPreferences() {
mDeviceNamePreference =
(Preference) findPreference(PREF_KEY_ADB_DEVICE_NAME);
mIpAddrPreference =
(Preference) findPreference(PREF_KEY_ADB_IP_ADDR);
mPairingMethodsCategory =
(PreferenceCategory) findPreference(PREF_KEY_PAIRING_METHODS_CATEGORY);
mCodePairingPreference =
(Preference) findPreference(PREF_KEY_ADB_CODE_PAIRING);
mCodePairingPreference.setOnPreferenceClickListener(preference -> {
showDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
return true;
});
mQrcodePairingPreference =
(Preference) findPreference(PREF_KEY_ADB_QRCODE_PAIRING);
mQrcodePairingPreference.setOnPreferenceClickListener(preference -> {
launchQrcodeScannerFragment();
return true;
});
mPairedDevicesCategory =
(PreferenceCategory) findPreference(PREF_KEY_PAIRED_DEVICES_CATEGORY);
mFooterCategory =
(PreferenceCategory) findPreference(PREF_KEY_FOOTER_CATEGORY);
mOffMessagePreference =
new FooterPreference(mFooterCategory.getContext());
final CharSequence title = getText(R.string.adb_wireless_list_empty_off);
mOffMessagePreference.setTitle(title);
mFooterCategory.addPreference(mOffMessagePreference);
}
@Override
public void onDestroyView() {
super.onDestroyView();
mWifiDebuggingEnabler.teardownSwitchController();
}
@Override
public void onResume() {
super.onResume();
getActivity().registerReceiver(mReceiver, mIntentFilter);
}
@Override
public void onPause() {
super.onPause();
removeDialog(AdbWirelessDialogUiBase.MODE_PAIRING);
getActivity().unregisterReceiver(mReceiver);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == PAIRED_DEVICE_REQUEST) {
handlePairedDeviceRequest(resultCode, data);
} else if (requestCode == PAIRING_DEVICE_REQUEST) {
handlePairingDeviceRequest(resultCode, data);
}
}
@Override
public int getMetricsCategory() {
return SettingsEnums.SETTINGS_ADB_WIRELESS;
}
@Override
public int getDialogMetricsCategory(int dialogId) {
return SettingsEnums.ADB_WIRELESS_DEVICE_PAIRING_DIALOG;
}
@Override
public Dialog onCreateDialog(int dialogId) {
Dialog d = AdbWirelessDialog.createModal(getActivity(),
dialogId == AdbWirelessDialogUiBase.MODE_PAIRING
? mPairingCodeDialogListener : null, dialogId);
if (dialogId == AdbWirelessDialogUiBase.MODE_PAIRING) {
mPairingCodeDialog = (AdbWirelessDialog) d;
try {
mAdbManager.enablePairingByPairingCode();
} catch (RemoteException e) {
Log.e(TAG, "Unable to enable pairing");
mPairingCodeDialog = null;
d = AdbWirelessDialog.createModal(getActivity(), null,
AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
}
}
if (d != null) {
return d;
}
return super.onCreateDialog(dialogId);
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.adb_wireless_settings;
}
@Override
protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
return buildPreferenceControllers(context, getActivity(), this /* fragment */,
getSettingsLifecycle());
}
private static List<AbstractPreferenceController> buildPreferenceControllers(
Context context, Activity activity, WirelessDebuggingFragment fragment,
Lifecycle lifecycle) {
final List<AbstractPreferenceController> controllers = new ArrayList<>();
sAdbIpAddressPreferenceController =
new AdbIpAddressPreferenceController(context, lifecycle);
controllers.add(sAdbIpAddressPreferenceController);
return controllers;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public void onEnabled(boolean enabled) {
if (enabled) {
showDebuggingPreferences();
mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(
Context.ADB_SERVICE));
try {
Map<String, PairDevice> newList = mAdbManager.getPairedDevices();
updatePairedDevicePreferences(newList);
mConnectionPort = mAdbManager.getAdbWirelessPort();
if (mConnectionPort > 0) {
Log.i(TAG, "onEnabled(): connect_port=" + mConnectionPort);
}
} catch (RemoteException e) {
Log.e(TAG, "Unable to request the paired list for Adb wireless");
}
sAdbIpAddressPreferenceController.updateState(mIpAddrPreference);
} else {
showOffMessage();
}
}
private void showOffMessage() {
mDeviceNamePreference.setVisible(false);
mIpAddrPreference.setVisible(false);
mPairingMethodsCategory.setVisible(false);
mPairedDevicesCategory.setVisible(false);
mFooterCategory.setVisible(true);
}
private void showDebuggingPreferences() {
mDeviceNamePreference.setVisible(true);
mIpAddrPreference.setVisible(true);
mPairingMethodsCategory.setVisible(true);
mPairedDevicesCategory.setVisible(true);
mFooterCategory.setVisible(false);
}
private void updatePairedDevicePreferences(Map<String, PairDevice> newList) {
// TODO(joshuaduong): Move the non-UI stuff into another thread
// as the processing could take some time.
if (newList == null) {
mPairedDevicesCategory.removeAll();
return;
}
if (mPairedDevicePreferences == null) {
mPairedDevicePreferences = new HashMap<String, AdbPairedDevicePreference>();
}
if (mPairedDevicePreferences.isEmpty()) {
for (Map.Entry<String, PairDevice> entry : newList.entrySet()) {
AdbPairedDevicePreference p =
new AdbPairedDevicePreference(entry.getValue(),
mPairedDevicesCategory.getContext());
mPairedDevicePreferences.put(
entry.getKey(),
p);
p.setOnPreferenceClickListener(preference -> {
AdbPairedDevicePreference pref =
(AdbPairedDevicePreference) preference;
launchPairedDeviceDetailsFragment(pref);
return true;
});
mPairedDevicesCategory.addPreference(p);
}
} else {
// Remove any devices no longer on the newList
mPairedDevicePreferences.entrySet().removeIf(entry -> {
if (newList.get(entry.getKey()) == null) {
mPairedDevicesCategory.removePreference(entry.getValue());
return true;
} else {
// It is in the newList. Just update the PairDevice value
AdbPairedDevicePreference p =
entry.getValue();
p.setPairedDevice(newList.get(entry.getKey()));
p.refresh();
return false;
}
});
// Add new devices if any.
for (Map.Entry<String, PairDevice> entry :
newList.entrySet()) {
if (mPairedDevicePreferences.get(entry.getKey()) == null) {
AdbPairedDevicePreference p =
new AdbPairedDevicePreference(entry.getValue(),
mPairedDevicesCategory.getContext());
mPairedDevicePreferences.put(
entry.getKey(),
p);
p.setOnPreferenceClickListener(preference -> {
AdbPairedDevicePreference pref =
(AdbPairedDevicePreference) preference;
launchPairedDeviceDetailsFragment(pref);
return true;
});
mPairedDevicesCategory.addPreference(p);
}
}
}
}
private void launchPairedDeviceDetailsFragment(AdbPairedDevicePreference p) {
// For sending to the device details fragment.
p.savePairedDeviceToExtras(p.getExtras());
new SubSettingLauncher(getContext())
.setTitleRes(R.string.adb_wireless_device_details_title)
.setDestination(AdbDeviceDetailsFragment.class.getName())
.setArguments(p.getExtras())
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, PAIRED_DEVICE_REQUEST)
.launch();
}
void handlePairedDeviceRequest(int result, Intent data) {
if (result != Activity.RESULT_OK) {
return;
}
Log.i(TAG, "Processing paired device request");
int requestType = data.getIntExtra(PAIRED_DEVICE_REQUEST_TYPE, -1);
PairDevice p;
switch (requestType) {
case FORGET_ACTION:
try {
p = (PairDevice) data.getParcelableExtra(PAIRED_DEVICE_EXTRA);
mAdbManager.unpairDevice(p.getGuid());
} catch (RemoteException e) {
Log.e(TAG, "Unable to forget the device");
}
break;
default:
break;
}
}
void handlePairingDeviceRequest(int result, Intent data) {
if (result != Activity.RESULT_OK) {
return;
}
int requestType = data.getIntExtra(PAIRING_DEVICE_REQUEST_TYPE, -1);
switch (requestType) {
case FAIL_ACTION:
showDialog(AdbWirelessDialogUiBase.MODE_PAIRING_FAILED);
break;
default:
break;
}
}
private String getDeviceName() {
// Keep device name in sync with Settings > About phone > Device name
String deviceName = Settings.Global.getString(getContext().getContentResolver(),
Settings.Global.DEVICE_NAME);
if (deviceName == null) {
deviceName = Build.MODEL;
}
return deviceName;
}
private void launchQrcodeScannerFragment() {
new SubSettingLauncher(getContext())
.setDestination(AdbQrcodeScannerFragment.class.getName())
.setSourceMetricsCategory(getMetricsCategory())
.setResultListener(this, PAIRING_DEVICE_REQUEST)
.launch();
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import android.content.ContentResolver;
import android.content.Context;
import android.database.ContentObserver;
import android.debug.IAdbManager;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.provider.Settings;
import android.util.Log;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.widget.MasterSwitchPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnPause;
import com.android.settingslib.core.lifecycle.events.OnResume;
import com.android.settingslib.development.DeveloperOptionsPreferenceController;
/**
* This controls the master switch controller in the developer options page for
* "Wireless debugging".
*/
public class WirelessDebuggingPreferenceController extends DeveloperOptionsPreferenceController
implements Preference.OnPreferenceChangeListener, PreferenceControllerMixin,
LifecycleObserver, OnResume, OnPause {
private static final String TAG = "WirelessDebugPrefCtrl";
private final IAdbManager mAdbManager;
private final ContentResolver mContentResolver;
private final ContentObserver mSettingsObserver;
private final Handler mHandler = new Handler(Looper.getMainLooper());
public static final String KEY_TOGGLE_ADB_WIRELESS = "toggle_adb_wireless";
public WirelessDebuggingPreferenceController(Context context, Lifecycle lifecycle) {
super(context);
if (lifecycle != null) {
lifecycle.addObserver(this);
}
mAdbManager = IAdbManager.Stub.asInterface(ServiceManager.getService(Context.ADB_SERVICE));
mSettingsObserver = new ContentObserver(mHandler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
updateState(mPreference);
}
};
mContentResolver = context.getContentResolver();
}
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
}
@Override
public boolean isAvailable() {
try {
return mAdbManager.isAdbWifiSupported();
} catch (RemoteException e) {
Log.e(TAG, "Unable to check if adb wifi is supported.", e);
}
return false;
}
@Override
public String getPreferenceKey() {
return KEY_TOGGLE_ADB_WIRELESS;
}
/**
* Called when developer options is enabled and the preference is available
*/
@Override
protected void onDeveloperOptionsSwitchEnabled() {
super.onDeveloperOptionsSwitchEnabled();
mPreference.setEnabled(true);
}
/**
* Called when developer options is disabled and the preference is available
*/
@Override
protected void onDeveloperOptionsSwitchDisabled() {
super.onDeveloperOptionsSwitchDisabled();
mPreference.setEnabled(false);
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_WIFI_ENABLED,
AdbPreferenceController.ADB_SETTING_OFF);
}
@Override
public void onResume() {
mContentResolver.registerContentObserver(
Settings.Global.getUriFor(Settings.Global.ADB_WIFI_ENABLED), false,
mSettingsObserver);
}
@Override
public void onPause() {
mContentResolver.unregisterContentObserver(mSettingsObserver);
}
@Override
public void updateState(Preference preference) {
boolean enabled = Settings.Global.getInt(mContentResolver,
Settings.Global.ADB_WIFI_ENABLED, AdbPreferenceController.ADB_SETTING_OFF)
!= AdbPreferenceController.ADB_SETTING_OFF;
((MasterSwitchPreference) preference).setChecked(enabled);
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
final boolean enabled = (Boolean) newValue;
Settings.Global.putInt(mContext.getContentResolver(),
Settings.Global.ADB_WIFI_ENABLED,
enabled ? AdbPreferenceController.ADB_SETTING_ON
: AdbPreferenceController.ADB_SETTING_OFF);
return true;
}
}

View File

@@ -68,6 +68,9 @@ public class WifiQrCode {
public static final String SECURITY_WEP = "WEP";
public static final String SECURITY_WPA_PSK = "WPA";
public static final String SECURITY_SAE = "SAE";
// Adb QR code pairing is in the following format:
// WIFI:T:ADB;S:myname;P:mypass;;
public static final String SECURITY_ADB = "ADB";
private String mQrCode;

View File

@@ -0,0 +1,160 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.content.Context;
import android.database.ContentObserver;
import android.provider.Settings.Global;
import androidx.lifecycle.LifecycleOwner;
import com.android.settings.SettingsActivity;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settings.widget.SwitchBar;
import com.android.settings.widget.SwitchBarController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowUtils.class)
public class WirelessDebuggingEnablerTest {
@Mock
private SettingsActivity mActivity;
@Mock
private SwitchBar mSwitchBar;
@Mock
private WirelessDebuggingEnabler.OnEnabledListener mListener;
private WirelessDebuggingEnabler mWirelessDebuggingEnabler;
private Context mContext;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mWirelessDebuggingEnabler = spy(new WirelessDebuggingEnabler(
mContext, new SwitchBarController(mSwitchBar), mListener, mLifecycle));
}
@Test
public void onCreation_shouldShowSwitchBar() {
verify(mSwitchBar).show();
}
@Test
public void teardownSwitchController_shouldHideSwitchBar() {
mWirelessDebuggingEnabler.teardownSwitchController();
verify(mSwitchBar).hide();
}
@Test
public void adbWifiEnabled_switchBarShouldBeChecked() {
// Set to disabled first otherwise we won't get the onChange() event
Global.putInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
mWirelessDebuggingEnabler.onResume();
verify(mSwitchBar).setChecked(false);
verify(mListener).onEnabled(false);
Global.putInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
final ContentObserver observer =
ReflectionHelpers.getField(mWirelessDebuggingEnabler, "mSettingsObserver");
observer.onChange(true, Global.getUriFor(Global.ADB_WIFI_ENABLED));
verify(mSwitchBar).setChecked(true);
// Should also get a callback
verify(mListener).onEnabled(true);
}
@Test
public void adbWifiEnabled_switchBarShouldNotBeChecked() {
Global.putInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
mWirelessDebuggingEnabler.onResume();
verify(mSwitchBar).setChecked(true);
verify(mListener).onEnabled(true);
Global.putInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
final ContentObserver observer =
ReflectionHelpers.getField(mWirelessDebuggingEnabler, "mSettingsObserver");
observer.onChange(true, Global.getUriFor(Global.ADB_WIFI_ENABLED));
verify(mSwitchBar).setChecked(false);
// Should also get a callback
verify(mListener).onEnabled(false);
}
@Test
public void onSwitchToggled_true_shouldSetAdbWifiEnabledTrue() {
Global.putInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
mWirelessDebuggingEnabler.onResume();
verify(mSwitchBar).setChecked(false);
verify(mListener).onEnabled(false);
mWirelessDebuggingEnabler.onSwitchToggled(true);
verify(mSwitchBar).setChecked(true);
verify(mListener).onEnabled(true);
assertThat(Global.getInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, -1)).isEqualTo(1);
// Should also get a callback
}
@Test
public void onSwitchToggled_false_shouldSetAdbWifiEnabledFalse() {
Global.putInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, 1 /* setting disabled */);
mWirelessDebuggingEnabler.onResume();
verify(mSwitchBar).setChecked(true);
verify(mListener).onEnabled(true);
mWirelessDebuggingEnabler.onSwitchToggled(false);
verify(mSwitchBar).setChecked(false);
verify(mListener).onEnabled(false);
assertThat(Global.getInt(mContext.getContentResolver(),
Global.ADB_WIFI_ENABLED, -1)).isEqualTo(0);
// Should also get a callback
}
}

View File

@@ -0,0 +1,120 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settings.development;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.ContentResolver;
import android.content.Context;
import android.debug.IAdbManager;
import android.os.RemoteException;
import android.provider.Settings.Global;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import com.android.settings.testutils.shadow.ShadowUtils;
import com.android.settings.widget.MasterSwitchPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.Config;
import org.robolectric.util.ReflectionHelpers;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowUtils.class)
public class WirelessDebuggingPreferenceControllerTest {
@Mock
private PreferenceScreen mScreen;
@Mock
private MasterSwitchPreference mPreference;
@Mock
private IAdbManager mAdbManager;
@Mock
private DevelopmentSettingsDashboardFragment mFragment;
private WirelessDebuggingPreferenceController mController;
private LifecycleOwner mLifecycleOwner;
private Lifecycle mLifecycle;
private Context mContext;
private ContentResolver mContentResolver;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mContext = RuntimeEnvironment.application;
mContentResolver = mContext.getContentResolver();
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
mController = spy(new WirelessDebuggingPreferenceController(mContext, mLifecycle));
ReflectionHelpers.setField(mController, "mAdbManager", mAdbManager);
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
}
@After
public void tearDown() {
}
@Test
public void isAvailable_isAdbWifiSupported_yes_shouldBeTrue() throws RemoteException {
when(mAdbManager.isAdbWifiSupported()).thenReturn(true);
assertThat(mController.isAvailable()).isTrue();
}
@Test
public void isAvailable_isAdbWifiSupported_shouldBeFalse() throws RemoteException {
when(mAdbManager.isAdbWifiSupported()).thenReturn(false);
assertThat(mController.isAvailable()).isFalse();
}
@Test
public void updateState_adbWifiEnabled_preferenceShouldBeChecked() {
Global.putInt(mContentResolver,
Global.ADB_WIFI_ENABLED, 1 /* setting enabled */);
mController.updateState(mPreference);
verify(mPreference).setChecked(true);
}
@Test
public void updateState_adbWifiDisabled_preferenceShouldNotBeChecked() {
Global.putInt(mContentResolver,
Global.ADB_WIFI_ENABLED, 0 /* setting disabled */);
mController.updateState(mPreference);
verify(mPreference).setChecked(false);
}
@Test
public void onPreferenceChange_turnOn_adbWifiEnabledTrue() {
mController.onPreferenceChange(null, true);
assertThat(Global.getInt(mContentResolver, Global.ADB_WIFI_ENABLED, -1)).isEqualTo(1);
}
}