Merge "Replace SeekBar with LabeledSeekBar for Accessibility-related settings in order to adjust the nonsense read-out by talkback."
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 637e710..c40328e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1546,7 +1546,7 @@
<activity
android:name=".Settings$FingerprintSuggestionActivity"
android:label="@string/security_settings_fingerprint_preference_title"
- android:icon="@drawable/ic_fingerprint">
+ android:icon="@drawable/ic_suggestion_fingerprint">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.suggested.category.SETTINGS_ONLY" />
@@ -1626,7 +1626,7 @@
</activity>
<activity android:name=".Settings$FingerprintEnrollSuggestionActivity"
- android:icon="@drawable/ic_settings_security">
+ android:icon="@drawable/ic_suggestion_fingerprint">
<intent-filter android:priority="2">
<action android:name="android.intent.action.MAIN" />
<category android:name="com.android.settings.suggested.category.LOCK_SCREEN" />
@@ -1638,7 +1638,7 @@
<meta-data android:name="com.android.settings.title"
android:resource="@string/suggested_lock_settings_title" />
<meta-data android:name="com.android.settings.summary"
- android:resource="@string/suggested_lock_settings_summary" />
+ android:resource="@string/suggested_fingerprint_lock_settings_summary" />
</activity>
<activity android:name="ChooseLockGeneric$InternalActivity" android:exported="false"
diff --git a/res/drawable/ic_suggestion_fingerprint.xml b/res/drawable/ic_suggestion_fingerprint.xml
new file mode 100644
index 0000000..feebd94
--- /dev/null
+++ b/res/drawable/ic_suggestion_fingerprint.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2016 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.
+-->
+
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="32dp"
+ android:height="32dp"
+ android:tint="?android:attr/colorAccent"
+ android:viewportWidth="32.0"
+ android:viewportHeight="32.0">
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M23.7,5.9c-0.1,0.0 -0.2,0.0 -0.3,-0.1C21.0,4.5 18.6,3.9 16.0,3.9c-2.5,0.0 -4.6,0.6 -6.9,1.9C8.8,6.0 8.3,5.9 8.1,5.5C7.9,5.2 8.0,4.7 8.4,4.5c2.5,-1.4 4.9,-2.1 7.7,-2.1c2.8,0.0 5.4,0.7 8.0,2.1c0.4,0.2 0.5,0.6 0.3,1.0C24.2,5.7 24.0,5.9 23.7,5.9z"/>
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M5.3,13.2c-0.1,0.0 -0.3,0.0 -0.4,-0.1c-0.3,-0.2 -0.4,-0.7 -0.2,-1.0c1.3,-1.9 2.9,-3.4 4.9,-4.5c4.1,-2.2 9.3,-2.2 13.4,0.0c1.9,1.1 3.6,2.5 4.9,4.4c0.2,0.3 0.1,0.8 -0.2,1.0c-0.3,0.2 -0.8,0.1 -1.0,-0.2c-1.2,-1.7 -2.6,-3.0 -4.3,-4.0c-3.7,-2.0 -8.3,-2.0 -12.0,0.0c-1.7,0.9 -3.2,2.3 -4.3,4.0C5.7,13.1 5.5,13.2 5.3,13.2z"/>
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M13.3,29.6c-0.2,0.0 -0.4,-0.1 -0.5,-0.2c-1.1,-1.2 -1.7,-2.0 -2.6,-3.6c-0.9,-1.7 -1.4,-3.7 -1.4,-5.9c0.0,-4.1 3.3,-7.4 7.4,-7.4c4.1,0.0 7.4,3.3 7.4,7.4c0.0,0.4 -0.3,0.7 -0.7,0.7s-0.7,-0.3 -0.7,-0.7c0.0,-3.3 -2.7,-5.9 -5.9,-5.9c-3.3,0.0 -5.9,2.7 -5.9,5.9c0.0,2.0 0.4,3.8 1.2,5.2c0.8,1.6 1.4,2.2 2.4,3.3c0.3,0.3 0.3,0.8 0.0,1.0C13.7,29.5 13.5,29.6 13.3,29.6z"/>
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M22.6,27.1c-1.6,0.0 -2.9,-0.4 -4.1,-1.2c-1.9,-1.4 -3.1,-3.6 -3.1,-6.0c0.0,-0.4 0.3,-0.7 0.7,-0.7s0.7,0.3 0.7,0.7c0.0,1.9 0.9,3.7 2.5,4.8c0.9,0.6 1.9,1.0 3.2,1.0c0.3,0.0 0.8,0.0 1.3,-0.1c0.4,-0.1 0.8,0.2 0.8,0.6c0.1,0.4 -0.2,0.8 -0.6,0.8C23.4,27.1 22.8,27.1 22.6,27.1z"/>
+ <path
+ android:fillColor="#ffffff"
+ android:pathData="M20.0,29.9c-0.1,0.0 -0.1,0.0 -0.2,0.0c-2.1,-0.6 -3.4,-1.4 -4.8,-2.9c-1.8,-1.9 -2.8,-4.4 -2.8,-7.1c0.0,-2.2 1.8,-4.1 4.1,-4.1c2.2,0.0 4.1,1.8 4.1,4.1c0.0,1.4 1.2,2.6 2.6,2.6c1.4,0.0 2.6,-1.2 2.6,-2.6c0.0,-5.1 -4.2,-9.3 -9.3,-9.3c-3.6,0.0 -6.9,2.1 -8.4,5.4C7.3,17.1 7.0,18.4 7.0,19.8c0.0,1.1 0.1,2.7 0.9,4.9c0.1,0.4 -0.1,0.8 -0.4,0.9c-0.4,0.1 -0.8,-0.1 -0.9,-0.4c-0.6,-1.8 -0.9,-3.6 -0.9,-5.4c0.0,-1.6 0.3,-3.1 0.9,-4.4c1.7,-3.8 5.6,-6.3 9.8,-6.3c5.9,0.0 10.7,4.8 10.7,10.7c0.0,2.2 -1.8,4.1 -4.1,4.1s-4.0,-1.8 -4.0,-4.1c0.0,-1.4 -1.2,-2.6 -2.6,-2.6c-1.4,0.0 -2.6,1.2 -2.6,2.6c0.0,2.3 0.9,4.5 2.4,6.1c1.2,1.3 2.4,2.0 4.2,2.5c0.4,0.1 0.6,0.5 0.5,0.9C20.6,29.7 20.3,29.9 20.0,29.9z"/>
+</vector>
+
diff --git a/res/layout/locale_order_list.xml b/res/layout/locale_order_list.xml
index 97ba9a3..e210c65 100644
--- a/res/layout/locale_order_list.xml
+++ b/res/layout/locale_order_list.xml
@@ -25,20 +25,19 @@
android:id="@+id/dragList"
android:layout_width="match_parent"
android:layout_height="0dp"
- android:layout_weight="1"/>
+ android:layout_weight="1"
+ android:scrollbars="vertical"/>
- <TextView
+ <Button
android:id="@+id/add_language"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:clickable="true"
- android:contextClickable="true"
android:drawableStart="@drawable/ic_add_24dp"
android:drawablePadding="12dp"
- android:gravity="center_vertical"
- android:measureWithLargestChild="false"
- android:padding="16dp"
+ android:minHeight="64dp"
+ android:textAlignment="textStart"
android:text="@string/add_a_language"
+ style="@style/Base.Widget.AppCompat.Button.Borderless"
android:textAppearance="?android:attr/textAppearanceListItem"/>
</LinearLayout>
diff --git a/res/layout/wifi_dialog.xml b/res/layout/wifi_dialog.xml
index c5e554d..05c33d7 100644
--- a/res/layout/wifi_dialog.xml
+++ b/res/layout/wifi_dialog.xml
@@ -157,6 +157,24 @@
android:text="@string/wifi_do_not_validate_eap_server_warning" />
</LinearLayout>
+ <LinearLayout android:id="@+id/l_domain"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/wifi_item" >
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ style="@style/wifi_item_label"
+ android:text="@string/wifi_eap_domain" />
+
+ <EditText android:id="@+id/domain"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ style="@style/wifi_item_edit_content"
+ android:singleLine="true"
+ android:inputType="textNoSuggestions" />
+ </LinearLayout>
+
<LinearLayout android:id="@+id/l_user_cert"
android:layout_width="match_parent"
android:layout_height="wrap_content"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 76bc8b5..58815c6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -966,6 +966,9 @@
<!-- Summary for suggested actions for screen lock -->
<string name="suggested_lock_settings_summary">Protect your device</string>
+ <!-- Summary for suggested actions for settings up a fingerprint lock -->
+ <string name="suggested_fingerprint_lock_settings_summary">Unlock with your fingerprint</string>
+
<!-- Title for security picker to choose the unlock method: None/Pattern/PIN/Password [CHAR LIMIT=22] -->
<string name="lock_settings_picker_title">Choose screen lock</string>
@@ -1613,6 +1616,8 @@
<string name="please_select_phase2">Phase 2 authentication</string>
<!-- Label for the EAP CA certificate of the network -->
<string name="wifi_eap_ca_cert">CA certificate</string>
+ <!-- Label for the domain name that the EAP CA certificate(s) can be used to validate. -->
+ <string name="wifi_eap_domain">Domain</string>
<!-- Label for the EAP user certificate of the network -->
<string name="wifi_eap_user_cert">User certificate</string>
<!-- Label for the EAP identity of the network -->
@@ -3547,7 +3552,7 @@
<!-- Toast that settings for an application is failed to open. -->
<string name="failed_to_open_app_settings_toast">Failed to open settings for <xliff:g id="spell_application_name">%1$s</xliff:g></string>
- <!-- Title for the 'keyboard and input methods' preference category. [CHAR LIMIT=35] -->
+ <!-- Title for the 'keyboard and input methods' preference category. [CHAR LIMIT=45] -->
<string name="keyboard_and_input_methods_category">Keyboard and input methods</string>
<!-- Title for the 'virtual keyboard' preference sub-screen. [CHAR LIMIT=35] -->
<string name="virtual_keyboard_category">Virtual keyboard</string>
@@ -6329,7 +6334,9 @@
<!-- App notification summary with notifications enabled [CHAR LIMIT=40] -->
<string name="notifications_enabled">Normal</string>
<!-- App notification summary with notifications disabled [CHAR LIMIT=40] -->
- <string name="notifications_disabled">Block</string>
+ <string name="notifications_disabled">Fully Blocked</string>
+ <!-- App notification summary with notifications disabled [CHAR LIMIT=40] -->
+ <string name="notifications_partially_disabled">Partially Blocked</string>
<!-- App notification summary with 2 items [CHAR LIMIT=15] -->
<string name="notifications_two_items"><xliff:g id="notif_state" example="Priority">%1$s</xliff:g> / <xliff:g id="notif_state" example="Priority">%2$s</xliff:g></string>
<!-- App notification summary with 3 items [CHAR LIMIT=15] -->
diff --git a/src/com/android/settings/ConfirmDeviceCredentialActivity.java b/src/com/android/settings/ConfirmDeviceCredentialActivity.java
index 28c0515..16d0685 100644
--- a/src/com/android/settings/ConfirmDeviceCredentialActivity.java
+++ b/src/com/android/settings/ConfirmDeviceCredentialActivity.java
@@ -19,6 +19,8 @@
import android.app.Activity;
import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
@@ -72,6 +74,11 @@
Log.e(TAG, "Invalid intent extra", se);
}
}
+ // if the client app did not hand in a title and we are about to show the work challenge,
+ // check whether there is a policy setting the organization name and use that as title
+ if ((title == null) && Utils.isManagedProfile(UserManager.get(this), userId)) {
+ title = getTitleFromOrganizationName(userId);
+ }
ChooseLockSettingsHelper helper = new ChooseLockSettingsHelper(this);
if (!helper.launchConfirmationActivity(0 /* request code */, null /* title */, title,
details, false /* returnCredentials */, true /* isExternal */, userId)) {
@@ -84,4 +91,10 @@
private boolean isInternalActivity() {
return this instanceof ConfirmDeviceCredentialActivity.InternalActivity;
}
+
+ private String getTitleFromOrganizationName(int userId) {
+ DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+ return (dpm != null) ? dpm.getOrganizationNameForUser(userId) : null;
+ }
}
diff --git a/src/com/android/settings/TetherService.java b/src/com/android/settings/TetherService.java
index d4944ef..1d68e90 100644
--- a/src/com/android/settings/TetherService.java
+++ b/src/com/android/settings/TetherService.java
@@ -37,6 +37,7 @@
import android.util.ArrayMap;
import android.util.Log;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.TetherUtil;
import java.util.ArrayList;
@@ -46,7 +47,8 @@
private static final String TAG = "TetherService";
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
- private static final String EXTRA_RESULT = "EntitlementResult";
+ @VisibleForTesting
+ public static final String EXTRA_RESULT = "EntitlementResult";
// Activity results to match the activity provision protocol.
// Default to something not ok.
@@ -295,7 +297,7 @@
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "Got provision result " + intent);
- String provisionResponse = context.getResources().getString(
+ String provisionResponse = getResources().getString(
com.android.internal.R.string.config_mobile_hotspot_provision_response);
if (provisionResponse.equals(intent.getAction())) {
diff --git a/src/com/android/settings/applications/AppStateNotificationBridge.java b/src/com/android/settings/applications/AppStateNotificationBridge.java
index 6d057b3..c7b78af 100644
--- a/src/com/android/settings/applications/AppStateNotificationBridge.java
+++ b/src/com/android/settings/applications/AppStateNotificationBridge.java
@@ -63,7 +63,11 @@
@Override
public boolean filterApp(AppEntry info) {
- return info.extraInfo != null && ((AppRow) info.extraInfo).banned;
+ if (info == null) {
+ return false;
+ }
+ AppRow row = (AppRow) info.extraInfo;
+ return row.banned || row.bannedTopics;
}
};
}
diff --git a/src/com/android/settings/applications/InstalledAppDetails.java b/src/com/android/settings/applications/InstalledAppDetails.java
index f31fa72..83159e3 100755
--- a/src/com/android/settings/applications/InstalledAppDetails.java
+++ b/src/com/android/settings/applications/InstalledAppDetails.java
@@ -821,6 +821,8 @@
public static CharSequence getNotificationSummary(AppRow appRow, Context context) {
if (appRow.banned) {
return context.getString(R.string.notifications_disabled);
+ } else if (appRow.bannedTopics) {
+ return context.getString(R.string.notifications_partially_disabled);
}
return context.getString(R.string.notifications_enabled);
}
diff --git a/src/com/android/settings/datausage/DataUsageMeteredSettings.java b/src/com/android/settings/datausage/DataUsageMeteredSettings.java
index 25e1a07..eb43d47 100644
--- a/src/com/android/settings/datausage/DataUsageMeteredSettings.java
+++ b/src/com/android/settings/datausage/DataUsageMeteredSettings.java
@@ -14,6 +14,7 @@
package com.android.settings.datausage;
+import android.app.backup.BackupManager;
import android.content.Context;
import android.content.res.Resources;
import android.net.NetworkPolicy;
@@ -150,6 +151,8 @@
super.notifyChanged();
if (!mBinding) {
mPolicyEditor.setPolicyMetered(mTemplate, isChecked());
+ // Stage the backup of the SettingsProvider package which backs this up
+ BackupManager.dataChanged("com.android.providers.settings");
}
}
}
diff --git a/src/com/android/settings/notification/NotificationBackend.java b/src/com/android/settings/notification/NotificationBackend.java
index 292d247..8dd6a4c 100644
--- a/src/com/android/settings/notification/NotificationBackend.java
+++ b/src/com/android/settings/notification/NotificationBackend.java
@@ -54,6 +54,7 @@
row.appImportance = getImportance(row.pkg, row.uid, null);
row.appBypassDnd = getBypassZenMode(row.pkg, row.uid, null);
row.appSensitive = getSensitive(row.pkg, row.uid, null);
+ row.bannedTopics = hasBannedTopics(row.pkg, row.uid);
return row;
}
@@ -170,6 +171,15 @@
}
}
+ public boolean hasBannedTopics(String pkg, int uid) {
+ try {
+ return sINM.hasBannedTopics(pkg, uid);
+ } catch (Exception e) {
+ Log.w(TAG, "Error calling NoMan", e);
+ return false;
+ }
+ }
+
static class Row {
public String section;
}
@@ -186,6 +196,7 @@
public int appImportance;
public boolean appBypassDnd;
public boolean appSensitive;
+ public boolean bannedTopics;
}
public static class TopicRow extends AppRow {
diff --git a/src/com/android/settings/vpn2/VpnSettings.java b/src/com/android/settings/vpn2/VpnSettings.java
index cd24be8..4cbb3df 100644
--- a/src/com/android/settings/vpn2/VpnSettings.java
+++ b/src/com/android/settings/vpn2/VpnSettings.java
@@ -223,6 +223,8 @@
final List<LegacyVpnInfo> connectedLegacyVpns = getConnectedLegacyVpns();
final List<AppVpnInfo> connectedAppVpns = getConnectedAppVpns();
+ final Set<Integer> readOnlyUsers = getReadOnlyUserProfiles();
+
// Refresh the PreferenceGroup which lists VPNs
getActivity().runOnUiThread(new Runnable() {
@Override
@@ -233,11 +235,13 @@
for (VpnProfile profile : vpnProfiles) {
ConfigPreference p = findOrCreatePreference(profile);
p.setState(ConfigPreference.STATE_NONE);
+ p.setEnabled(!readOnlyUsers.contains(UserHandle.myUserId()));
updates.add(p);
}
for (AppVpnInfo app : vpnApps) {
AppPreference p = findOrCreatePreference(app);
p.setState(AppPreference.STATE_DISCONNECTED);
+ p.setEnabled(!readOnlyUsers.contains(app.userId));
updates.add(p);
}
@@ -417,6 +421,19 @@
return connections;
}
+ @WorkerThread
+ private Set<Integer> getReadOnlyUserProfiles() {
+ Set<Integer> result = new ArraySet<>();
+ for (UserHandle profile : mUserManager.getUserProfiles()) {
+ final int profileId = profile.getIdentifier();
+ if (mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_VPN, profile)
+ || mConnectivityManager.getAlwaysOnVpnPackageForUser(profileId) != null) {
+ result.add(profileId);
+ }
+ }
+ return result;
+ }
+
static List<AppVpnInfo> getVpnApps(Context context, boolean includeProfiles) {
List<AppVpnInfo> result = Lists.newArrayList();
diff --git a/src/com/android/settings/wifi/WifiConfigController.java b/src/com/android/settings/wifi/WifiConfigController.java
index 3bb1473..b3b284b 100644
--- a/src/com/android/settings/wifi/WifiConfigController.java
+++ b/src/com/android/settings/wifi/WifiConfigController.java
@@ -127,6 +127,7 @@
private Spinner mSecuritySpinner;
private Spinner mEapMethodSpinner;
private Spinner mEapCaCertSpinner;
+ private TextView mEapDomainView;
private Spinner mPhase2Spinner;
// Associated with mPhase2Spinner, one of mPhase2FullAdapter or mPhase2PeapAdapter
private ArrayAdapter<String> mPhase2Adapter;
@@ -414,7 +415,7 @@
if (mEapCaCertSpinner != null
&& mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE
&& ((String) mEapCaCertSpinner.getSelectedItem())
- .equals(mDoNotValidateEapServerString)) {
+ .equals(mDoNotValidateEapServerString)) {
// Display warning if user chooses not to validate the EAP server with a user-supplied
// CA certificate in an EAP network configuration.
mView.findViewById(R.id.no_ca_cert_warning).setVisibility(View.VISIBLE);
@@ -514,17 +515,22 @@
// Note: |caCert| should not be able to take the value |unspecifiedCert|,
// since we prevent such configurations from being saved.
config.enterpriseConfig.setCaCertificateAliases(null);
- } else if (caCert.equals(mMultipleCertSetString)) {
- if (mAccessPoint != null) {
- if (!mAccessPoint.isSaved()) {
- Log.e(TAG, "Multiple certs can only be set when editing saved network");
- }
- config.enterpriseConfig.setCaCertificateAliases(
- mAccessPoint.getConfig().enterpriseConfig
- .getCaCertificateAliases());
- }
} else {
- config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert});
+ config.enterpriseConfig.setDomainSuffixMatch(
+ mEapDomainView.getText().toString());
+ if (caCert.equals(mMultipleCertSetString)) {
+ if (mAccessPoint != null) {
+ if (!mAccessPoint.isSaved()) {
+ Log.e(TAG, "Multiple certs can only be set "
+ + "when editing saved network");
+ }
+ config.enterpriseConfig.setCaCertificateAliases(
+ mAccessPoint.getConfig().enterpriseConfig
+ .getCaCertificateAliases());
+ }
+ } else {
+ config.enterpriseConfig.setCaCertificateAliases(new String[] {caCert});
+ }
}
String clientCert = (String) mEapUserCertSpinner.getSelectedItem();
@@ -738,6 +744,7 @@
mPhase2Spinner = (Spinner) mView.findViewById(R.id.phase2);
mEapCaCertSpinner = (Spinner) mView.findViewById(R.id.ca_cert);
mEapCaCertSpinner.setOnItemSelectedListener(this);
+ mEapDomainView = (TextView) mView.findViewById(R.id.domain);
mEapUserCertSpinner = (Spinner) mView.findViewById(R.id.user_cert);
mEapUserCertSpinner.setOnItemSelectedListener(this);
mEapIdentityView = (TextView) mView.findViewById(R.id.identity);
@@ -787,6 +794,7 @@
Credentials.CA_CERTIFICATE, true, mDoNotValidateEapServerString);
mEapCaCertSpinner.setSelection(MULTIPLE_CERT_SET_INDEX);
}
+ mEapDomainView.setText(enterpriseConfig.getDomainSuffixMatch());
setSelection(mEapUserCertSpinner, enterpriseConfig.getClientCertificateAlias());
mEapIdentityView.setText(enterpriseConfig.getIdentity());
mEapAnonymousView.setText(enterpriseConfig.getAnonymousIdentity());
@@ -811,6 +819,7 @@
* EAP-TLS valid fields include
* user_cert
* ca_cert
+ * domain
* identity
* EAP-TTLS valid fields include
* phase2: PAP, MSCHAP, MSCHAPV2, GTC
@@ -823,6 +832,7 @@
// Common defaults
mView.findViewById(R.id.l_method).setVisibility(View.VISIBLE);
mView.findViewById(R.id.l_identity).setVisibility(View.VISIBLE);
+ mView.findViewById(R.id.l_domain).setVisibility(View.VISIBLE);
// Defaults for most of the EAP methods and over-riden by
// by certain EAP methods
@@ -835,6 +845,7 @@
case WIFI_EAP_METHOD_PWD:
setPhase2Invisible();
setCaCertInvisible();
+ setDomainInvisible();
setAnonymousIdentInvisible();
setUserCertInvisible();
break;
@@ -870,11 +881,22 @@
setPhase2Invisible();
setAnonymousIdentInvisible();
setCaCertInvisible();
+ setDomainInvisible();
setUserCertInvisible();
setPasswordInvisible();
setIdentityInvisible();
break;
}
+
+ if (mView.findViewById(R.id.l_ca_cert).getVisibility() != View.GONE) {
+ String eapCertSelection = (String) mEapCaCertSpinner.getSelectedItem();
+ if (eapCertSelection.equals(mDoNotValidateEapServerString)
+ || eapCertSelection.equals(mUnspecifiedCertString)) {
+ // Domain suffix matching is not relevant if the user hasn't chosen a CA
+ // certificate yet, or chooses not to validate the EAP server.
+ setDomainInvisible();
+ }
+ }
}
private void setIdentityInvisible() {
@@ -892,6 +914,11 @@
mEapCaCertSpinner.setSelection(UNSPECIFIED_CERT_INDEX);
}
+ private void setDomainInvisible() {
+ mView.findViewById(R.id.l_domain).setVisibility(View.GONE);
+ mEapDomainView.setText("");
+ }
+
private void setUserCertInvisible() {
mView.findViewById(R.id.l_user_cert).setVisibility(View.GONE);
mEapUserCertSpinner.setSelection(UNSPECIFIED_CERT_INDEX);
@@ -1120,7 +1147,7 @@
if (parent == mSecuritySpinner) {
mAccessPointSecurity = position;
showSecurityFields();
- } else if (parent == mEapMethodSpinner) {
+ } else if (parent == mEapMethodSpinner || parent == mEapCaCertSpinner) {
showSecurityFields();
} else if (parent == mProxySettingsSpinner) {
showProxyFields();
diff --git a/tests/unit/src/com/android/settings/TetherServiceTest.java b/tests/unit/src/com/android/settings/TetherServiceTest.java
new file mode 100644
index 0000000..09c6119
--- /dev/null
+++ b/tests/unit/src/com/android/settings/TetherServiceTest.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2016 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;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.*;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+import static android.net.ConnectivityManager.EXTRA_ADD_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_PROVISION_CALLBACK;
+import static android.net.ConnectivityManager.EXTRA_REM_TETHER_TYPE;
+import static android.net.ConnectivityManager.EXTRA_RUN_PROVISION;
+import static android.net.ConnectivityManager.EXTRA_SET_ALARM;
+import static android.net.ConnectivityManager.TETHERING_BLUETOOTH;
+import static android.net.ConnectivityManager.TETHERING_INVALID;
+import static android.net.ConnectivityManager.TETHERING_USB;
+import static android.net.ConnectivityManager.TETHERING_WIFI;
+import static android.net.ConnectivityManager.TETHER_ERROR_NO_ERROR;
+import static android.net.ConnectivityManager.TETHER_ERROR_PROVISION_FAILED;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.ContextWrapper;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+import android.content.res.Resources;
+import android.net.ConnectivityManager;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Bundle;
+import android.os.ResultReceiver;
+import android.os.SystemClock;
+import android.test.ServiceTestCase;
+import android.test.mock.MockResources;
+import android.util.Log;
+
+import com.android.settings.TetherService;
+
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.lang.ref.WeakReference;
+
+public class TetherServiceTest extends ServiceTestCase<TetherService> {
+
+ private static final String TAG = "TetherServiceTest";
+ private static final String TEST_RESPONSE_ACTION = "testProvisioningResponseAction";
+ private static final String TEST_NO_UI_ACTION = "testNoUiProvisioningRequestAction";
+ private static final int BOGUS_RECEIVER_RESULT = -5;
+ private static final int TEST_CHECK_PERIOD = 100;
+ private static final int MS_PER_HOUR = 60 * 60 * 1000;
+ private static final int SHORT_TIMEOUT = 100;
+ private static final int PROVISION_TIMEOUT = 1000;
+
+ private TetherService mService;
+ private MockResources mResources;
+ int mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
+ private int mLastTetherRequestType = TETHERING_INVALID;
+ private int mProvisionResponse = BOGUS_RECEIVER_RESULT;
+ private ProvisionReceiver mProvisionReceiver;
+ private Receiver mResultReceiver;
+
+ @Mock private AlarmManager mAlarmManager;
+ @Mock private ConnectivityManager mConnectivityManager;
+ @Mock private WifiManager mWifiManager;
+ @Mock private SharedPreferences mPrefs;
+ @Mock private Editor mPrefEditor;
+ @Captor private ArgumentCaptor<PendingIntent> mPiCaptor;
+ @Captor private ArgumentCaptor<String> mStoredTypes;
+
+ public TetherServiceTest() {
+ super(TetherService.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+
+ mResources = new MockResources();
+ mContext = new TestContextWrapper(getContext());
+ setContext(mContext);
+
+ mResultReceiver = new Receiver(this);
+ mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
+ mProvisionResponse = Activity.RESULT_OK;
+ mProvisionReceiver = new ProvisionReceiver();
+ IntentFilter filter = new IntentFilter(TEST_NO_UI_ACTION);
+ filter.addCategory(Intent.CATEGORY_DEFAULT);
+ mContext.registerReceiver(mProvisionReceiver, filter);
+
+ final String CURRENT_TYPES = "currentTethers";
+ when(mPrefs.getString(CURRENT_TYPES, "")).thenReturn("");
+ when(mPrefs.edit()).thenReturn(mPrefEditor);
+ when(mPrefEditor.putString(eq(CURRENT_TYPES), mStoredTypes.capture())).thenReturn(
+ mPrefEditor);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mContext.unregisterReceiver(mProvisionReceiver);
+ super.tearDown();
+ }
+
+ private void cancelAllProvisioning() {
+ int[] types = new int[]{TETHERING_BLUETOOTH, TETHERING_WIFI, TETHERING_USB};
+ for (int type : types) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_REM_TETHER_TYPE, type);
+ startService(intent);
+ }
+ }
+
+ public void testStartForProvision() {
+ runProvisioningForType(TETHERING_WIFI);
+
+ assertTrue(waitForProvisionRequest(TETHERING_WIFI));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
+ }
+
+ public void testScheduleRechecks() {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, TETHERING_WIFI);
+ intent.putExtra(EXTRA_SET_ALARM, true);
+ startService(intent);
+
+ long period = TEST_CHECK_PERIOD * MS_PER_HOUR;
+ verify(mAlarmManager).setRepeating(eq(AlarmManager.ELAPSED_REALTIME), anyLong(),
+ eq(period), mPiCaptor.capture());
+ PendingIntent pi = mPiCaptor.getValue();
+ assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
+ }
+
+ public void testStartMultiple() {
+ runProvisioningForType(TETHERING_WIFI);
+
+ assertTrue(waitForProvisionRequest(TETHERING_WIFI));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
+
+ runProvisioningForType(TETHERING_USB);
+
+ assertTrue(waitForProvisionRequest(TETHERING_USB));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
+
+ runProvisioningForType(TETHERING_BLUETOOTH);
+
+ assertTrue(waitForProvisionRequest(TETHERING_BLUETOOTH));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
+ }
+
+ public void testPersistTypes() {
+ runProvisioningForType(TETHERING_WIFI);
+
+ waitForProvisionRequest(TETHERING_WIFI);
+ waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
+
+ runProvisioningForType(TETHERING_BLUETOOTH);
+
+ waitForProvisionRequest(TETHERING_BLUETOOTH);
+ waitForProvisionResponse(TETHER_ERROR_NO_ERROR);
+
+ shutdownService();
+ assertEquals(TETHERING_WIFI + "," + TETHERING_BLUETOOTH, mStoredTypes.getValue());
+ }
+
+ public void testFailureStopsTethering_Wifi() {
+ mProvisionResponse = Activity.RESULT_CANCELED;
+
+ runProvisioningForType(TETHERING_WIFI);
+
+ assertTrue(waitForProvisionRequest(TETHERING_WIFI));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
+
+ verify(mWifiManager).setWifiApEnabled(isNull(WifiConfiguration.class), eq(false));
+ }
+
+ public void testFailureStopsTethering_Usb() {
+ mProvisionResponse = Activity.RESULT_CANCELED;
+
+ runProvisioningForType(TETHERING_USB);
+
+ assertTrue(waitForProvisionRequest(TETHERING_USB));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_PROVISION_FAILED));
+
+ verify(mConnectivityManager).setUsbTethering(eq(false));
+ }
+
+ public void testCancelAlarm() {
+ runProvisioningForType(TETHERING_WIFI);
+
+ assertTrue(waitForProvisionRequest(TETHERING_WIFI));
+ assertTrue(waitForProvisionResponse(TETHER_ERROR_NO_ERROR));
+
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_REM_TETHER_TYPE, TETHERING_WIFI);
+ startService(intent);
+
+ verify(mAlarmManager).cancel(mPiCaptor.capture());
+ PendingIntent pi = mPiCaptor.getValue();
+ assertEquals(TetherService.class.getName(), pi.getIntent().getComponent().getClassName());
+ }
+
+ private void runProvisioningForType(int type) {
+ Intent intent = new Intent();
+ intent.putExtra(EXTRA_ADD_TETHER_TYPE, type);
+ intent.putExtra(EXTRA_RUN_PROVISION, true);
+ intent.putExtra(EXTRA_PROVISION_CALLBACK, mResultReceiver);
+ startService(intent);
+ }
+
+ private boolean waitForProvisionRequest(int expectedType) {
+ long startTime = SystemClock.uptimeMillis();
+ while (true) {
+ if (mLastTetherRequestType == expectedType) {
+ mLastTetherRequestType = -1;
+ return true;
+ }
+ if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
+ Log.v(TAG, String.format(
+ "waitForProvisionRequest timeout: expected=%d, actual=%d",
+ expectedType, mLastTetherRequestType));
+ return false;
+ }
+ SystemClock.sleep(SHORT_TIMEOUT);
+ }
+ }
+
+ private boolean waitForProvisionResponse(int expectedValue) {
+ long startTime = SystemClock.uptimeMillis();
+ while (true) {
+ if (mLastReceiverResultCode == expectedValue) {
+ mLastReceiverResultCode = BOGUS_RECEIVER_RESULT;
+ return true;
+ }
+ if ((SystemClock.uptimeMillis() - startTime) > PROVISION_TIMEOUT) {
+ Log.v(TAG, String.format(
+ "waitForProvisionResponse timeout: expected=%d, actual=%d",
+ expectedValue, mLastReceiverResultCode));
+ return false;
+ }
+ SystemClock.sleep(SHORT_TIMEOUT);
+ }
+ }
+
+ private static class MockResources extends android.test.mock.MockResources {
+ @Override
+ public int getInteger(int id) {
+ switch(id) {
+ case com.android.internal.R.integer.config_mobile_hotspot_provision_check_period:
+ return TEST_CHECK_PERIOD;
+ default:
+ return 0;
+ }
+ }
+
+ @Override
+ public String getString(int id) {
+ switch(id) {
+ case com.android.internal.R.string.config_mobile_hotspot_provision_response:
+ return TEST_RESPONSE_ACTION;
+ case com.android.internal.R.string.config_mobile_hotspot_provision_app_no_ui:
+ return TEST_NO_UI_ACTION;
+ default:
+ return null;
+ }
+ }
+ }
+
+ private class TestContextWrapper extends ContextWrapper {
+
+ public TestContextWrapper(Context base) {
+ super(base);
+ }
+
+ @Override
+ public Resources getResources() {
+ return mResources;
+ }
+
+ @Override
+ public SharedPreferences getSharedPreferences(String name, int mode) {
+ // Stub out prefs to control the persisted tether type list.
+ if (name == "tetherPrefs") {
+ return mPrefs;
+ }
+ return super.getSharedPreferences(name, mode);
+ }
+
+ @Override
+ public Object getSystemService(String name) {
+ if (ALARM_SERVICE.equals(name)) {
+ return mAlarmManager;
+ } else if (CONNECTIVITY_SERVICE.equals(name)) {
+ return mConnectivityManager;
+ } else if (WIFI_SERVICE.equals(name)) {
+ return mWifiManager;
+ }
+
+ return super.getSystemService(name);
+ }
+ }
+
+ private static final class Receiver extends ResultReceiver {
+ final WeakReference<TetherServiceTest> mTest;
+
+ Receiver(TetherServiceTest test) {
+ super(null);
+ mTest = new WeakReference<TetherServiceTest>(test);
+ }
+
+ @Override
+ protected void onReceiveResult(int resultCode, Bundle resultData) {
+ TetherServiceTest test = mTest.get();
+ if (test != null) {
+ test.mLastReceiverResultCode = resultCode;
+ }
+ }
+ };
+
+ /**
+ * Stubs out the provisioning app receiver.
+ */
+ private class ProvisionReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mLastTetherRequestType = intent.getIntExtra("TETHER_TYPE", TETHERING_INVALID);
+ sendResponse(mProvisionResponse, context);
+ }
+
+ private void sendResponse(int response, Context context) {
+ Intent responseIntent = new Intent(TEST_RESPONSE_ACTION);
+ responseIntent.putExtra(TetherService.EXTRA_RESULT, response);
+ context.sendBroadcast(
+ responseIntent, android.Manifest.permission.CONNECTIVITY_INTERNAL);
+ }
+ }
+}