Merge "Revert "Add new secure setting for controlling Messaging notifications"" into ics-mr1
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 48adfad..3becec0 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -22,9 +22,12 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.graphics.SurfaceTexture;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
 import android.os.Handler;
 import android.os.Looper;
 import android.os.Message;
+import android.os.SystemProperties;
 import android.util.Log;
 import android.view.Surface;
 import android.view.SurfaceHolder;
@@ -154,6 +157,7 @@
     private boolean mOneShot;
     private boolean mWithBuffer;
     private boolean mFaceDetectionRunning = false;
+    private boolean mReleased = false;
 
     /**
      * Broadcast Action:  A new picture is taken by the camera, and the entry of
@@ -303,7 +307,7 @@
     }
 
     protected void finalize() {
-        native_release();
+        release();
     }
 
     private native final void native_setup(Object camera_this, int cameraId);
@@ -318,6 +322,15 @@
     public final void release() {
         native_release();
         mFaceDetectionRunning = false;
+        if (mCameraSoundPlayers != null) {
+            for (CameraSoundPlayer csp: mCameraSoundPlayers) {
+                if (csp != null) {
+                    csp.release();
+                }
+            }
+            mCameraSoundPlayers = null;
+        }
+        mReleased = true;
     }
 
     /**
@@ -2354,7 +2367,7 @@
          *
          * <p>The reference code is as follows.
          *
-	 * <pre>
+         * <pre>
          * public void onOrientationChanged(int orientation) {
          *     if (orientation == ORIENTATION_UNKNOWN) return;
          *     android.hardware.Camera.CameraInfo info =
@@ -2369,7 +2382,7 @@
          *     }
          *     mParameters.setRotation(rotation);
          * }
-	 * </pre>
+         * </pre>
          *
          * @param rotation The rotation angle in degrees relative to the
          *                 orientation of the camera. Rotation can only be 0,
@@ -3452,4 +3465,194 @@
             return result;
         }
     };
+
+    /**
+     * <p>The set of default system sounds for camera actions. Use this with
+     * {@link #playSound} to play an appropriate sound when implementing a
+     * custom still or video recording mechanism through the preview
+     * callbacks.</p>
+     *
+     * <p>There is no need to play sounds when using {@link #takePicture} or
+     * {@link android.media.MediaRecorder} for still images or video,
+     * respectively, as these play their own sounds when needed.</p>
+     *
+     * @see #playSound
+     * @hide
+     */
+    public static class Sound {
+        /**
+         * The sound used by {@link android.hardware.Camera#takePicture} to
+         * indicate still image capture.
+         */
+        public static final int SHUTTER_CLICK         = 0;
+
+        /**
+         * A sound to indicate that focusing has completed. Because deciding
+         * when this occurs is application-dependent, this sound is not used by
+         * any methods in the Camera class.
+         */
+        public static final int FOCUS_COMPLETE        = 1;
+
+        /**
+         * The sound used by {@link android.media.MediaRecorder#start} to
+         * indicate the start of video recording.
+         */
+        public static final int START_VIDEO_RECORDING = 2;
+
+        /**
+         * The sound used by {@link android.media.MediaRecorder#stop} to
+         * indicate the end of video recording.
+         */
+        public static final int STOP_VIDEO_RECORDING  = 3;
+
+        private static final int NUM_SOUNDS           = 4;
+    };
+
+    /**
+     * <p>Play one of the predefined platform sounds for camera actions.</p>
+     *
+     * <p>Use this method to play a platform-specific sound for various camera
+     * actions. The sound playing is done asynchronously, with the same behavior
+     * and content as the sounds played by {@link #takePicture takePicture},
+     * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
+     * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
+     *
+     * <p>Using this method makes it easy to match the default device sounds
+     * when recording or capturing data through the preview callbacks
+     * ({@link #setPreviewCallback setPreviewCallback},
+     * {@link #setPreviewTexture setPreviewTexture}).</p>
+     *
+     * @param soundId The type of sound to play, selected from the options in
+     *   {@link android.hardware.Camera.Sound}
+     * @see android.hardware.Camera.Sound
+     * @see #takePicture
+     * @see android.media.MediaRecorder
+     * @hide
+     */
+    public void playSound(int soundId) {
+        if (mReleased) return;
+        if (mCameraSoundPlayers == null) {
+            mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS];
+        }
+        if (mCameraSoundPlayers[soundId] == null) {
+            mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
+        }
+        mCameraSoundPlayers[soundId].play();
+    }
+
+    private CameraSoundPlayer[] mCameraSoundPlayers;
+
+    private static class CameraSoundPlayer implements Runnable {
+        private int mSoundId;
+        private int mAudioStreamType;
+        private MediaPlayer mPlayer;
+        private Thread mThread;
+        private boolean mExit;
+        private int mPlayCount;
+
+        private static final String mShutterSound    =
+                "/system/media/audio/ui/camera_click.ogg";
+        private static final String mFocusSound      =
+                "/system/media/audio/ui/camera_focus.ogg";
+        private static final String mVideoStartSound =
+                "/system/media/audio/ui/VideoRecord.ogg";
+        private static final String mVideoStopSound  =
+                "/system/media/audio/ui/VideoRecord.ogg";
+
+        @Override
+        public void run() {
+            String soundFilePath;
+            switch (mSoundId) {
+                case Sound.SHUTTER_CLICK:
+                    soundFilePath = mShutterSound;
+                    break;
+                case Sound.FOCUS_COMPLETE:
+                    soundFilePath = mFocusSound;
+                    break;
+                case Sound.START_VIDEO_RECORDING:
+                    soundFilePath = mVideoStartSound;
+                    break;
+                case Sound.STOP_VIDEO_RECORDING:
+                    soundFilePath = mVideoStopSound;
+                    break;
+                default:
+                    Log.e(TAG, "Unknown sound " + mSoundId + " requested.");
+                    return;
+            }
+            mPlayer = new MediaPlayer();
+            try {
+                mPlayer.setAudioStreamType(mAudioStreamType);
+                mPlayer.setDataSource(soundFilePath);
+                mPlayer.setLooping(false);
+                mPlayer.prepare();
+            } catch(IOException e) {
+                Log.e(TAG, "Error setting up sound " + mSoundId, e);
+                return;
+            }
+
+            while(true) {
+                try {
+                    synchronized (this) {
+                        while(true) {
+                            if (mExit) {
+                                return;
+                            } else if (mPlayCount <= 0) {
+                                wait();
+                            } else {
+                                mPlayCount--;
+                                break;
+                            }
+                        }
+                    }
+                    mPlayer.start();
+                } catch (Exception e) {
+                    Log.e(TAG, "Error playing sound " + mSoundId, e);
+                }
+            }
+        }
+
+        public CameraSoundPlayer(int soundId) {
+            mSoundId = soundId;
+            if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) {
+                mAudioStreamType = AudioManager.STREAM_MUSIC;
+            } else {
+                mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED;
+            }
+        }
+
+        public void play() {
+            if (mThread == null) {
+                mThread = new Thread(this);
+                mThread.start();
+            }
+            synchronized (this) {
+                mPlayCount++;
+                notifyAll();
+            }
+        }
+
+        public void release() {
+            if (mThread != null) {
+                synchronized (this) {
+                    mExit = true;
+                    notifyAll();
+                }
+                try {
+                    mThread.join();
+                } catch (InterruptedException e) {
+                }
+                mThread = null;
+            }
+            if (mPlayer != null) {
+                mPlayer.release();
+                mPlayer = null;
+            }
+        }
+
+        @Override
+        protected void finalize() {
+            release();
+        }
+    }
+
 }
diff --git a/data/sounds/AudioPackage5.mk b/data/sounds/AudioPackage5.mk
index 550f990..5961f06 100755
--- a/data/sounds/AudioPackage5.mk
+++ b/data/sounds/AudioPackage5.mk
@@ -20,6 +20,7 @@
 	$(LOCAL_PATH)/effects/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
 	$(LOCAL_PATH)/effects/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
 	$(LOCAL_PATH)/effects/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
 	$(LOCAL_PATH)/effects/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
 	$(LOCAL_PATH)/effects/Dock.ogg:system/media/audio/ui/Dock.ogg \
 	$(LOCAL_PATH)/effects/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/AudioPackage6.mk b/data/sounds/AudioPackage6.mk
index 610e821..d113a29 100755
--- a/data/sounds/AudioPackage6.mk
+++ b/data/sounds/AudioPackage6.mk
@@ -19,6 +19,7 @@
 	$(LOCAL_PATH)/effects/ogg/KeypressReturn.ogg:system/media/audio/ui/KeypressReturn.ogg \
 	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
 	$(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
 	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
 	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
 	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/AudioPackage7.mk b/data/sounds/AudioPackage7.mk
index 93e0fa0..6ae624e 100755
--- a/data/sounds/AudioPackage7.mk
+++ b/data/sounds/AudioPackage7.mk
@@ -21,6 +21,7 @@
 	$(LOCAL_PATH)/effects/ogg/KeypressReturn_120.ogg:system/media/audio/ui/KeypressReturn.ogg \
 	$(LOCAL_PATH)/effects/ogg/VideoRecord.ogg:system/media/audio/ui/VideoRecord.ogg \
 	$(LOCAL_PATH)/effects/ogg/camera_click.ogg:system/media/audio/ui/camera_click.ogg \
+	$(LOCAL_PATH)/effects/ogg/camera_focus.ogg:system/media/audio/ui/camera_focus.ogg \
 	$(LOCAL_PATH)/effects/ogg/LowBattery.ogg:system/media/audio/ui/LowBattery.ogg \
 	$(LOCAL_PATH)/effects/ogg/Dock.ogg:system/media/audio/ui/Dock.ogg \
 	$(LOCAL_PATH)/effects/ogg/Undock.ogg:system/media/audio/ui/Undock.ogg \
diff --git a/data/sounds/effects/ogg/camera_focus.ogg b/data/sounds/effects/ogg/camera_focus.ogg
new file mode 100644
index 0000000..0db2683
--- /dev/null
+++ b/data/sounds/effects/ogg/camera_focus.ogg
Binary files differ
diff --git a/services/sensorservice/SensorDevice.cpp b/services/sensorservice/SensorDevice.cpp
index d82a7e2..7575ebd 100644
--- a/services/sensorservice/SensorDevice.cpp
+++ b/services/sensorservice/SensorDevice.cpp
@@ -166,7 +166,11 @@
 
 ssize_t SensorDevice::poll(sensors_event_t* buffer, size_t count) {
     if (!mSensorDevice) return NO_INIT;
-    return mSensorDevice->poll(mSensorDevice, buffer, count);
+    ssize_t c;
+    do {
+        c = mSensorDevice->poll(mSensorDevice, buffer, count);
+    } while (c == -EINTR);
+    return c;
 }
 
 status_t SensorDevice::activate(void* ident, int handle, int enabled)
diff --git a/services/sensorservice/SensorService.cpp b/services/sensorservice/SensorService.cpp
index c2c6b4d..6202143 100644
--- a/services/sensorservice/SensorService.cpp
+++ b/services/sensorservice/SensorService.cpp
@@ -286,7 +286,8 @@
         }
     } while (count >= 0 || Thread::exitPending());
 
-    LOGW("Exiting SensorService::threadLoop!");
+    LOGW("Exiting SensorService::threadLoop => aborting...");
+    abort();
     return false;
 }