diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 567400bdf9e..b68fca09fb4 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -1716,6 +1716,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 6ab0009f4b9..6965935e34b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -700,6 +700,10 @@
Enable widgets
Disabled by administrator
+
+ NFC unlock
+
+
@@ -4946,4 +4950,21 @@
+
+
+ NFC Screen Lock Settings
+ Paired Tags
+ Enabled
+ Pair with a tag
+
+
+ NfcPairingActivity
+ Pair now
+ Set the tag flat on a table and place the phone on top
+ Tag and phone successfully paired, you are done!
+ The tag provided cannot be used to unlock your phone
+ The pairing with your tag failed, please try again
+ NFC is not enabled and is required for NFC Unlock. Please turn on NFC.
+ NFC Settings
+
diff --git a/res/xml/security_settings_biometric_weak.xml b/res/xml/security_settings_biometric_weak.xml
index 7236673c178..c2e8ab7cb8d 100644
--- a/res/xml/security_settings_biometric_weak.xml
+++ b/res/xml/security_settings_biometric_weak.xml
@@ -61,6 +61,11 @@
android:title="@string/owner_info_settings_title"
android:summary="@string/owner_info_settings_summary"/>
+
+
diff --git a/res/xml/security_settings_chooser.xml b/res/xml/security_settings_chooser.xml
index d29d221f65c..1a5431593cb 100644
--- a/res/xml/security_settings_chooser.xml
+++ b/res/xml/security_settings_chooser.xml
@@ -36,6 +36,11 @@
android:title="@string/owner_info_settings_title"
android:summary="@string/owner_info_settings_summary"/>
+
+
diff --git a/res/xml/security_settings_nfc_unlock.xml b/res/xml/security_settings_nfc_unlock.xml
new file mode 100644
index 00000000000..c3442c604da
--- /dev/null
+++ b/res/xml/security_settings_nfc_unlock.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/xml/security_settings_password.xml b/res/xml/security_settings_password.xml
index 7cde2f5d428..a102efa9cb6 100644
--- a/res/xml/security_settings_password.xml
+++ b/res/xml/security_settings_password.xml
@@ -48,6 +48,11 @@
android:title="@string/owner_info_settings_title"
android:summary="@string/owner_info_settings_summary"/>
+
+
diff --git a/res/xml/security_settings_pattern.xml b/res/xml/security_settings_pattern.xml
index d8e06b3566a..0584e4ae247 100644
--- a/res/xml/security_settings_pattern.xml
+++ b/res/xml/security_settings_pattern.xml
@@ -52,6 +52,11 @@
android:title="@string/owner_info_settings_title"
android:summary="@string/owner_info_settings_summary"/>
+
+
diff --git a/res/xml/security_settings_pin.xml b/res/xml/security_settings_pin.xml
index 1e8fbbeaba0..1417e217853 100644
--- a/res/xml/security_settings_pin.xml
+++ b/res/xml/security_settings_pin.xml
@@ -48,6 +48,11 @@
android:title="@string/owner_info_settings_title"
android:summary="@string/owner_info_settings_summary"/>
+
+
diff --git a/src/com/android/settings/NfcLockFragment.java b/src/com/android/settings/NfcLockFragment.java
new file mode 100644
index 00000000000..b7bf6f768c6
--- /dev/null
+++ b/src/com/android/settings/NfcLockFragment.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 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 android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcUnlock;
+import android.os.Bundle;
+import android.preference.CheckBoxPreference;
+import android.preference.Preference;
+
+import android.preference.PreferenceCategory;
+import android.util.Log;
+import com.android.internal.widget.LockPatternUtils;
+
+import java.text.DateFormat;
+import java.util.Date;
+
+import static android.preference.Preference.OnPreferenceClickListener;
+
+public class NfcLockFragment extends SettingsPreferenceFragment {
+
+ private static final String NFC_PAIRING = "nfc_pairing";
+ private static final String NFC_UNLOCK_ENABLED = "nfc_unlock_enabled";
+ private static final String TAGS_CATEGORY = "nfc_unlock_tags_category";
+ private static final String TAG_FORMAT = "Tag # %d";
+
+ private NfcUnlock mNfcUnlock;
+ private LockPatternUtils mLockPatternUtils;
+ private NfcAdapter mNfcAdapter;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mNfcUnlock = NfcUnlock.getInstance(NfcAdapter.getDefaultAdapter(getActivity()));
+ mLockPatternUtils = new LockPatternUtils(getActivity());
+ mNfcAdapter = NfcAdapter.getDefaultAdapter(getActivity());
+ addPreferencesFromResource(R.xml.security_settings_nfc_unlock);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+
+ boolean prefsEnabled = (mLockPatternUtils.isLockPasswordEnabled() ||
+ mLockPatternUtils.isLockPatternEnabled()) && mNfcAdapter.isEnabled();
+ CheckBoxPreference unlockPref = (CheckBoxPreference) findPreference(NFC_UNLOCK_ENABLED);
+ unlockPref.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ mNfcUnlock.setNfcUnlockEnabled((Boolean) newValue);
+ return true;
+ }
+ });
+ Preference pairingPref = findPreference(NFC_PAIRING);
+ unlockPref.setEnabled(prefsEnabled);
+ pairingPref.setEnabled(prefsEnabled);
+
+ long[] tagRegistryTimes = mNfcUnlock.getTagRegistryTimes();
+ unlockPref.setChecked(mNfcUnlock.getNfcUnlockEnabled());
+
+ final PreferenceCategory pairedTags = (PreferenceCategory) findPreference(TAGS_CATEGORY);
+ pairedTags.setEnabled(prefsEnabled);
+
+ loadTagList(tagRegistryTimes, pairedTags);
+ }
+
+ private void loadTagList(long[] tagRegistryTimes, final PreferenceCategory pairedTags) {
+ pairedTags.removeAll();
+
+ for (int i = 0; i < tagRegistryTimes.length; i++) {
+
+ final Preference thisPreference = new Preference(getActivity());
+ final long timestamp = tagRegistryTimes[i];
+
+ thisPreference.setTitle(String.format(TAG_FORMAT, i));
+ thisPreference.setSummary(
+ DateFormat.getDateTimeInstance().format(new Date(tagRegistryTimes[i])));
+ thisPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+
+ AlertDialog.Builder deleteDialogBuilder = new AlertDialog.Builder(getActivity());
+
+ deleteDialogBuilder.setTitle(thisPreference.getTitle());
+ deleteDialogBuilder.setItems(new String[] {"Delete"},
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == 0) {
+ if (mNfcUnlock.deregisterTag(timestamp)) {
+ loadTagList(mNfcUnlock.getTagRegistryTimes(),
+ pairedTags);
+ }
+ }
+ }
+ });
+
+
+ deleteDialogBuilder.show();
+
+ return true;
+ }
+ });
+
+
+ pairedTags.addPreference(thisPreference);
+ }
+ }
+}
diff --git a/src/com/android/settings/PairNfcDevice.java b/src/com/android/settings/PairNfcDevice.java
new file mode 100644
index 00000000000..638f0d78ea2
--- /dev/null
+++ b/src/com/android/settings/PairNfcDevice.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2013 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 android.app.Activity;
+import android.app.AlertDialog;
+import android.app.PendingIntent;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.nfc.NfcAdapter;
+import android.nfc.NfcUnlock;
+import android.nfc.Tag;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.PowerManager;
+import android.provider.Settings;
+import android.view.Menu;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+public class PairNfcDevice extends Activity {
+ private static String TAG = PairNfcDevice.class.getName();
+
+ private TextView mStatusText;
+ private ImageView mStatusImage;
+
+ private PendingIntent mPendingIntent;
+ private NfcAdapter mAdapter;
+
+ private Handler mHandler = new Handler();
+ private PowerManager.WakeLock mWakeLock;
+
+ private NfcUnlock mNfcUnlock;
+
+ // If pairing fails, we immediately get a new intent that would not leave time for the user to
+ // read the error message. So we'll just drop it and the user has to try again.
+ // TEST
+ private boolean mWaitingForDeviceDelayed;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_nfc_pairing);
+
+ mStatusText = (TextView) findViewById(R.id.status_text);
+ mStatusImage = (ImageView) findViewById(R.id.status_image);
+
+ mAdapter = NfcAdapter.getDefaultAdapter(this);
+ mPendingIntent = PendingIntent.getActivity(
+ this, 0, new Intent(this, getClass()).addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP), 0);
+
+ mNfcUnlock = NfcUnlock.getInstance(mAdapter);
+
+ setWaitingForDeviceMode();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ return false;
+ }
+
+ public void onPause() {
+ super.onPause();
+ mAdapter.disableForegroundDispatch(this);
+ if (mWakeLock != null) {
+ mWakeLock.release();
+ }
+ }
+
+ public void onResume() {
+ super.onResume();
+
+ if (!mAdapter.isEnabled()) {
+ // We need the user to start NFC.
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(this);
+ dialogBuilder.setMessage(R.string.enable_nfc);
+ dialogBuilder.setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ startActivity(new Intent(Settings.ACTION_NFC_SETTINGS));
+ }
+ });
+ dialogBuilder.show();
+ }
+
+ mAdapter.enableForegroundDispatch(this, mPendingIntent, null, null);
+
+ if (mWakeLock == null) {
+ PowerManager pm = (PowerManager) getSystemService(POWER_SERVICE);
+ mWakeLock = pm.newWakeLock(PowerManager.SCREEN_BRIGHT_WAKE_LOCK, TAG);
+ }
+ mWakeLock.acquire();
+ }
+
+ @Override
+ public void onNewIntent(Intent intent) {
+ Tag tag = (Tag) intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
+ if (!mWaitingForDeviceDelayed) {
+ processTag(tag);
+ }
+ }
+
+ private void processTag(Tag tag) {
+ if (mNfcUnlock.registerTag(tag)) {
+ setPairingSucceededMode();
+ } else {
+ setPairingFailedMode();
+ }
+ }
+
+ private void setWaitingForDeviceModeDelayed(int delayInMs) {
+ mWaitingForDeviceDelayed = true;
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mWaitingForDeviceDelayed = false;
+ setWaitingForDeviceMode();
+ }
+ }, delayInMs);
+ }
+
+ private void setWaitingForDeviceMode() {
+ mStatusImage.setImageResource(R.drawable.no_ring_detected);
+ mStatusText.setText(R.string.status_no_ring_detected);
+ }
+
+ private void setPairingFailedMode() {
+ setErrorMode(R.string.status_error_pairing_failed);
+ }
+
+ private void setPairingSucceededMode() {
+ mStatusImage.setImageResource(R.drawable.ring_paired);
+ mStatusText.setText(R.string.status_device_paired);
+
+ // Automatically quit.
+ mHandler.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ finish();
+ }
+ }, 4000);
+ }
+
+ private void setErrorMode(int errorMsgResourceId) {
+ mStatusText.setText(errorMsgResourceId);
+ setWaitingForDeviceModeDelayed(2500);
+ }
+}
diff --git a/src/com/android/settings/SecuritySettings.java b/src/com/android/settings/SecuritySettings.java
index e4dcea1db7c..45c36ac295e 100644
--- a/src/com/android/settings/SecuritySettings.java
+++ b/src/com/android/settings/SecuritySettings.java
@@ -29,7 +29,9 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.UserInfo;
+import android.nfc.NfcUnlock;
import android.os.Bundle;
+import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.preference.CheckBoxPreference;
@@ -57,6 +59,7 @@ public class SecuritySettings extends RestrictedSettingsFragment
// Lock Settings
private static final String KEY_UNLOCK_SET_OR_CHANGE = "unlock_set_or_change";
+ private static final String KEY_NFC_UNLOCK_SET_OR_CHANGE = "nfc_unlock_set_or_change";
private static final String KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING =
"biometric_weak_improve_matching";
private static final String KEY_BIOMETRIC_WEAK_LIVELINESS = "biometric_weak_liveliness";
@@ -225,6 +228,17 @@ public class SecuritySettings extends RestrictedSettingsFragment
}
}
+ // don't display NFC unlock settings if the prop is not enabled
+ if (!NfcUnlock.getPropertyEnabled()) {
+ PreferenceGroup securityCategory =
+ (PreferenceGroup) root.findPreference(KEY_SECURITY_CATEGORY);
+
+ if (securityCategory != null) {
+ securityCategory.removePreference(
+ root.findPreference(KEY_NFC_UNLOCK_SET_OR_CHANGE));
+ }
+ }
+
// Append the rest of the settings
addPreferencesFromResource(R.xml.security_settings_misc);
@@ -520,6 +534,9 @@ public class SecuritySettings extends RestrictedSettingsFragment
if (KEY_UNLOCK_SET_OR_CHANGE.equals(key)) {
startFragment(this, "com.android.settings.ChooseLockGeneric$ChooseLockGenericFragment",
SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
+ } else if (KEY_NFC_UNLOCK_SET_OR_CHANGE.equals(key)) {
+ startFragment(this, "com.android.settings.NfcLockFragment",
+ SET_OR_CHANGE_LOCK_METHOD_REQUEST, null);
} else if (KEY_BIOMETRIC_WEAK_IMPROVE_MATCHING.equals(key)) {
ChooseLockSettingsHelper helper =
new ChooseLockSettingsHelper(this.getActivity(), this);