am a42cf680: Merge "Camera: Add support for notifying NFC stack when camera is active" into mnc-dr-dev

* commit 'a42cf680a57e73eb4ed874bb78b7f47c6d5d76d3':
  Camera: Add support for notifying NFC stack when camera is active
diff --git a/core/java/android/hardware/ICameraServiceProxy.aidl b/core/java/android/hardware/ICameraServiceProxy.aidl
index 0bb24bc..0e654d5 100644
--- a/core/java/android/hardware/ICameraServiceProxy.aidl
+++ b/core/java/android/hardware/ICameraServiceProxy.aidl
@@ -19,6 +19,8 @@
 /**
  * Binder interface for the camera service proxy running in system_server.
  *
+ * Keep in sync with frameworks/av/include/camera/ICameraServiceProxy.h
+ *
  * @hide
  */
 interface ICameraServiceProxy
@@ -27,4 +29,9 @@
      * Ping the service proxy to update the valid users for the camera service.
      */
     oneway void pingForUserUpdate();
+
+    /**
+     * Update the status of a camera device
+     */
+     oneway void notifyCameraState(String cameraId, int newCameraState);
 }
diff --git a/services/core/java/com/android/server/camera/CameraService.java b/services/core/java/com/android/server/camera/CameraService.java
index 0be24f4..f82454a 100644
--- a/services/core/java/com/android/server/camera/CameraService.java
+++ b/services/core/java/com/android/server/camera/CameraService.java
@@ -23,13 +23,17 @@
 import android.content.pm.UserInfo;
 import android.hardware.ICameraService;
 import android.hardware.ICameraServiceProxy;
+import android.nfc.INfcAdapter;
 import android.os.Handler;
 import android.os.IBinder;
+import android.os.Binder;
 import android.os.Message;
 import android.os.Process;
 import android.os.RemoteException;
 import android.os.UserManager;
+import android.os.SystemProperties;
 import android.util.Slog;
+import android.util.ArraySet;
 
 import com.android.server.ServiceThread;
 import com.android.server.SystemService;
@@ -44,8 +48,10 @@
  *
  * @hide
  */
-public class CameraService extends SystemService implements Handler.Callback {
+public class CameraService extends SystemService
+        implements Handler.Callback, IBinder.DeathRecipient {
     private static final String TAG = "CameraService_proxy";
+    private static final boolean DEBUG = false;
 
     /**
      * This must match the ICameraService.aidl definition
@@ -58,6 +64,16 @@
     public static final int NO_EVENT = 0; // NOOP
     public static final int USER_SWITCHED = 1; // User changed, argument is the new user handle
 
+    // State arguments to use with the notifyCameraState call from camera service:
+    public static final int CAMERA_STATE_OPEN = 0;
+    public static final int CAMERA_STATE_ACTIVE = 1;
+    public static final int CAMERA_STATE_IDLE = 2;
+    public static final int CAMERA_STATE_CLOSED = 3;
+
+    // Flags arguments to NFC adapter to enable/disable NFC
+    public static final int DISABLE_POLLING_FLAGS = 0x1000;
+    public static final int ENABLE_POLLING_FLAGS = 0x0000;
+
     // Handler message codes
     private static final int MSG_SWITCH_USER = 1;
 
@@ -72,6 +88,17 @@
     private Set<Integer> mEnabledCameraUsers;
     private int mLastUser;
 
+    private ICameraService mCameraServiceRaw;
+
+    private final ArraySet<String> mActiveCameraIds = new ArraySet<>();
+
+    private static final String NFC_NOTIFICATION_PROP = "ro.camera.notify_nfc";
+    private static final String NFC_SERVICE_BINDER_NAME = "nfc";
+    private static final IBinder nfcInterfaceToken = new Binder();
+
+    private final boolean mNotifyNfc;
+    private int mActiveCameraCount = 0;
+
     private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
@@ -102,6 +129,14 @@
         public void pingForUserUpdate() {
             notifySwitchWithRetries(30);
         }
+
+        @Override
+        public void notifyCameraState(String cameraId, int newCameraState) {
+            String state = cameraStateToString(newCameraState);
+            if (DEBUG) Slog.v(TAG, "Camera " + cameraId + " state now " + state);
+
+            updateActivityCount(cameraId, newCameraState);
+        }
     };
 
     public CameraService(Context context) {
@@ -110,6 +145,9 @@
         mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_DISPLAY, /*allowTo*/false);
         mHandlerThread.start();
         mHandler = new Handler(mHandlerThread.getLooper(), this);
+
+        mNotifyNfc = SystemProperties.getInt(NFC_NOTIFICATION_PROP, 0) > 0;
+        if (DEBUG) Slog.v(TAG, "Notify NFC behavior is " + (mNotifyNfc ? "active" : "disabled"));
     }
 
     @Override
@@ -161,13 +199,32 @@
         }
     }
 
+    /**
+     * Handle the death of the native camera service
+     */
+    @Override
+    public void binderDied() {
+        if (DEBUG) Slog.w(TAG, "Native camera service has died");
+        synchronized(mLock) {
+            mCameraServiceRaw = null;
+
+            // All cameras reset to idle on camera service death
+            boolean wasEmpty = mActiveCameraIds.isEmpty();
+            mActiveCameraIds.clear();
+
+            if ( mNotifyNfc && !wasEmpty ) {
+                notifyNfcService(/*enablePolling*/ true);
+            }
+        }
+    }
+
     private void switchUserLocked(int userHandle) {
         Set<Integer> currentUserHandles = getEnabledUserHandles(userHandle);
         mLastUser = userHandle;
         if (mEnabledCameraUsers == null || !mEnabledCameraUsers.equals(currentUserHandles)) {
             // Some user handles have been added or removed, update mediaserver.
             mEnabledCameraUsers = currentUserHandles;
-            notifyMediaserver(USER_SWITCHED, currentUserHandles);
+            notifyMediaserverLocked(USER_SWITCHED, currentUserHandles);
         }
     }
 
@@ -187,7 +244,7 @@
             if (mEnabledCameraUsers == null) {
                 return;
             }
-            if (notifyMediaserver(USER_SWITCHED, mEnabledCameraUsers)) {
+            if (notifyMediaserverLocked(USER_SWITCHED, mEnabledCameraUsers)) {
                 retries = 0;
             }
         }
@@ -199,19 +256,27 @@
                 RETRY_DELAY_TIME);
     }
 
-    private boolean notifyMediaserver(int eventType, Set<Integer> updatedUserHandles) {
+    private boolean notifyMediaserverLocked(int eventType, Set<Integer> updatedUserHandles) {
         // Forward the user switch event to the native camera service running in the mediaserver
         // process.
-        IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
-        if (cameraServiceBinder == null) {
-            Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
-            return false; // Camera service not active, cannot evict user clients.
+        if (mCameraServiceRaw == null) {
+            IBinder cameraServiceBinder = getBinderService(CAMERA_SERVICE_BINDER_NAME);
+            if (cameraServiceBinder == null) {
+                Slog.w(TAG, "Could not notify mediaserver, camera service not available.");
+                return false; // Camera service not active, cannot evict user clients.
+            }
+            try {
+                cameraServiceBinder.linkToDeath(this, /*flags*/ 0);
+            } catch (RemoteException e) {
+                Slog.w(TAG, "Could not link to death of native camera service");
+                return false;
+            }
+
+            mCameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
         }
 
-        ICameraService cameraServiceRaw = ICameraService.Stub.asInterface(cameraServiceBinder);
-
         try {
-            cameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
+            mCameraServiceRaw.notifySystemEvent(eventType, toArray(updatedUserHandles));
         } catch (RemoteException e) {
             Slog.w(TAG, "Could not notify mediaserver, remote exception: " + e);
             // Not much we can do if camera service is dead.
@@ -220,6 +285,44 @@
         return true;
     }
 
+    private void updateActivityCount(String cameraId, int newCameraState) {
+        synchronized(mLock) {
+            boolean wasEmpty = mActiveCameraIds.isEmpty();
+            switch (newCameraState) {
+                case CAMERA_STATE_OPEN:
+                    break;
+                case CAMERA_STATE_ACTIVE:
+                    mActiveCameraIds.add(cameraId);
+                    break;
+                case CAMERA_STATE_IDLE:
+                case CAMERA_STATE_CLOSED:
+                    mActiveCameraIds.remove(cameraId);
+                    break;
+            }
+            boolean isEmpty = mActiveCameraIds.isEmpty();
+            if ( mNotifyNfc && (wasEmpty != isEmpty) ) {
+                notifyNfcService(isEmpty);
+            }
+        }
+    }
+
+    private void notifyNfcService(boolean enablePolling) {
+
+        IBinder nfcServiceBinder = getBinderService(NFC_SERVICE_BINDER_NAME);
+        if (nfcServiceBinder == null) {
+            Slog.w(TAG, "Could not connect to NFC service to notify it of camera state");
+            return;
+        }
+        INfcAdapter nfcAdapterRaw = INfcAdapter.Stub.asInterface(nfcServiceBinder);
+        int flags = enablePolling ? ENABLE_POLLING_FLAGS : DISABLE_POLLING_FLAGS;
+        if (DEBUG) Slog.v(TAG, "Setting NFC reader mode to flags " + flags);
+        try {
+            nfcAdapterRaw.setReaderMode(nfcInterfaceToken, null, flags, null);
+        } catch (RemoteException e) {
+            Slog.w(TAG, "Could not notify NFC service, remote exception: " + e);
+        }
+    }
+
     private static int[] toArray(Collection<Integer> c) {
         int len = c.size();
         int[] ret = new int[len];
@@ -229,4 +332,15 @@
         }
         return ret;
     }
+
+    private static String cameraStateToString(int newCameraState) {
+        switch (newCameraState) {
+            case CAMERA_STATE_OPEN: return "CAMERA_STATE_OPEN";
+            case CAMERA_STATE_ACTIVE: return "CAMERA_STATE_ACTIVE";
+            case CAMERA_STATE_IDLE: return "CAMERA_STATE_IDLE";
+            case CAMERA_STATE_CLOSED: return "CAMERA_STATE_CLOSED";
+            default: break;
+        }
+        return "CAMERA_STATE_UNKNOWN";
+    }
 }