Adding INfcUnlockSettings and NfcUnlock interface class.

Change-Id: Ie55a5d4bb58c2944952fc84cce32d3573a3a1a22
diff --git a/Android.mk b/Android.mk
index 8fcac91..9b0ac27 100644
--- a/Android.mk
+++ b/Android.mk
@@ -152,6 +152,7 @@
 	core/java/android/nfc/INfcAdapterExtras.aidl \
 	core/java/android/nfc/INfcTag.aidl \
 	core/java/android/nfc/INfcCardEmulation.aidl \
+	core/java/android/nfc/INfcUnlockSettings.aidl \
 	core/java/android/os/IBatteryPropertiesListener.aidl \
 	core/java/android/os/IBatteryPropertiesRegistrar.aidl \
 	core/java/android/os/ICancellationSignal.aidl \
diff --git a/api/current.txt b/api/current.txt
index 11a5f77..7742be2 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -15540,6 +15540,11 @@
     method public android.nfc.NfcAdapter getDefaultAdapter();
   }
 
+  public class NfcUnlock {
+    method public static synchronized android.nfc.NfcUnlock getInstance(android.nfc.NfcAdapter);
+    method public boolean getNfcUnlockEnabled();
+  }
+
   public final class Tag implements android.os.Parcelable {
     method public int describeContents();
     method public byte[] getId();
@@ -21696,6 +21701,7 @@
     field public static final java.lang.String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
     field public static final deprecated java.lang.String LOGGING_ID = "logging_id";
     field public static final deprecated java.lang.String NETWORK_PREFERENCE = "network_preference";
+    field public static final java.lang.String NFC_UNLOCK_ENABLED = "nfc_unlock_enabled";
     field public static final java.lang.String PARENTAL_CONTROL_ENABLED = "parental_control_enabled";
     field public static final java.lang.String PARENTAL_CONTROL_LAST_UPDATE = "parental_control_last_update";
     field public static final java.lang.String PARENTAL_CONTROL_REDIRECT_URL = "parental_control_redirect_url";
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 8414738..10988c6 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -25,6 +25,7 @@
 import android.nfc.INfcAdapterExtras;
 import android.nfc.INfcTag;
 import android.nfc.INfcCardEmulation;
+import android.nfc.INfcUnlockSettings;
 import android.os.Bundle;
 
 /**
@@ -35,6 +36,7 @@
     INfcTag getNfcTagInterface();
     INfcCardEmulation getNfcCardEmulationInterface();
     INfcAdapterExtras getNfcAdapterExtrasInterface(in String pkg);
+    INfcUnlockSettings getNfcUnlockSettingsInterface();
 
     int getState();
     boolean disable(boolean saveState);
diff --git a/core/java/android/nfc/INfcUnlockSettings.aidl b/core/java/android/nfc/INfcUnlockSettings.aidl
new file mode 100644
index 0000000..649eeed
--- /dev/null
+++ b/core/java/android/nfc/INfcUnlockSettings.aidl
@@ -0,0 +1,70 @@
+/*
+ * 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 android.nfc;
+
+import android.nfc.Tag;
+import java.util.List;
+
+/**
+ * Interface to NFC unlock functionality.
+ *
+ * @hide
+ */
+interface INfcUnlockSettings {
+
+    /**
+     * Checks the validity of the tag and attempts to unlock the screen.
+     *
+     * @return true if the screen was successfuly unlocked.
+     */
+    boolean tryUnlock(int userId, in Tag tag);
+
+    /**
+     * Registers the given tag as an unlock tag. Subsequent calls to {@code tryUnlock}
+     * with the same {@code tag} should succeed.
+     *
+     * @return true if the tag was successfully registered.
+     */
+    boolean registerTag(int userId, in Tag tag);
+
+    /**
+     * Deregisters the tag with the corresponding timestamp.
+     * Subsequent calls to {@code tryUnlock} with the same tag should fail.
+     *
+     * @return true if the tag was successfully deleted.
+     */
+    boolean deregisterTag(int userId, long timestamp);
+
+    /**
+     * Used for user-visible rendering of registered tags.
+     *
+     * @return a list of the times in millis since epoch when the registered tags were paired.
+     */
+    long[] getTagRegistryTimes(int userId);
+
+    /**
+     * Determines the state of the NFC unlock feature.
+     *
+     * @return true if NFC unlock is enabled.
+     */
+    boolean getNfcUnlockEnabled(int userId);
+
+    /**
+     * Sets the state [ON | OFF] of the NFC unlock feature.
+     */
+    void setNfcUnlockEnabled(int userId, boolean enabled);
+}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 6743c6c..e8b7437 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -292,6 +292,7 @@
     static INfcAdapter sService;
     static INfcTag sTagService;
     static INfcCardEmulation sCardEmulationService;
+    static INfcUnlockSettings sNfcUnlockSettingsService;
 
     /**
      * The NfcAdapter object for each application context.
@@ -432,6 +433,13 @@
                 throw new UnsupportedOperationException();
             }
 
+            try {
+               sNfcUnlockSettingsService = sService.getNfcUnlockSettingsInterface();
+            } catch (RemoteException e) {
+                Log.e(TAG, "could not retrieve NFC unlock settings service");
+                sNfcUnlockSettingsService = null;
+            }
+
             sIsInitialized = true;
         }
         if (context == null) {
@@ -549,6 +557,22 @@
     }
 
     /**
+     * Returns the binder interface to the NFC unlock service.
+     *
+     * @throws UnsupportedOperationException if the service is not available.
+     * @hide
+     */
+    public INfcUnlockSettings getNfcUnlockSettingsService() throws UnsupportedOperationException {
+         isEnabled();
+
+        if (sNfcUnlockSettingsService == null) {
+            throw new UnsupportedOperationException("NfcUnlockSettingsService not available");
+        }
+
+        return sNfcUnlockSettingsService;
+    }
+
+    /**
      * NFC service dead - attempt best effort recovery
      * @hide
      */
diff --git a/core/java/android/nfc/NfcUnlock.java b/core/java/android/nfc/NfcUnlock.java
new file mode 100644
index 0000000..82dcd96
--- /dev/null
+++ b/core/java/android/nfc/NfcUnlock.java
@@ -0,0 +1,255 @@
+/*
+ * 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 android.nfc;
+
+import static com.android.internal.util.Preconditions.checkNotNull;
+
+import android.annotation.Nullable;
+import android.app.ActivityManager;
+import android.content.Context;
+import android.os.RemoteException;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * Provides an interface to read and update NFC unlock settings.
+ * <p/>
+ * Allows system services (currently exclusively LockSettingsService) to
+ * register NFC tags to be used to unlock the device, as well as the ability
+ * to enable/disable the service entirely.
+ *
+ */
+public class NfcUnlock {
+
+    /**
+     * Action to unlock the device.
+     *
+     * @hide
+     */
+    public static final String ACTION_NFC_UNLOCK = "android.nfc.ACTION_NFC_UNLOCK";
+    /**
+     * Permission to unlock the device.
+     *
+     * @hide
+     */
+    public static final String NFC_UNLOCK_PERMISSION = "android.permission.NFC_UNLOCK";
+
+    /**
+     * Property to enable NFC Unlock
+     *
+     * @hide
+     */
+    public static final String PROPERTY = "ro.com.android.nfc.unlock";
+
+    private static final String TAG = "NfcUnlock";
+    private static HashMap<Context, NfcUnlock> sNfcUnlocks = new HashMap<Context, NfcUnlock>();
+
+    private final Context mContext;
+    private final boolean mEnabled;
+    private INfcUnlockSettings sService;
+
+    private NfcUnlock(Context context, INfcUnlockSettings service) {
+        this.mContext = checkNotNull(context);
+        this.sService = checkNotNull(service);
+        this.mEnabled = getPropertyEnabled();
+    }
+
+    /**
+     * Returns an instance of {@link NfcUnlock}.
+     */
+    public static synchronized NfcUnlock getInstance(NfcAdapter nfcAdapter) {
+        Context context = nfcAdapter.getContext();
+        if (context == null) {
+            Log.e(TAG, "NfcAdapter context is null");
+            throw new UnsupportedOperationException();
+        }
+
+        NfcUnlock manager = sNfcUnlocks.get(context);
+        if (manager == null) {
+            INfcUnlockSettings service = nfcAdapter.getNfcUnlockSettingsService();
+            manager = new NfcUnlock(context, service);
+            sNfcUnlocks.put(context, manager);
+        }
+
+        return manager;
+    }
+
+    /**
+     * Registers the given {@code tag} as an unlock tag.
+     *
+     * @return true if the tag was successfully registered.
+     * @hide
+     */
+    public boolean registerTag(Tag tag) {
+        enforcePropertyEnabled();
+
+        int currentUser = ActivityManager.getCurrentUser();
+
+        try {
+            return sService.registerTag(currentUser, tag);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NfcUnlockSettingsService");
+                return false;
+            }
+
+            try {
+                return sService.registerTag(currentUser, tag);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach NfcUnlockSettingsService", ee);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Deregisters the given {@code tag} as an unlock tag.
+     *
+     * @return true if the tag was successfully deregistered.
+     * @hide
+     */
+    public boolean deregisterTag(long timestamp) {
+        enforcePropertyEnabled();
+        int currentUser = ActivityManager.getCurrentUser();
+
+        try {
+            return sService.deregisterTag(currentUser, timestamp);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NfcUnlockSettingsService");
+                return false;
+            }
+
+            try {
+                return sService.deregisterTag(currentUser, timestamp);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach NfcUnlockSettingsService", ee);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Determines the enable state of the NFC unlock feature.
+     *
+     * @return true if NFC unlock is enabled.
+     */
+    public boolean getNfcUnlockEnabled() {
+        enforcePropertyEnabled();
+        int currentUser = ActivityManager.getCurrentUser();
+
+        try {
+            return sService.getNfcUnlockEnabled(currentUser);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NfcUnlockSettingsService");
+                return false;
+            }
+
+            try {
+                return sService.getNfcUnlockEnabled(currentUser);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach NfcUnlockSettingsService", ee);
+                return false;
+            }
+        }
+    }
+
+    /**
+     * Set the enable state of the NFC unlock feature.
+     *
+     * @return true if the setting was successfully persisted.
+     * @hide
+     */
+    public boolean setNfcUnlockEnabled(boolean enabled) {
+        enforcePropertyEnabled();
+        int currentUser = ActivityManager.getCurrentUser();
+
+        try {
+            sService.setNfcUnlockEnabled(currentUser, enabled);
+            return true;
+        }  catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NfcUnlockSettingsService");
+                return false;
+            }
+
+            try {
+                sService.setNfcUnlockEnabled(currentUser, enabled);
+                return true;
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach NfcUnlockSettingsService", ee);
+                return false;
+            }
+
+        }
+    }
+
+    /**
+     * Returns a list of times (in millis since epoch) corresponding to when
+     * unlock tags were registered.
+     *
+     * @hide
+     */
+    @Nullable
+    public long[] getTagRegistryTimes() {
+        enforcePropertyEnabled();
+        int currentUser = ActivityManager.getCurrentUser();
+
+        try {
+            return sService.getTagRegistryTimes(currentUser);
+        } catch (RemoteException e) {
+            recoverService();
+            if (sService == null) {
+                Log.e(TAG, "Failed to recover NfcUnlockSettingsService");
+                return null;
+            }
+
+            try {
+                return sService.getTagRegistryTimes(currentUser);
+            } catch (RemoteException ee) {
+                Log.e(TAG, "Failed to reach NfcUnlockSettingsService", ee);
+                return null;
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    public static boolean getPropertyEnabled() {
+        return SystemProperties.get(PROPERTY).equals("ON");
+    }
+
+    private void recoverService() {
+        NfcAdapter adapter = NfcAdapter.getDefaultAdapter(mContext);
+        sService = adapter.getNfcUnlockSettingsService();
+    }
+
+
+    private void enforcePropertyEnabled() {
+        if (!mEnabled) {
+            throw new UnsupportedOperationException("NFC Unlock property is not enabled");
+        }
+    }
+}
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 0dffc17..a213df1 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -3407,6 +3407,11 @@
         public static final String LOCK_PATTERN_VISIBLE = "lock_pattern_visible_pattern";
 
         /**
+         * Whether the NFC unlock feature is enabled (0 = false, 1 = true)
+         */
+        public static final String NFC_UNLOCK_ENABLED = "nfc_unlock_enabled";
+
+        /**
          * Whether lock pattern will vibrate as user enters (0 = false, 1 =
          * true)
          *
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index c923e14..6de7a40 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -809,8 +809,15 @@
     <!-- Allows access to the loop radio (Android@Home mesh network) device.
 	@hide -->
     <permission android:name="android.permission.LOOP_RADIO"
-	android:permissionGroup="android.permission-group.NETWORK"
-	android:protectionLevel="signature|system" />
+	    android:permissionGroup="android.permission-group.NETWORK"
+	    android:protectionLevel="signature|system" />
+
+    <!-- Allows for the NFC process to unlock the device
+         @hide This should only be used by the Nfc apk
+    -->
+    <permission android:name="android.permission.NFC_UNLOCK"
+        android:permissionGroup="android.permission-group.SYSTEM_TOOLS"
+        android:protectionLevel="signature" />
 
     <!-- ================================== -->
     <!-- Permissions for accessing accounts -->
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
index 1bae9b8..dfcd8a8 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardHostView.java
@@ -16,6 +16,7 @@
 
 package com.android.keyguard;
 
+import android.nfc.NfcUnlock;
 import com.android.internal.widget.LockPatternUtils;
 import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
 import com.android.keyguard.KeyguardUpdateMonitor.DisplayClientState;
@@ -50,6 +51,7 @@
 import android.util.AttributeSet;
 import android.util.Log;
 import android.util.Slog;
+
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
@@ -296,6 +298,11 @@
                 }
             }
         }
+        @Override
+        public void onNfcUnlock() {
+            if (NfcUnlock.getPropertyEnabled()) mCallback.dismiss(true);
+        }
+
     };
 
     private static final boolean isMusicPlaying(int playbackState) {
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
index a849316..3b712e9 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -36,6 +36,7 @@
 import static android.os.BatteryManager.EXTRA_HEALTH;
 import android.media.AudioManager;
 import android.media.IRemoteControlDisplay;
+import android.nfc.NfcUnlock;
 import android.os.BatteryManager;
 import android.os.Bundle;
 import android.os.Handler;
@@ -94,6 +95,7 @@
     protected static final int MSG_REPORT_EMERGENCY_CALL_ACTION = 318;
     private static final int MSG_SCREEN_TURNED_ON = 319;
     private static final int MSG_SCREEN_TURNED_OFF = 320;
+    private static final int MSG_NFC_UNLOCK = 321;
 
     private static KeyguardUpdateMonitor sInstance;
 
@@ -194,6 +196,9 @@
                 case MSG_SCREEN_TURNED_ON:
                     handleScreenTurnedOn();
                     break;
+                case MSG_NFC_UNLOCK:
+                    handleNfcUnlock();
+                    break;
             }
         }
     };
@@ -311,6 +316,15 @@
         }
     };
 
+    private final BroadcastReceiver mNfcUnlockReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (NfcUnlock.ACTION_NFC_UNLOCK.equals(intent.getAction())) {
+                mHandler.sendEmptyMessage(MSG_NFC_UNLOCK);
+            }
+        }
+    }
+    ;
     /**
      * When we receive a
      * {@link com.android.internal.telephony.TelephonyIntents#ACTION_SIM_STATE_CHANGED} broadcast,
@@ -495,6 +509,15 @@
         }
     }
 
+    private void handleNfcUnlock() {
+        for (int i = 0; i < mCallbacks.size(); i++) {
+            KeyguardUpdateMonitorCallback cb = mCallbacks.get(i).get();
+            if (cb != null) {
+                cb.onNfcUnlock();
+            }
+        }
+    }
+
     private KeyguardUpdateMonitor(Context context) {
         mContext = context;
 
@@ -524,6 +547,11 @@
         filter.addAction(Intent.ACTION_USER_REMOVED);
         context.registerReceiver(mBroadcastReceiver, filter);
 
+        final IntentFilter nfcUnlockIntentFilter = new IntentFilter();
+        nfcUnlockIntentFilter.addAction(NfcUnlock.ACTION_NFC_UNLOCK);
+        context.registerReceiver(mNfcUnlockReceiver, nfcUnlockIntentFilter,
+                NfcUnlock.NFC_UNLOCK_PERMISSION, null /* run on default scheduler */);
+
         final IntentFilter bootCompleteFilter = new IntentFilter();
         bootCompleteFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
         bootCompleteFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
diff --git a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
index c08880d..481d132 100644
--- a/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
+++ b/packages/Keyguard/src/com/android/keyguard/KeyguardUpdateMonitorCallback.java
@@ -20,6 +20,7 @@
 import android.graphics.Bitmap;
 import android.media.AudioManager;
 import android.os.SystemClock;
+import android.util.Log;
 import android.view.WindowManagerPolicy;
 
 import com.android.internal.telephony.IccCardConstants;
@@ -172,4 +173,9 @@
      *   {@link WindowManagerPolicy#OFF_BECAUSE_OF_PROX_SENSOR}.
      */
     public void onScreenTurnedOff(int why) { }
+
+    /**
+     * Called when the NFC Service has found a tag that is registered for NFC unlock.
+     */
+    public void onNfcUnlock() { }
 }