Merge "Use libcorkscrew to format the stack trace."
diff --git a/api/current.txt b/api/current.txt
index 62291f7..4cf23eb 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -14244,13 +14244,6 @@
method public static void texSubImage2D(int, int, int, int, android.graphics.Bitmap, int, int);
}
- public abstract class ManagedEGLContext {
- ctor public ManagedEGLContext(javax.microedition.khronos.egl.EGLContext);
- method public javax.microedition.khronos.egl.EGLContext getContext();
- method public abstract void onTerminate(javax.microedition.khronos.egl.EGLContext);
- method public void terminate();
- }
-
public class Matrix {
ctor public Matrix();
method public static void frustumM(float[], int, float, float, float, float, float, float);
@@ -18434,14 +18427,14 @@
ctor public RSSurfaceView(android.content.Context);
ctor public RSSurfaceView(android.content.Context, android.util.AttributeSet);
method public android.renderscript.RenderScriptGL createRenderScriptGL(android.renderscript.RenderScriptGL.SurfaceConfig);
- method public synchronized void destroyRenderScriptGL();
+ method public void destroyRenderScriptGL();
method public android.renderscript.RenderScriptGL getRenderScriptGL();
method public void pause();
method public void resume();
method public void setRenderScriptGL(android.renderscript.RenderScriptGL);
- method public synchronized void surfaceChanged(android.view.SurfaceHolder, int, int, int);
+ method public void surfaceChanged(android.view.SurfaceHolder, int, int, int);
method public void surfaceCreated(android.view.SurfaceHolder);
- method public synchronized void surfaceDestroyed(android.view.SurfaceHolder);
+ method public void surfaceDestroyed(android.view.SurfaceHolder);
}
public class RSTextureView extends android.view.TextureView implements android.view.TextureView.SurfaceTextureListener {
diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java
index 4e38011..7ca6155 100644
--- a/core/java/android/hardware/Camera.java
+++ b/core/java/android/hardware/Camera.java
@@ -22,12 +22,9 @@
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;
@@ -157,7 +154,6 @@
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
@@ -322,15 +318,6 @@
public final void release() {
native_release();
mFaceDetectionRunning = false;
- if (mCameraSoundPlayers != null) {
- for (CameraSoundPlayer csp: mCameraSoundPlayers) {
- if (csp != null) {
- csp.release();
- }
- }
- mCameraSoundPlayers = null;
- }
- mReleased = true;
}
/**
@@ -3503,194 +3490,4 @@
return false;
}
};
-
- /**
- * <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/core/java/android/hardware/CameraSound.java b/core/java/android/hardware/CameraSound.java
new file mode 100644
index 0000000..32de0cd
--- /dev/null
+++ b/core/java/android/hardware/CameraSound.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright (C) 2011 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.hardware;
+
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.SystemProperties;
+import android.util.Log;
+
+import java.io.IOException;
+
+/**
+ * <p>Use this class 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 #android.hardware.Camera#takePicture}
+ * or {@link android.media.MediaRecorder} for still images or video,
+ * respectively, as these play their own sounds when needed.</p>
+ *
+ * @hide
+ */
+public class CameraSound {
+ private static final String TAG = "CameraSound";
+ /**
+ * 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;
+ private CameraSoundPlayer[] mCameraSoundPlayers;
+
+ public CameraSound() {
+ }
+
+ /**
+ * <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.</p>
+ *
+ * @param soundId The type of sound to play, selected from SHUTTER_CLICK,
+ * FOCUS_COMPLETE, START_VIDEO_RECORDING, or STOP_VIDEO_RECORDING.
+ * @see android.hardware#takePicture
+ * @see android.media.MediaRecorder
+ * @see #SHUTTER_CLICK
+ * @see #FOCUS_COMPLETE
+ * @see #START_VIDEO_RECORDING
+ * @see #STOP_VIDEO_RECORDING
+ */
+ public void playSound(int soundId) {
+ if (mCameraSoundPlayers == null) {
+ mCameraSoundPlayers = new CameraSoundPlayer[NUM_SOUNDS];
+ }
+ if (mCameraSoundPlayers[soundId] == null) {
+ mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId);
+ }
+ mCameraSoundPlayers[soundId].play();
+ }
+
+ public void release() {
+ if (mCameraSoundPlayers != null) {
+ for (CameraSoundPlayer csp: mCameraSoundPlayers) {
+ if (csp != null) {
+ csp.release();
+ }
+ }
+ mCameraSoundPlayers = null;
+ }
+ }
+
+ 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 SHUTTER_CLICK:
+ soundFilePath = mShutterSound;
+ break;
+ case FOCUS_COMPLETE:
+ soundFilePath = mFocusSound;
+ break;
+ case START_VIDEO_RECORDING:
+ soundFilePath = mVideoStartSound;
+ break;
+ case 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();
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/net/DhcpStateMachine.java b/core/java/android/net/DhcpStateMachine.java
index fc6a44a..397a12a 100644
--- a/core/java/android/net/DhcpStateMachine.java
+++ b/core/java/android/net/DhcpStateMachine.java
@@ -347,21 +347,25 @@
if (success) {
if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName);
- long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
+ long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion
- //Sanity check for renewal
- //TODO: would be good to notify the user that his network configuration is
- //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
- if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
- leaseDuration = MIN_RENEWAL_TIME_SECS;
- }
- //Do it a bit earlier than half the lease duration time
- //to beat the native DHCP client and avoid extra packets
- //48% for one hour lease time = 29 minutes
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() +
- leaseDuration * 480, //in milliseconds
- mDhcpRenewalIntent);
+ //Sanity check for renewal
+ if (leaseDuration >= 0) {
+ //TODO: would be good to notify the user that his network configuration is
+ //bad and that the device cannot renew below MIN_RENEWAL_TIME_SECS
+ if (leaseDuration < MIN_RENEWAL_TIME_SECS) {
+ leaseDuration = MIN_RENEWAL_TIME_SECS;
+ }
+ //Do it a bit earlier than half the lease duration time
+ //to beat the native DHCP client and avoid extra packets
+ //48% for one hour lease time = 29 minutes
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() +
+ leaseDuration * 480, //in milliseconds
+ mDhcpRenewalIntent);
+ } else {
+ //infinite lease time, no renewal needed
+ }
mController.obtainMessage(CMD_POST_DHCP_ACTION, DHCP_SUCCESS, 0, dhcpInfoInternal)
.sendToTarget();
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 273ee91..02096f2 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -357,8 +357,11 @@
throw new IllegalArgumentException("context cannot be null");
}
context = context.getApplicationContext();
- /* use getSystemService() instead of just instantiating to take
- * advantage of the context's cached NfcManager & NfcAdapter */
+ if (context == null) {
+ throw new IllegalArgumentException(
+ "context not associated with any application (using a mock context?)");
+ }
+ /* use getSystemService() for consistency */
NfcManager manager = (NfcManager) context.getSystemService(Context.NFC_SERVICE);
if (manager == null) {
// NFC not available
diff --git a/core/java/android/nfc/NfcManager.java b/core/java/android/nfc/NfcManager.java
index 6ec2e21..2bbed57 100644
--- a/core/java/android/nfc/NfcManager.java
+++ b/core/java/android/nfc/NfcManager.java
@@ -40,6 +40,10 @@
public NfcManager(Context context) {
NfcAdapter adapter;
context = context.getApplicationContext();
+ if (context == null) {
+ throw new IllegalArgumentException(
+ "context not associated with any application (using a mock context?)");
+ }
try {
adapter = NfcAdapter.getNfcAdapter(context);
} catch (UnsupportedOperationException e) {
diff --git a/core/java/android/os/Process.java b/core/java/android/os/Process.java
index 3362575..e1bc275 100644
--- a/core/java/android/os/Process.java
+++ b/core/java/android/os/Process.java
@@ -101,7 +101,7 @@
* Defines the UID/GID for the NFC service process.
* @hide
*/
- public static final int NFC_UID = 1025;
+ public static final int NFC_UID = 1027;
/**
* Defines the GID for the group that allows write access to the internal media storage.
diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java
index 28251a6..5282e61 100644
--- a/core/java/android/service/textservice/SpellCheckerService.java
+++ b/core/java/android/service/textservice/SpellCheckerService.java
@@ -138,6 +138,25 @@
}
/**
+ * @hide
+ * The default implementation returns an array of SuggestionsInfo by simply calling
+ * onGetSuggestions().
+ * When you override this method, make sure that suggestionsLimit is applied to suggestions
+ * that share the same start position and length.
+ */
+ public SuggestionsInfo[] onGetSuggestionsMultipleForSentence(TextInfo[] textInfos,
+ int suggestionsLimit) {
+ final int length = textInfos.length;
+ final SuggestionsInfo[] retval = new SuggestionsInfo[length];
+ for (int i = 0; i < length; ++i) {
+ retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit);
+ retval[i].setCookieAndSequence(
+ textInfos[i].getCookie(), textInfos[i].getSequence());
+ }
+ return retval;
+ }
+
+ /**
* Request to abort all tasks executed in SpellChecker.
* This function will run on the incoming IPC thread.
* So, this is not called on the main thread,
@@ -196,6 +215,16 @@
}
@Override
+ public void onGetSuggestionsMultipleForSentence(
+ TextInfo[] textInfos, int suggestionsLimit) {
+ try {
+ mListener.onGetSuggestionsForSentence(
+ mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit));
+ } catch (RemoteException e) {
+ }
+ }
+
+ @Override
public void onCancel() {
mSession.onCancel();
}
diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java
index 8e39d6e..f77cf7e 100644
--- a/core/java/android/view/HardwareRenderer.java
+++ b/core/java/android/view/HardwareRenderer.java
@@ -162,13 +162,21 @@
abstract void updateSurface(SurfaceHolder holder) throws Surface.OutOfResourcesException;
/**
- * Destoys the layers used by the specified view hierarchy.
+ * Destroys the layers used by the specified view hierarchy.
*
* @param view The root of the view hierarchy
*/
abstract void destroyLayers(View view);
/**
+ * Destroys all hardware rendering resources associated with the specified
+ * view hierarchy.
+ *
+ * @param view The root of the view hierarchy
+ */
+ abstract void destroyHardwareResources(View view);
+
+ /**
* This method should be invoked whenever the current hardware renderer
* context should be reset.
*
@@ -348,15 +356,6 @@
}
/**
- * Invoke this method when the system needs to clean up all resources
- * associated with hardware rendering.
- */
- static void terminate() {
- Log.d(LOG_TAG, "Terminating hardware rendering");
- Gl20Renderer.terminate();
- }
-
- /**
* Indicates whether hardware acceleration is currently enabled.
*
* @return True if hardware acceleration is in use, false otherwise.
@@ -412,8 +411,8 @@
static final Object[] sEglLock = new Object[0];
int mWidth = -1, mHeight = -1;
- static final ThreadLocal<Gl20Renderer.MyEGLContext> sEglContextStorage
- = new ThreadLocal<Gl20Renderer.MyEGLContext>();
+ static final ThreadLocal<Gl20Renderer.Gl20RendererEglContext> sEglContextStorage
+ = new ThreadLocal<Gl20Renderer.Gl20RendererEglContext>();
EGLContext mEglContext;
Thread mEglThread;
@@ -565,13 +564,13 @@
}
}
- Gl20Renderer.MyEGLContext managedContext = sEglContextStorage.get();
+ Gl20Renderer.Gl20RendererEglContext managedContext = sEglContextStorage.get();
mEglContext = managedContext != null ? managedContext.getContext() : null;
mEglThread = Thread.currentThread();
if (mEglContext == null) {
mEglContext = createContext(sEgl, sEglDisplay, sEglConfig);
- sEglContextStorage.set(new Gl20Renderer.MyEGLContext(mEglContext));
+ sEglContextStorage.set(new Gl20Renderer.Gl20RendererEglContext(mEglContext));
}
}
@@ -909,10 +908,10 @@
private static EGLSurface sPbuffer;
private static final Object[] sPbufferLock = new Object[0];
- static class MyEGLContext extends ManagedEGLContext {
+ static class Gl20RendererEglContext extends ManagedEGLContext {
final Handler mHandler = new Handler();
- public MyEGLContext(EGLContext context) {
+ public Gl20RendererEglContext(EGLContext context) {
super(context);
}
@@ -939,7 +938,8 @@
sEglContextStorage.remove();
sEgl.eglDestroySurface(sEglDisplay, sPbuffer);
- sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
+ sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE,
+ EGL_NO_SURFACE, EGL_NO_CONTEXT);
sEgl.eglReleaseThread();
sEgl.eglTerminate(sEglDisplay);
@@ -1046,10 +1046,9 @@
}
}
- private void destroyHardwareLayer(View view) {
- if (view.destroyLayer()) {
- view.invalidate(true);
- }
+ private static void destroyHardwareLayer(View view) {
+ view.destroyLayer();
+
if (view instanceof ViewGroup) {
ViewGroup group = (ViewGroup) view;
@@ -1059,6 +1058,36 @@
}
}
}
+
+ @Override
+ void destroyHardwareResources(View view) {
+ if (view != null) {
+ boolean needsContext = true;
+ if (isEnabled() && checkCurrent() != SURFACE_STATE_ERROR) needsContext = false;
+
+ if (needsContext) {
+ Gl20RendererEglContext managedContext = sEglContextStorage.get();
+ if (managedContext == null) return;
+ usePbufferSurface(managedContext.getContext());
+ }
+
+ destroyResources(view);
+ GLES20Canvas.flushCaches(GLES20Canvas.FLUSH_CACHES_LAYERS);
+ }
+ }
+
+ private static void destroyResources(View view) {
+ view.destroyHardwareResources();
+
+ if (view instanceof ViewGroup) {
+ ViewGroup group = (ViewGroup) view;
+
+ int count = group.getChildCount();
+ for (int i = 0; i < count; i++) {
+ destroyResources(group.getChildAt(i));
+ }
+ }
+ }
static HardwareRenderer create(boolean translucent) {
if (GLES20Canvas.isAvailable()) {
@@ -1070,7 +1099,7 @@
static void trimMemory(int level) {
if (sEgl == null || sEglConfig == null) return;
- Gl20Renderer.MyEGLContext managedContext = sEglContextStorage.get();
+ Gl20RendererEglContext managedContext = sEglContextStorage.get();
// We do not have OpenGL objects
if (managedContext == null) {
return;
diff --git a/core/java/android/view/TextureView.java b/core/java/android/view/TextureView.java
index 1697382..74916f0 100644
--- a/core/java/android/view/TextureView.java
+++ b/core/java/android/view/TextureView.java
@@ -203,7 +203,10 @@
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
+ destroySurface();
+ }
+ private void destroySurface() {
if (mLayer != null) {
boolean shouldRelease = true;
if (mListener != null) {
@@ -300,6 +303,17 @@
return false;
}
+ /**
+ * @hide
+ */
+ @Override
+ protected void destroyHardwareResources() {
+ super.destroyHardwareResources();
+ destroySurface();
+ invalidateParentCaches();
+ invalidate(true);
+ }
+
@Override
HardwareLayer getHardwareLayer() {
if (mLayer == null) {
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 65b1bd0..74c1ce8 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -10129,7 +10129,7 @@
switch (mLayerType) {
case LAYER_TYPE_HARDWARE:
destroyLayer();
- // fall through - unaccelerated views may use software layer mechanism instead
+ // fall through - non-accelerated views may use software layer mechanism instead
case LAYER_TYPE_SOFTWARE:
destroyDrawingCache();
break;
@@ -10196,7 +10196,11 @@
switch (mLayerType) {
case LAYER_TYPE_HARDWARE:
- getHardwareLayer();
+ if (mAttachInfo.mHardwareRenderer != null &&
+ mAttachInfo.mHardwareRenderer.isEnabled() &&
+ mAttachInfo.mHardwareRenderer.validate()) {
+ getHardwareLayer();
+ }
break;
case LAYER_TYPE_SOFTWARE:
buildDrawingCache(true);
@@ -10291,12 +10295,31 @@
if (mHardwareLayer != null) {
mHardwareLayer.destroy();
mHardwareLayer = null;
+
+ invalidate(true);
+ invalidateParentCaches();
+
return true;
}
return false;
}
/**
+ * Destroys all hardware rendering resources. This method is invoked
+ * when the system needs to reclaim resources. Upon execution of this
+ * method, you should free any OpenGL resources created by the view.
+ *
+ * Note: you <strong>must</strong> call
+ * <code>super.destroyHardwareResources()</code> when overriding
+ * this method.
+ *
+ * @hide
+ */
+ protected void destroyHardwareResources() {
+ destroyLayer();
+ }
+
+ /**
* <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
* to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
* bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java
index 723846a..18d5c40 100644
--- a/core/java/android/view/ViewRootImpl.java
+++ b/core/java/android/view/ViewRootImpl.java
@@ -577,6 +577,13 @@
}
}
+ void terminateHardwareResources() {
+ if (mAttachInfo.mHardwareRenderer != null) {
+ mAttachInfo.mHardwareRenderer.destroyHardwareResources(mView);
+ mAttachInfo.mHardwareRenderer.destroy(false);
+ }
+ }
+
void destroyHardwareLayers() {
if (mThread != Thread.currentThread()) {
if (mAttachInfo.mHardwareRenderer != null &&
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
index 660e3f4..dfd1d55 100644
--- a/core/java/android/view/WindowManagerImpl.java
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -425,7 +425,7 @@
if (mViews == null) return;
int count = mViews.length;
for (int i = 0; i < count; i++) {
- mRoots[i].destroyHardwareResources();
+ mRoots[i].terminateHardwareResources();
}
}
// Terminate the hardware renderer to free all resources
diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java
index 489587e..1d66cbe 100644
--- a/core/java/android/view/textservice/SpellCheckerSession.java
+++ b/core/java/android/view/textservice/SpellCheckerSession.java
@@ -88,14 +88,17 @@
* This meta-data must reference an XML resource.
**/
public static final String SERVICE_META_DATA = "android.view.textservice.scs";
+ private static final String SUPPORT_SENTENCE_SPELL_CHECK = "SupportSentenceSpellCheck";
private static final int MSG_ON_GET_SUGGESTION_MULTIPLE = 1;
+ private static final int MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE = 2;
private final InternalListener mInternalListener;
private final ITextServicesManager mTextServicesManager;
private final SpellCheckerInfo mSpellCheckerInfo;
private final SpellCheckerSessionListenerImpl mSpellCheckerSessionListenerImpl;
+ private final SpellCheckerSubtype mSubtype;
private boolean mIsUsed;
private SpellCheckerSessionListener mSpellCheckerSessionListener;
@@ -108,6 +111,9 @@
case MSG_ON_GET_SUGGESTION_MULTIPLE:
handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj);
break;
+ case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE:
+ handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj);
+ break;
}
}
};
@@ -117,7 +123,8 @@
* @hide
*/
public SpellCheckerSession(
- SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener) {
+ SpellCheckerInfo info, ITextServicesManager tsm, SpellCheckerSessionListener listener,
+ SpellCheckerSubtype subtype) {
if (info == null || listener == null || tsm == null) {
throw new NullPointerException();
}
@@ -127,6 +134,7 @@
mTextServicesManager = tsm;
mIsUsed = true;
mSpellCheckerSessionListener = listener;
+ mSubtype = subtype;
}
/**
@@ -167,6 +175,14 @@
}
/**
+ * @hide
+ */
+ public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) {
+ mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence(
+ new TextInfo[] {textInfo}, suggestionsLimit);
+ }
+
+ /**
* Get candidate strings for a substring of the specified text.
* @param textInfo text metadata for a spell checker
* @param suggestionsLimit the number of limit of suggestions returned
@@ -195,10 +211,15 @@
mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos);
}
+ private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) {
+ mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos);
+ }
+
private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub {
private static final int TASK_CANCEL = 1;
private static final int TASK_GET_SUGGESTIONS_MULTIPLE = 2;
private static final int TASK_CLOSE = 3;
+ private static final int TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE = 4;
private final Queue<SpellCheckerParams> mPendingTasks =
new LinkedList<SpellCheckerParams>();
private Handler mHandler;
@@ -236,6 +257,9 @@
case TASK_CLOSE:
processClose();
break;
+ case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE:
+ processGetSuggestionsMultipleForSentence(scp);
+ break;
}
}
@@ -266,6 +290,15 @@
suggestionsLimit, sequentialWords));
}
+ public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) {
+ if (DBG) {
+ Log.w(TAG, "getSuggestionsMultipleForSentence");
+ }
+ processOrEnqueueTask(
+ new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE,
+ textInfos, suggestionsLimit, false));
+ }
+
public void close() {
if (DBG) {
Log.w(TAG, "close");
@@ -355,10 +388,34 @@
}
}
+ private void processGetSuggestionsMultipleForSentence(SpellCheckerParams scp) {
+ if (!checkOpenConnection()) {
+ return;
+ }
+ if (DBG) {
+ Log.w(TAG, "Get suggestions from the spell checker.");
+ }
+ if (scp.mTextInfos.length != 1) {
+ throw new IllegalArgumentException();
+ }
+ try {
+ mISpellCheckerSession.onGetSuggestionsMultipleForSentence(
+ scp.mTextInfos, scp.mSuggestionsLimit);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to get suggestions " + e);
+ }
+ }
+
@Override
public void onGetSuggestions(SuggestionsInfo[] results) {
mHandler.sendMessage(Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE, results));
}
+
+ @Override
+ public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
+ mHandler.sendMessage(
+ Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results));
+ }
}
/**
@@ -370,6 +427,10 @@
* @param results an array of results of getSuggestions
*/
public void onGetSuggestions(SuggestionsInfo[] results);
+ /**
+ * @hide
+ */
+ public void onGetSuggestionsForSentence(SuggestionsInfo[] results);
}
private static class InternalListener extends ITextServicesSessionListener.Stub {
@@ -411,4 +472,11 @@
public ISpellCheckerSessionListener getSpellCheckerSessionListener() {
return mSpellCheckerSessionListenerImpl;
}
+
+ /**
+ * @hide
+ */
+ public boolean isSentenceSpellCheckSupported() {
+ return mSubtype.containsExtraValueKey(SUPPORT_SENTENCE_SPELL_CHECK);
+ }
}
diff --git a/core/java/android/view/textservice/SpellCheckerSubtype.java b/core/java/android/view/textservice/SpellCheckerSubtype.java
index aeb3ba6..1bbaf6c 100644
--- a/core/java/android/view/textservice/SpellCheckerSubtype.java
+++ b/core/java/android/view/textservice/SpellCheckerSubtype.java
@@ -21,9 +21,11 @@
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
+import android.util.Slog;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
@@ -33,11 +35,15 @@
* Subtype can describe locale (e.g. en_US, fr_FR...) used for settings.
*/
public final class SpellCheckerSubtype implements Parcelable {
+ private static final String TAG = SpellCheckerSubtype.class.getSimpleName();
+ private static final String EXTRA_VALUE_PAIR_SEPARATOR = ",";
+ private static final String EXTRA_VALUE_KEY_VALUE_SEPARATOR = "=";
private final int mSubtypeHashCode;
private final int mSubtypeNameResId;
private final String mSubtypeLocale;
private final String mSubtypeExtraValue;
+ private HashMap<String, String> mExtraValueHashMapCache;
/**
* Constructor
@@ -83,6 +89,48 @@
return mSubtypeExtraValue;
}
+ private HashMap<String, String> getExtraValueHashMap() {
+ if (mExtraValueHashMapCache == null) {
+ mExtraValueHashMapCache = new HashMap<String, String>();
+ final String[] pairs = mSubtypeExtraValue.split(EXTRA_VALUE_PAIR_SEPARATOR);
+ final int N = pairs.length;
+ for (int i = 0; i < N; ++i) {
+ final String[] pair = pairs[i].split(EXTRA_VALUE_KEY_VALUE_SEPARATOR);
+ if (pair.length == 1) {
+ mExtraValueHashMapCache.put(pair[0], null);
+ } else if (pair.length > 1) {
+ if (pair.length > 2) {
+ Slog.w(TAG, "ExtraValue has two or more '='s");
+ }
+ mExtraValueHashMapCache.put(pair[0], pair[1]);
+ }
+ }
+ }
+ return mExtraValueHashMapCache;
+ }
+
+ /**
+ * @hide
+ * The string of ExtraValue in subtype should be defined as follows:
+ * example: key0,key1=value1,key2,key3,key4=value4
+ * @param key the key of extra value
+ * @return the subtype contains specified the extra value
+ */
+ public boolean containsExtraValueKey(String key) {
+ return getExtraValueHashMap().containsKey(key);
+ }
+
+ /**
+ * @hide
+ * The string of ExtraValue in subtype should be defined as follows:
+ * example: key0,key1=value1,key2,key3,key4=value4
+ * @param key the key of extra value
+ * @return the value of the specified key
+ */
+ public String getExtraValueOf(String key) {
+ return getExtraValueHashMap().get(key);
+ }
+
@Override
public int hashCode() {
return mSubtypeHashCode;
diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java
index ddd0361..9b99770 100644
--- a/core/java/android/view/textservice/SuggestionsInfo.java
+++ b/core/java/android/view/textservice/SuggestionsInfo.java
@@ -21,11 +21,14 @@
import android.os.Parcel;
import android.os.Parcelable;
+import java.util.Arrays;
+
/**
* This class contains a metadata of suggestions from the text service
*/
public final class SuggestionsInfo implements Parcelable {
private static final String[] EMPTY = ArrayUtils.emptyArray(String.class);
+ private static final int NOT_A_LENGTH = -1;
/**
* Flag of the attributes of the suggestions that can be obtained by
@@ -47,6 +50,8 @@
public static final int RESULT_ATTR_HAS_RECOMMENDED_SUGGESTIONS = 0x0004;
private final int mSuggestionsAttributes;
private final String[] mSuggestions;
+ private final int[] mStartPosArray;
+ private final int[] mLengthArray;
private final boolean mSuggestionsAvailable;
private int mCookie;
private int mSequence;
@@ -57,16 +62,7 @@
* @param suggestions from the text service
*/
public SuggestionsInfo(int suggestionsAttributes, String[] suggestions) {
- mSuggestionsAttributes = suggestionsAttributes;
- if (suggestions == null) {
- mSuggestions = EMPTY;
- mSuggestionsAvailable = false;
- } else {
- mSuggestions = suggestions;
- mSuggestionsAvailable = true;
- }
- mCookie = 0;
- mSequence = 0;
+ this(suggestionsAttributes, suggestions, 0, 0);
}
/**
@@ -78,12 +74,46 @@
*/
public SuggestionsInfo(
int suggestionsAttributes, String[] suggestions, int cookie, int sequence) {
+ this(suggestionsAttributes, suggestions, cookie, sequence, null, null);
+ }
+
+ /**
+ * @hide
+ * Constructor.
+ * @param suggestionsAttributes from the text service
+ * @param suggestions from the text service
+ * @param cookie the cookie of the input TextInfo
+ * @param sequence the cookie of the input TextInfo
+ * @param startPosArray the array of start positions of suggestions
+ * @param lengthArray the array of length of suggestions
+ */
+ public SuggestionsInfo(
+ int suggestionsAttributes, String[] suggestions, int cookie, int sequence,
+ int[] startPosArray, int[] lengthArray) {
+ final int suggestsLen;
if (suggestions == null) {
mSuggestions = EMPTY;
mSuggestionsAvailable = false;
+ suggestsLen = 0;
+ mStartPosArray = new int[0];
+ mLengthArray = new int[0];
} else {
mSuggestions = suggestions;
mSuggestionsAvailable = true;
+ suggestsLen = suggestions.length;
+ if (startPosArray == null || lengthArray == null) {
+ mStartPosArray = new int[suggestsLen];
+ mLengthArray = new int[suggestsLen];
+ for (int i = 0; i < suggestsLen; ++i) {
+ mStartPosArray[i] = 0;
+ mLengthArray[i] = NOT_A_LENGTH;
+ }
+ } else if (suggestsLen != startPosArray.length || suggestsLen != lengthArray.length) {
+ throw new IllegalArgumentException();
+ } else {
+ mStartPosArray = Arrays.copyOf(startPosArray, suggestsLen);
+ mLengthArray = Arrays.copyOf(lengthArray, suggestsLen);
+ }
}
mSuggestionsAttributes = suggestionsAttributes;
mCookie = cookie;
@@ -96,6 +126,10 @@
mCookie = source.readInt();
mSequence = source.readInt();
mSuggestionsAvailable = source.readInt() == 1;
+ mStartPosArray = new int[mSuggestions.length];
+ mLengthArray = new int[mSuggestions.length];
+ source.readIntArray(mStartPosArray);
+ source.readIntArray(mLengthArray);
}
/**
@@ -111,6 +145,8 @@
dest.writeInt(mCookie);
dest.writeInt(mSequence);
dest.writeInt(mSuggestionsAvailable ? 1 : 0);
+ dest.writeIntArray(mStartPosArray);
+ dest.writeIntArray(mLengthArray);
}
/**
@@ -191,4 +227,24 @@
public int describeContents() {
return 0;
}
+
+ /**
+ * @hide
+ */
+ public int getSuggestionStartPosAt(int i) {
+ if (i >= 0 && i < mStartPosArray.length) {
+ return mStartPosArray[i];
+ }
+ return -1;
+ }
+
+ /**
+ * @hide
+ */
+ public int getSuggestionLengthAt(int i) {
+ if (i >= 0 && i < mLengthArray.length) {
+ return mLengthArray[i];
+ }
+ return -1;
+ }
}
diff --git a/core/java/android/view/textservice/TextServicesManager.java b/core/java/android/view/textservice/TextServicesManager.java
index 69f88a5..fc59e6e 100644
--- a/core/java/android/view/textservice/TextServicesManager.java
+++ b/core/java/android/view/textservice/TextServicesManager.java
@@ -157,7 +157,8 @@
if (subtypeInUse == null) {
return null;
}
- final SpellCheckerSession session = new SpellCheckerSession(sci, sService, listener);
+ final SpellCheckerSession session = new SpellCheckerSession(
+ sci, sService, listener, subtypeInUse);
try {
sService.getSpellCheckerService(sci.getId(), subtypeInUse.getLocale(),
session.getTextServicesSessionListener(),
diff --git a/core/java/android/webkit/HTML5VideoFullScreen.java b/core/java/android/webkit/HTML5VideoFullScreen.java
index f29aff2..cb555ea 100644
--- a/core/java/android/webkit/HTML5VideoFullScreen.java
+++ b/core/java/android/webkit/HTML5VideoFullScreen.java
@@ -261,6 +261,8 @@
mLayout.addView(getSurfaceView(), layoutParams);
mLayout.setVisibility(View.VISIBLE);
+ mLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
WebChromeClient client = webView.getWebChromeClient();
if (client != null) {
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index 3ec7e2f..7bbad78 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -17,6 +17,7 @@
package android.webkit;
import android.annotation.Widget;
+import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ClipboardManager;
@@ -61,6 +62,7 @@
import android.util.AttributeSet;
import android.util.EventLog;
import android.util.Log;
+import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.HardwareCanvas;
@@ -77,6 +79,7 @@
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ViewTreeObserver;
+import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
@@ -739,6 +742,7 @@
static final int SCREEN_ON = 136;
static final int ENTER_FULLSCREEN_VIDEO = 137;
static final int UPDATE_SELECTION = 138;
+ static final int UPDATE_ZOOM_DENSITY = 139;
private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
@@ -794,7 +798,9 @@
"AUTOFILL_COMPLETE", // = 134;
"SELECT_AT", // = 135;
"SCREEN_ON", // = 136;
- "ENTER_FULLSCREEN_VIDEO" // = 137;
+ "ENTER_FULLSCREEN_VIDEO", // = 137;
+ "UPDATE_SELECTION", // = 138;
+ "UPDATE_ZOOM_DENSITY" // = 139;
};
// If the site doesn't use the viewport meta tag to specify the viewport,
@@ -8432,6 +8438,11 @@
mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
break;
}
+ case UPDATE_ZOOM_DENSITY: {
+ final float density = (Float) msg.obj;
+ mZoomManager.updateDefaultZoomDensity(density);
+ break;
+ }
case REPLACE_BASE_CONTENT: {
nativeReplaceBaseContent(msg.arg1);
break;
@@ -8446,7 +8457,11 @@
// nativeCreate sets mNativeClass to a non-zero value
String drawableDir = BrowserFrame.getRawResFilename(
BrowserFrame.DRAWABLEDIR, mContext);
- nativeCreate(msg.arg1, drawableDir);
+ WindowManager windowManager =
+ (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+ nativeCreate(msg.arg1, drawableDir,
+ ActivityManager.isHighEndGfx(display));
if (mDelaySetPicture != null) {
setNewPicture(mDelaySetPicture, true);
mDelaySetPicture = null;
@@ -9475,7 +9490,7 @@
private native Rect nativeCacheHitNodeBounds();
private native int nativeCacheHitNodePointer();
/* package */ native void nativeClearCursor();
- private native void nativeCreate(int ptr, String drawableDir);
+ private native void nativeCreate(int ptr, String drawableDir, boolean isHighEndGfx);
private native int nativeCursorFramePointer();
private native Rect nativeCursorNodeBounds();
private native int nativeCursorNodePointer();
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index 278bd6e..42b9eab 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -2330,7 +2330,8 @@
/ mViewportDensityDpi;
}
if (adjust != mWebView.getDefaultZoomScale()) {
- mWebView.updateDefaultZoomDensity(adjust);
+ Message.obtain(mWebView.mPrivateHandler,
+ WebView.UPDATE_ZOOM_DENSITY, adjust).sendToTarget();
}
int defaultScale = (int) (adjust * 100);
diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java
index 2184297..d03db10 100644
--- a/core/java/android/widget/SpellChecker.java
+++ b/core/java/android/widget/SpellChecker.java
@@ -249,6 +249,12 @@
}
@Override
+ public void onGetSuggestionsForSentence(SuggestionsInfo[] results) {
+ // TODO: Handle the position and length for each suggestion
+ onGetSuggestions(results);
+ }
+
+ @Override
public void onGetSuggestions(SuggestionsInfo[] results) {
Editable editable = (Editable) mTextView.getText();
diff --git a/core/java/com/android/internal/os/BatteryStatsImpl.java b/core/java/com/android/internal/os/BatteryStatsImpl.java
index 3e96c81..fec4cbc 100644
--- a/core/java/com/android/internal/os/BatteryStatsImpl.java
+++ b/core/java/com/android/internal/os/BatteryStatsImpl.java
@@ -1275,7 +1275,7 @@
// record changes to the battery level.
if (mHistoryLastWritten.batteryLevel == mHistoryCur.batteryLevel &&
(dataSize >= MAX_MAX_HISTORY_BUFFER
- || ((mHistoryEnd.states^mHistoryCur.states)
+ || ((mHistoryLastWritten.states^mHistoryCur.states)
& HistoryItem.MOST_INTERESTING_STATES) == 0)) {
return;
}
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
index 3c61968..ba0aa1a 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl
@@ -24,6 +24,7 @@
oneway interface ISpellCheckerSession {
void onGetSuggestionsMultiple(
in TextInfo[] textInfos, int suggestionsLimit, boolean multipleWords);
+ void onGetSuggestionsMultipleForSentence(in TextInfo[] textInfos, int suggestionsLimit);
void onCancel();
void onClose();
}
diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
index 796b06e..b44dbc8 100644
--- a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
+++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl
@@ -23,4 +23,5 @@
*/
oneway interface ISpellCheckerSessionListener {
void onGetSuggestions(in SuggestionsInfo[] results);
+ void onGetSuggestionsForSentence(in SuggestionsInfo[] results);
}
diff --git a/core/java/com/android/internal/widget/ScrollingTabContainerView.java b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
index b7bc366..25b0065 100644
--- a/core/java/com/android/internal/widget/ScrollingTabContainerView.java
+++ b/core/java/com/android/internal/widget/ScrollingTabContainerView.java
@@ -29,6 +29,7 @@
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
+import android.view.ViewParent;
import android.view.animation.DecelerateInterpolator;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
@@ -392,7 +393,11 @@
final ActionBar.Tab tab = mTab;
final View custom = tab.getCustomView();
if (custom != null) {
- addView(custom);
+ final ViewParent customParent = custom.getParent();
+ if (customParent != this) {
+ if (customParent != null) ((ViewGroup) customParent).removeView(custom);
+ addView(custom);
+ }
mCustomView = custom;
if (mTextView != null) mTextView.setVisibility(GONE);
if (mIconView != null) {
diff --git a/core/jni/android/graphics/HarfbuzzSkia.cpp b/core/jni/android/graphics/HarfbuzzSkia.cpp
index e02070d..d78081e 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.cpp
+++ b/core/jni/android/graphics/HarfbuzzSkia.cpp
@@ -47,25 +47,14 @@
namespace android {
-static void setupPaintWithFontData(SkPaint* paint, FontData* data) {
- paint->setTypeface(data->typeFace);
- paint->setTextSize(data->textSize);
- paint->setTextSkewX(data->textSkewX);
- paint->setTextScaleX(data->textScaleX);
- paint->setFlags(data->flags);
- paint->setHinting(data->hinting);
-}
-
static HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length,
HB_Glyph* glyphs, hb_uint32* glyphsSize, HB_Bool isRTL)
{
- FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
- SkPaint paint;
- setupPaintWithFontData(&paint, data);
+ SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
- paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
uint16_t* skiaGlyphs = reinterpret_cast<uint16_t*>(glyphs);
- int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
+ int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), skiaGlyphs);
// HB_Glyph is 32-bit, but Skia outputs only 16-bit numbers. So our
// |glyphs| array needs to be converted.
@@ -80,11 +69,8 @@
static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 numGlyphs,
HB_Fixed* advances, int flags)
{
- FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
- SkPaint paint;
- setupPaintWithFontData(&paint, data);
-
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
uint16_t* glyphs16 = new uint16_t[numGlyphs];
if (!glyphs16)
@@ -92,7 +78,7 @@
for (unsigned i = 0; i < numGlyphs; ++i)
glyphs16[i] = glyphs[i];
SkScalar* scalarAdvances = reinterpret_cast<SkScalar*>(advances);
- paint.getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
+ paint->getTextWidths(glyphs16, numGlyphs * sizeof(uint16_t), scalarAdvances);
// The |advances| values which Skia outputs are SkScalars, which are floats
// in Chromium. However, Harfbuzz wants them in 26.6 fixed point format.
@@ -108,14 +94,11 @@
static HB_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 length)
{
- FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
- SkPaint paint;
- setupPaintWithFontData(&paint, data);
-
- paint.setTextEncoding(SkPaint::kUTF16_TextEncoding);
+ SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+ paint->setTextEncoding(SkPaint::kUTF16_TextEncoding);
uint16_t* glyphs16 = new uint16_t[length];
- int numGlyphs = paint.textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
+ int numGlyphs = paint->textToGlyphs(characters, length * sizeof(uint16_t), glyphs16);
bool result = true;
for (int i = 0; i < numGlyphs; ++i) {
@@ -131,22 +114,20 @@
static HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_uint32 point,
HB_Fixed* xPos, HB_Fixed* yPos, hb_uint32* resultingNumPoints)
{
- FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
- SkPaint paint;
- setupPaintWithFontData(&paint, data);
-
if (flags & HB_ShaperFlag_UseDesignMetrics)
// This is requesting pre-hinted positions. We can't support this.
return HB_Err_Invalid_Argument;
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+ SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
+
uint16_t glyph16 = glyph;
SkPath path;
- paint.getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
+ paint->getTextPath(&glyph16, sizeof(glyph16), 0, 0, &path);
uint32_t numPoints = path.getPoints(0, 0);
if (point >= numPoints)
return HB_Err_Invalid_SubTable;
- SkPoint* points = reinterpret_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
+ SkPoint* points = static_cast<SkPoint*>(malloc(sizeof(SkPoint) * (point + 1)));
if (!points)
return HB_Err_Invalid_SubTable;
// Skia does let us get a single point from the path.
@@ -161,15 +142,13 @@
static void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* metrics)
{
- FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
- SkPaint paint;
- setupPaintWithFontData(&paint, data);
+ SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
+ paint->setTextEncoding(SkPaint::kGlyphID_TextEncoding);
- paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding);
uint16_t glyph16 = glyph;
SkScalar width;
SkRect bounds;
- paint.getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
+ paint->getTextWidths(&glyph16, sizeof(glyph16), &width, &bounds);
metrics->x = SkScalarToHBFixed(bounds.fLeft);
metrics->y = SkScalarToHBFixed(bounds.fTop);
@@ -185,12 +164,10 @@
static HB_Fixed getFontMetric(HB_Font hbFont, HB_FontMetric metric)
{
- FontData* data = reinterpret_cast<FontData*>(hbFont->userData);
- SkPaint paint;
- setupPaintWithFontData(&paint, data);
+ SkPaint* paint = static_cast<SkPaint*>(hbFont->userData);
SkPaint::FontMetrics skiaMetrics;
- paint.getFontMetrics(&skiaMetrics);
+ paint->getFontMetrics(&skiaMetrics);
switch (metric) {
case HB_FontAscent:
@@ -211,10 +188,9 @@
getFontMetric,
};
-HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
+HB_Error harfbuzzSkiaGetTable(void* font, const HB_Tag tag, HB_Byte* buffer, HB_UInt* len)
{
- FontData* data = reinterpret_cast<FontData*>(voidface);
- SkTypeface* typeface = data->typeFace;
+ SkTypeface* typeface = static_cast<SkTypeface*>(font);
if (!typeface) {
LOGD("Typeface cannot be null");
diff --git a/core/jni/android/graphics/HarfbuzzSkia.h b/core/jni/android/graphics/HarfbuzzSkia.h
index 99b389a..2772f4d 100644
--- a/core/jni/android/graphics/HarfbuzzSkia.h
+++ b/core/jni/android/graphics/HarfbuzzSkia.h
@@ -47,15 +47,6 @@
return SkScalarToFloat(value) * 64.0f;
}
-typedef struct {
- SkTypeface* typeFace;
- SkScalar textSize;
- SkScalar textSkewX;
- SkScalar textScaleX;
- uint32_t flags;
- SkPaint::Hinting hinting;
-} FontData;
-
HB_Error harfbuzzSkiaGetTable(void* voidface, const HB_Tag, HB_Byte* buffer, HB_UInt* len);
extern const HB_FontClass harfbuzzSkiaClass;
diff --git a/core/jni/android/graphics/Paint.cpp b/core/jni/android/graphics/Paint.cpp
index 2ab7da9..883940b 100644
--- a/core/jni/android/graphics/Paint.cpp
+++ b/core/jni/android/graphics/Paint.cpp
@@ -484,8 +484,8 @@
jchar* glyphsArray = env->GetCharArrayElements(glyphs, NULL);
- TextLayoutCacheValue value;
- value.computeValues(paint, text, start, count, contextCount, flags);
+ TextLayoutCacheValue value(contextCount);
+ TextLayoutEngine::getInstance().computeValues(&value, paint, text, start, count, contextCount, flags);
const jchar* shapedGlyphs = value.getGlyphs();
size_t glyphsCount = value.getGlyphsCount();
memcpy(glyphsArray, shapedGlyphs, sizeof(jchar) * glyphsCount);
diff --git a/core/jni/android/graphics/TextLayoutCache.cpp b/core/jni/android/graphics/TextLayoutCache.cpp
index 5fed2f3..17492aa 100644
--- a/core/jni/android/graphics/TextLayoutCache.cpp
+++ b/core/jni/android/graphics/TextLayoutCache.cpp
@@ -34,15 +34,10 @@
#if USE_TEXT_LAYOUT_CACHE
ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutCache);
-
- static SkTypeface* gDefaultTypeface = SkFontHost::CreateTypeface(
- NULL, NULL, NULL, 0, SkTypeface::kNormal);
-
- static SkTypeface* gArabicTypeface = NULL;
- static SkTypeface* gHebrewRegularTypeface = NULL;
- static SkTypeface* gHebrewBoldTypeface = NULL;
+ ANDROID_SINGLETON_STATIC_INSTANCE(TextLayoutEngine);
#endif
+
//--------------------------------------------------------------------------------------------------
TextLayoutCache::TextLayoutCache() :
@@ -113,10 +108,12 @@
startTime = systemTime(SYSTEM_TIME_MONOTONIC);
}
- value = new TextLayoutCacheValue();
+ value = new TextLayoutCacheValue(contextCount);
// Compute advances and store them
- value->computeValues(paint, text, start, count, contextCount, dirFlags);
+ TextLayoutEngine::getInstance().computeValues(value.get(), paint,
+ reinterpret_cast<const UChar*>(text), start, count,
+ size_t(contextCount), int(dirFlags));
if (mDebugEnabled) {
value->setElapsedTime(systemTime(SYSTEM_TIME_MONOTONIC) - startTime);
@@ -193,7 +190,7 @@
value->getElapsedTime() * 0.000001f,
elapsedTimeThruCacheGet * 0.000001f,
deltaPercent,
- String8(text, count).string());
+ String8(text + start, count).string());
}
if (mCacheHitCount % DEFAULT_DUMP_STATS_CACHE_HIT_INTERVAL == 0) {
dumpCacheStats();
@@ -311,8 +308,16 @@
/**
* TextLayoutCacheValue
*/
-TextLayoutCacheValue::TextLayoutCacheValue() :
+TextLayoutCacheValue::TextLayoutCacheValue(size_t contextCount) :
mTotalAdvance(0), mElapsedTime(0) {
+ // Give a hint for advances and glyphs vectors size
+ mAdvances.setCapacity(contextCount);
+ mGlyphs.setCapacity(contextCount);
+}
+
+size_t TextLayoutCacheValue::getSize() const {
+ return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() +
+ sizeof(jchar) * mGlyphs.capacity();
}
void TextLayoutCacheValue::setElapsedTime(uint32_t time) {
@@ -323,176 +328,51 @@
return mElapsedTime;
}
-void TextLayoutCacheValue::computeValues(SkPaint* paint, const UChar* chars,
+//HB_ShaperItem TextLayoutEngine::mShaperItem;
+//HB_FontRec TextLayoutEngine::mFontRec;
+//SkPaint TextLayoutEngine::mShapingPaint;
+
+TextLayoutEngine::TextLayoutEngine() : mShaperItemGlyphArraySize(0),
+ mShaperItemLogClustersArraySize(0) {
+ mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal);
+ mArabicTypeface = NULL;
+ mHebrewRegularTypeface = NULL;
+ mHebrewBoldTypeface = NULL;
+
+ mFontRec.klass = &harfbuzzSkiaClass;
+ mFontRec.userData = 0;
+
+ // The values which harfbuzzSkiaClass returns are already scaled to
+ // pixel units, so we just set all these to one to disable further
+ // scaling.
+ mFontRec.x_ppem = 1;
+ mFontRec.y_ppem = 1;
+ mFontRec.x_scale = 1;
+ mFontRec.y_scale = 1;
+
+ memset(&mShaperItem, 0, sizeof(mShaperItem));
+
+ mShaperItem.font = &mFontRec;
+ mShaperItem.font->userData = &mShapingPaint;
+}
+
+TextLayoutEngine::~TextLayoutEngine() {
+ // FIXME should free fonts and caches but since this class is a singleton,
+ // we don't bother at the moment
+}
+
+void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags) {
- // Give a hint for advances, glyphs and log clusters vectors size
- mAdvances.setCapacity(contextCount);
- mGlyphs.setCapacity(contextCount);
computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags,
- &mAdvances, &mTotalAdvance, &mGlyphs);
+ &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs);
#if DEBUG_ADVANCES
LOGD("Advances - start=%d, count=%d, contextCount=%d, totalAdvance=%f", start, count,
contextCount, mTotalAdvance);
#endif
}
-size_t TextLayoutCacheValue::getSize() const {
- return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() +
- sizeof(jchar) * mGlyphs.capacity();
-}
-
-void TextLayoutCacheValue::initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font,
- FontData* fontData, SkPaint* paint, const UChar* chars, size_t count) {
- font->klass = &harfbuzzSkiaClass;
- font->userData = 0;
-
- // The values which harfbuzzSkiaClass returns are already scaled to
- // pixel units, so we just set all these to one to disable further
- // scaling.
- font->x_ppem = 1;
- font->y_ppem = 1;
- font->x_scale = 1;
- font->y_scale = 1;
-
- // Reset kerning
- shaperItem.kerning_applied = false;
-
- // Define font data
- fontData->textSize = paint->getTextSize();
- fontData->textSkewX = paint->getTextSkewX();
- fontData->textScaleX = paint->getTextScaleX();
- fontData->flags = paint->getFlags();
- fontData->hinting = paint->getHinting();
-
- shaperItem.font = font;
- shaperItem.font->userData = fontData;
-
- // We cannot know, ahead of time, how many glyphs a given script run
- // will produce. We take a guess that script runs will not produce more
- // than twice as many glyphs as there are code points plus a bit of
- // padding and fallback if we find that we are wrong.
- createGlyphArrays(shaperItem, (count + 2) * 2);
-
- // Create log clusters array
- shaperItem.log_clusters = new unsigned short[count];
-
- // Set the string properties
- shaperItem.string = chars;
- shaperItem.stringLength = count;
-}
-
-void TextLayoutCacheValue::freeShaperItem(HB_ShaperItem& shaperItem) {
- deleteGlyphArrays(shaperItem);
- delete[] shaperItem.log_clusters;
- HB_FreeFace(shaperItem.face);
-}
-
-unsigned TextLayoutCacheValue::shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
- size_t count, bool isRTL) {
- // Update Harfbuzz Shaper
- shaperItem.item.pos = 0;
- shaperItem.item.length = count;
- shaperItem.item.bidiLevel = isRTL;
-
- // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
- // This is needed as the Typeface used for shaping can be not the default one
- // when we are shapping any script that needs to use a fallback Font.
- // If we are a "common" script we dont need to shift
- unsigned result = 0;
- switch(shaperItem.item.script) {
- case HB_Script_Arabic:
- case HB_Script_Hebrew: {
- const uint16_t* text16 = (const uint16_t*)shaperItem.string;
- SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
- result = paint->getBaseGlyphCount(firstUnichar);
- break;
- }
- default:
- break;
- }
-
- // Set the correct Typeface depending on the script
- FontData* data = reinterpret_cast<FontData*>(shaperItem.font->userData);
- switch(shaperItem.item.script) {
- case HB_Script_Arabic:
- data->typeFace = getCachedTypeface(&gArabicTypeface, TYPEFACE_ARABIC);
-#if DEBUG_GLYPHS
- LOGD("Using Arabic Typeface");
-#endif
- break;
-
- case HB_Script_Hebrew:
- if(paint->getTypeface()) {
- switch(paint->getTypeface()->style()) {
- case SkTypeface::kNormal:
- case SkTypeface::kItalic:
- default:
- data->typeFace = getCachedTypeface(&gHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
-#if DEBUG_GLYPHS
- LOGD("Using Hebrew Regular/Italic Typeface");
-#endif
- break;
- case SkTypeface::kBold:
- case SkTypeface::kBoldItalic:
- data->typeFace = getCachedTypeface(&gHebrewBoldTypeface, TYPE_FACE_HEBREW_BOLD);
-#if DEBUG_GLYPHS
- LOGD("Using Hebrew Bold/BoldItalic Typeface");
-#endif
- break;
- }
- } else {
- data->typeFace = getCachedTypeface(&gHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
-#if DEBUG_GLYPHS
- LOGD("Using Hebrew Regular Typeface");
-#endif
- }
- break;
-
- default:
- if(paint->getTypeface()) {
- data->typeFace = paint->getTypeface();
-#if DEBUG_GLYPHS
- LOGD("Using Paint Typeface");
-#endif
- } else {
- data->typeFace = gDefaultTypeface;
-#if DEBUG_GLYPHS
- LOGD("Using Default Typeface");
-#endif
- }
- break;
- }
-
- shaperItem.face = HB_NewFace(data, harfbuzzSkiaGetTable);
-
-#if DEBUG_GLYPHS
- LOGD("Run typeFace = %p", data->typeFace);
- LOGD("Run typeFace->uniqueID = %d", data->typeFace->uniqueID());
-#endif
-
- // Shape
- while (!HB_ShapeItem(&shaperItem)) {
- // We overflowed our arrays. Resize and retry.
- // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
- deleteGlyphArrays(shaperItem);
- createGlyphArrays(shaperItem, shaperItem.num_glyphs << 1);
- }
-
- return result;
-}
-
-SkTypeface* TextLayoutCacheValue::getCachedTypeface(SkTypeface** typeface, const char path[]) {
- if (!*typeface) {
- *typeface = SkTypeface::CreateFromFile(path);
-#if DEBUG_GLYPHS
- LOGD("Created SkTypeface from file: %s", path);
-#endif
- }
- return *typeface;
-}
-
-void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
size_t start, size_t count, size_t contextCount, int dirFlags,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs) {
@@ -510,13 +390,6 @@
case kBidi_Force_RTL: forceRTL = true; break; // every char is RTL
}
- HB_ShaperItem shaperItem;
- HB_FontRec font;
- FontData fontData;
-
- // Initialize Harfbuzz Shaper
- initShaperItem(shaperItem, &font, &fontData, paint, chars, contextCount);
-
bool useSingleRun = false;
bool isRTL = forceRTL;
if (forceLTR || forceRTL) {
@@ -627,45 +500,43 @@
}
}
-void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+void TextLayoutEngine::computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
size_t count, bool isRTL,
Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
Vector<jchar>* const outGlyphs) {
- unsigned glyphBaseCount = 0;
-
*outTotalAdvance = 0;
jfloat totalAdvance = 0;
- unsigned numCodePoints = 0;
+ // Set the string properties
+ mShaperItem.string = chars;
+ mShaperItem.stringLength = count;
- ssize_t startFontRun = 0;
- ssize_t endFontRun = 0;
+ // Define shaping paint properties
+ mShapingPaint.setTextSize(paint->getTextSize());
+ mShapingPaint.setTextSkewX(paint->getTextSkewX());
+ mShapingPaint.setTextScaleX(paint->getTextScaleX());
+ mShapingPaint.setFlags(paint->getFlags());
+ mShapingPaint.setHinting(paint->getHinting());
+
+ // Split the BiDi run into Script runs. Harfbuzz will populate the pos, length and script
+ // into the shaperItem
ssize_t indexFontRun = isRTL ? count - 1 : 0;
- size_t countFontRun = 0;
-
- HB_ShaperItem shaperItem;
- HB_FontRec font;
- FontData fontData;
-
- // Zero the Shaper struct
- memset(&shaperItem, 0, sizeof(shaperItem));
-
- // Split the BiDi run into Script runs. Harfbuzz will populate the script into the shaperItem
- while((isRTL) ?
- hb_utf16_script_run_prev(&numCodePoints, &shaperItem.item, chars,
+ unsigned numCodePoints = 0;
+ while ((isRTL) ?
+ hb_utf16_script_run_prev(&numCodePoints, &mShaperItem.item, chars,
count, &indexFontRun):
- hb_utf16_script_run_next(&numCodePoints, &shaperItem.item, chars,
+ hb_utf16_script_run_next(&numCodePoints, &mShaperItem.item, chars,
count, &indexFontRun)) {
- startFontRun = shaperItem.item.pos;
- countFontRun = shaperItem.item.length;
- endFontRun = startFontRun + countFontRun;
+ ssize_t startFontRun = mShaperItem.item.pos;
+ size_t countFontRun = mShaperItem.item.length;
+ ssize_t endFontRun = startFontRun + countFontRun;
#if DEBUG_GLYPHS
- LOGD("Shaped Font Run with");
+ LOGD("Shaping Font Run with");
LOGD(" -- isRTL=%d", isRTL);
- LOGD(" -- HB script=%d", shaperItem.item.script);
+ LOGD(" -- HB script=%d", mShaperItem.item.script);
LOGD(" -- startFontRun=%d", startFontRun);
LOGD(" -- endFontRun=%d", endFontRun);
LOGD(" -- countFontRun=%d", countFontRun);
@@ -673,19 +544,17 @@
LOGD(" -- string='%s'", String8(chars, count).string());
#endif
- // Initialize Harfbuzz Shaper
- initShaperItem(shaperItem, &font, &fontData, paint, chars + startFontRun, countFontRun);
-
- // Shape the Font run and get the base glyph count for offsetting the glyphIDs later on
- glyphBaseCount = shapeFontRun(shaperItem, paint, countFontRun, isRTL);
+ // Initialize Harfbuzz Shaper and get the base glyph count for offsetting the glyphIDs
+ // and shape the Font run
+ size_t glyphBaseCount = shapeFontRun(paint, isRTL);
#if DEBUG_GLYPHS
- LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", shaperItem.num_glyphs,
- shaperItem.kerning_applied);
+ LOGD("HARFBUZZ -- num_glypth=%d - kerning_applied=%d", mShaperItem.num_glyphs,
+ mShaperItem.kerning_applied);
LOGD(" -- isDevKernText=%d", paint->isDevKernText());
LOGD(" -- glyphBaseCount=%d", glyphBaseCount);
- logGlyphs(shaperItem);
+ logGlyphs(mShaperItem);
#endif
if (isRTL) {
endFontRun = startFontRun;
@@ -699,7 +568,7 @@
#endif
}
- if (shaperItem.advances == NULL || shaperItem.num_glyphs == 0) {
+ if (mShaperItem.advances == NULL || mShaperItem.num_glyphs == 0) {
#if DEBUG_GLYPHS
LOGD("HARFBUZZ -- advances array is empty or num_glypth = 0");
#endif
@@ -708,16 +577,16 @@
}
// Get Advances and their total
- jfloat currentAdvance = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[0]]);
+ jfloat currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[0]]);
jfloat totalFontRunAdvance = currentAdvance;
outAdvances->add(currentAdvance);
for (size_t i = 1; i < countFontRun; i++) {
- size_t clusterPrevious = shaperItem.log_clusters[i - 1];
- size_t cluster = shaperItem.log_clusters[i];
+ size_t clusterPrevious = mShaperItem.log_clusters[i - 1];
+ size_t cluster = mShaperItem.log_clusters[i];
if (cluster == clusterPrevious) {
outAdvances->add(0);
} else {
- currentAdvance = HBFixedToFloat(shaperItem.advances[shaperItem.log_clusters[i]]);
+ currentAdvance = HBFixedToFloat(mShaperItem.advances[mShaperItem.log_clusters[i]]);
totalFontRunAdvance += currentAdvance;
outAdvances->add(currentAdvance);
}
@@ -733,38 +602,188 @@
// Get Glyphs and reverse them in place if RTL
if (outGlyphs) {
- size_t countGlyphs = shaperItem.num_glyphs;
+ size_t countGlyphs = mShaperItem.num_glyphs;
for (size_t i = 0; i < countGlyphs; i++) {
jchar glyph = glyphBaseCount +
- (jchar) shaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
+ (jchar) mShaperItem.glyphs[(!isRTL) ? i : countGlyphs - 1 - i];
#if DEBUG_GLYPHS
LOGD("HARFBUZZ -- glyph[%d]=%d", i, glyph);
#endif
outGlyphs->add(glyph);
}
}
- // Cleaning
- freeShaperItem(shaperItem);
}
*outTotalAdvance = totalAdvance;
}
-void TextLayoutCacheValue::deleteGlyphArrays(HB_ShaperItem& shaperItem) {
- delete[] shaperItem.glyphs;
- delete[] shaperItem.attributes;
- delete[] shaperItem.advances;
- delete[] shaperItem.offsets;
+
+size_t TextLayoutEngine::shapeFontRun(SkPaint* paint, bool isRTL) {
+ // Reset kerning
+ mShaperItem.kerning_applied = false;
+
+ // Update Harfbuzz Shaper
+ mShaperItem.item.bidiLevel = isRTL;
+
+ SkTypeface* typeface = paint->getTypeface();
+
+ // Set the correct Typeface depending on the script
+ switch (mShaperItem.item.script) {
+ case HB_Script_Arabic:
+ typeface = getCachedTypeface(&mArabicTypeface, TYPEFACE_ARABIC);
+#if DEBUG_GLYPHS
+ LOGD("Using Arabic Typeface");
+#endif
+ break;
+
+ case HB_Script_Hebrew:
+ if (typeface) {
+ switch (typeface->style()) {
+ case SkTypeface::kBold:
+ case SkTypeface::kBoldItalic:
+ typeface = getCachedTypeface(&mHebrewBoldTypeface, TYPE_FACE_HEBREW_BOLD);
+#if DEBUG_GLYPHS
+ LOGD("Using Hebrew Bold/BoldItalic Typeface");
+#endif
+ break;
+
+ case SkTypeface::kNormal:
+ case SkTypeface::kItalic:
+ default:
+ typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
+#if DEBUG_GLYPHS
+ LOGD("Using Hebrew Regular/Italic Typeface");
+#endif
+ break;
+ }
+ } else {
+ typeface = getCachedTypeface(&mHebrewRegularTypeface, TYPE_FACE_HEBREW_REGULAR);
+#if DEBUG_GLYPHS
+ LOGD("Using Hebrew Regular Typeface");
+#endif
+ }
+ break;
+
+ default:
+ if (!typeface) {
+ typeface = mDefaultTypeface;
+#if DEBUG_GLYPHS
+ LOGD("Using Default Typeface");
+#endif
+ } else {
+#if DEBUG_GLYPHS
+ LOGD("Using Paint Typeface");
+#endif
+ }
+ break;
+ }
+
+ mShapingPaint.setTypeface(typeface);
+ mShaperItem.face = getCachedHBFace(typeface);
+
+#if DEBUG_GLYPHS
+ LOGD("Run typeFace = %p, uniqueID = %d, hb_face = %p",
+ typeface, typeface->uniqueID(), mShaperItem.face);
+#endif
+
+ // Get the glyphs base count for offsetting the glyphIDs returned by Harfbuzz
+ // This is needed as the Typeface used for shaping can be not the default one
+ // when we are shaping any script that needs to use a fallback Font.
+ // If we are a "common" script we dont need to shift
+ size_t baseGlyphCount = 0;
+ switch (mShaperItem.item.script) {
+ case HB_Script_Arabic:
+ case HB_Script_Hebrew: {
+ const uint16_t* text16 = (const uint16_t*)mShaperItem.string;
+ SkUnichar firstUnichar = SkUTF16_NextUnichar(&text16);
+ baseGlyphCount = paint->getBaseGlyphCount(firstUnichar);
+ break;
+ }
+ default:
+ break;
+ }
+
+ // Shape
+ ensureShaperItemLogClustersArray(mShaperItem.item.length);
+ ensureShaperItemGlyphArrays(mShaperItem.item.length * 3 / 2);
+ mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
+ while (!HB_ShapeItem(&mShaperItem)) {
+ // We overflowed our glyph arrays. Resize and retry.
+ // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size.
+ ensureShaperItemGlyphArrays(mShaperItem.num_glyphs * 2);
+ mShaperItem.num_glyphs = mShaperItemGlyphArraySize;
+ }
+ return baseGlyphCount;
}
-void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem& shaperItem, int size) {
+void TextLayoutEngine::ensureShaperItemGlyphArrays(size_t size) {
+ if (size > mShaperItemGlyphArraySize) {
+ deleteShaperItemGlyphArrays();
+ createShaperItemGlyphArrays(size);
+ }
+}
+
+void TextLayoutEngine::createShaperItemGlyphArrays(size_t size) {
#if DEBUG_GLYPHS
LOGD("createGlyphArrays -- size=%d", size);
#endif
- shaperItem.glyphs = new HB_Glyph[size];
- shaperItem.attributes = new HB_GlyphAttributes[size];
- shaperItem.advances = new HB_Fixed[size];
- shaperItem.offsets = new HB_FixedPoint[size];
- shaperItem.num_glyphs = size;
+ mShaperItemGlyphArraySize = size;
+ mShaperItem.glyphs = new HB_Glyph[size];
+ mShaperItem.attributes = new HB_GlyphAttributes[size];
+ mShaperItem.advances = new HB_Fixed[size];
+ mShaperItem.offsets = new HB_FixedPoint[size];
+}
+
+void TextLayoutEngine::deleteShaperItemGlyphArrays() {
+ delete[] mShaperItem.glyphs;
+ delete[] mShaperItem.attributes;
+ delete[] mShaperItem.advances;
+ delete[] mShaperItem.offsets;
+}
+
+void TextLayoutEngine::ensureShaperItemLogClustersArray(size_t size) {
+ if (size > mShaperItemLogClustersArraySize) {
+ deleteShaperItemLogClustersArray();
+ createShaperItemLogClustersArray(size);
+ }
+}
+
+void TextLayoutEngine::createShaperItemLogClustersArray(size_t size) {
+#if DEBUG_GLYPHS
+ LOGD("createLogClustersArray -- size=%d", size);
+#endif
+ mShaperItemLogClustersArraySize = size;
+ mShaperItem.log_clusters = new unsigned short[size];
+}
+
+void TextLayoutEngine::deleteShaperItemLogClustersArray() {
+ delete[] mShaperItem.log_clusters;
+}
+
+SkTypeface* TextLayoutEngine::getCachedTypeface(SkTypeface** typeface, const char path[]) {
+ if (!*typeface) {
+ *typeface = SkTypeface::CreateFromFile(path);
+ (*typeface)->ref();
+#if DEBUG_GLYPHS
+ LOGD("Created SkTypeface from file: %s", path);
+#endif
+ }
+ return *typeface;
+}
+
+HB_Face TextLayoutEngine::getCachedHBFace(SkTypeface* typeface) {
+ SkFontID fontId = typeface->uniqueID();
+ ssize_t index = mCachedHBFaces.indexOfKey(fontId);
+ if (index >= 0) {
+ return mCachedHBFaces.valueAt(index);
+ }
+ HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable);
+ if (face) {
+#if DEBUG_GLYPHS
+ LOGD("Created HB_NewFace %p from paint typeface: %p", face, typeface);
+#endif
+ mCachedHBFaces.add(fontId, face);
+ }
+ return face;
}
} // namespace android
diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h
index 5c938f7..dfdcd03 100644
--- a/core/jni/android/graphics/TextLayoutCache.h
+++ b/core/jni/android/graphics/TextLayoutCache.h
@@ -23,6 +23,7 @@
#include <utils/threads.h>
#include <utils/String16.h>
#include <utils/GenerationCache.h>
+#include <utils/KeyedVector.h>
#include <utils/Compare.h>
#include <utils/RefBase.h>
#include <utils/Singleton.h>
@@ -116,14 +117,11 @@
*/
class TextLayoutCacheValue : public RefBase {
public:
- TextLayoutCacheValue();
+ TextLayoutCacheValue(size_t contextCount);
void setElapsedTime(uint32_t time);
uint32_t getElapsedTime();
- void computeValues(SkPaint* paint, const UChar* chars, size_t start, size_t count,
- size_t contextCount, int dirFlags);
-
inline const jfloat* getAdvances() const { return mAdvances.array(); }
inline size_t getAdvancesCount() const { return mAdvances.size(); }
inline jfloat getTotalAdvance() const { return mTotalAdvance; }
@@ -131,12 +129,6 @@
inline size_t getGlyphsCount() const { return mGlyphs.size(); }
/**
- * Get the size of the Cache entry
- */
- size_t getSize() const;
-
-private:
- /**
* Advances vector
*/
Vector<jfloat> mAdvances;
@@ -152,34 +144,16 @@
Vector<jchar> mGlyphs;
/**
+ * Get the size of the Cache entry
+ */
+ size_t getSize() const;
+
+private:
+ /**
* Time for computing the values (in milliseconds)
*/
uint32_t mElapsedTime;
- static void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
- size_t start, size_t count, size_t contextCount, int dirFlags,
- Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
- Vector<jchar>* const outGlyphs);
-
- static void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
- size_t count, bool isRTL,
- Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
- Vector<jchar>* const outGlyphs);
-
- static void initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font, FontData* fontData,
- SkPaint* paint, const UChar* chars, size_t count);
-
- static void freeShaperItem(HB_ShaperItem& shaperItem);
-
- static unsigned shapeFontRun(HB_ShaperItem& shaperItem, SkPaint* paint,
- size_t count, bool isRTL);
-
- static SkTypeface* getCachedTypeface(SkTypeface** typeface, const char path[]);
-
- static void deleteGlyphArrays(HB_ShaperItem& shaperItem);
-
- static void createGlyphArrays(HB_ShaperItem& shaperItem, int size);
-
}; // TextLayoutCacheValue
/**
@@ -240,6 +214,72 @@
}; // TextLayoutCache
+/**
+ * The TextLayoutEngine is responsible for shaping with Harfbuzz library
+ */
+class TextLayoutEngine : public Singleton<TextLayoutEngine> {
+public:
+ TextLayoutEngine();
+ virtual ~TextLayoutEngine();
+
+ void computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars,
+ size_t start, size_t count, size_t contextCount, int dirFlags);
+
+private:
+ /**
+ * Harfbuzz shaper item
+ */
+ HB_ShaperItem mShaperItem;
+
+ /**
+ * Harfbuzz font
+ */
+ HB_FontRec mFontRec;
+
+ /**
+ * Skia Paint used for shaping
+ */
+ SkPaint mShapingPaint;
+
+ /**
+ * Skia typefaces cached for shaping
+ */
+ SkTypeface* mDefaultTypeface;
+ SkTypeface* mArabicTypeface;
+ SkTypeface* mHebrewRegularTypeface;
+ SkTypeface* mHebrewBoldTypeface;
+
+ KeyedVector<SkFontID, HB_Face> mCachedHBFaces;
+
+ size_t mShaperItemGlyphArraySize;
+ size_t mShaperItemLogClustersArraySize;
+
+ size_t shapeFontRun(SkPaint* paint, bool isRTL);
+
+ void computeValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+ size_t start, size_t count, size_t contextCount, int dirFlags,
+ Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+ Vector<jchar>* const outGlyphs);
+
+ void computeRunValuesWithHarfbuzz(SkPaint* paint, const UChar* chars,
+ size_t count, bool isRTL,
+ Vector<jfloat>* const outAdvances, jfloat* outTotalAdvance,
+ Vector<jchar>* const outGlyphs);
+
+ SkTypeface* getCachedTypeface(SkTypeface** typeface, const char path[]);
+ HB_Face getCachedHBFace(SkTypeface* typeface);
+
+ void ensureShaperItemGlyphArrays(size_t size);
+ void createShaperItemGlyphArrays(size_t size);
+ void deleteShaperItemGlyphArrays();
+
+ void ensureShaperItemLogClustersArray(size_t size);
+ void createShaperItemLogClustersArray(size_t size);
+ void deleteShaperItemLogClustersArray();
+
+}; // TextLayoutEngine
+
+
} // namespace android
#endif /* ANDROID_TEXT_LAYOUT_CACHE_H */
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 25f7d25..326f186 100755
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -314,9 +314,12 @@
<integer name="config_carDockKeepsScreenOn">1</integer>
<!-- Control whether being in the desk dock should enable accelerometer
- based screen orientation. Note this should probably default to true
- like car dock, but we haven't had a chance to test it. -->
- <bool name="config_deskDockEnablesAccelerometer">false</bool>
+ based screen orientation. This defaults to true because it is
+ common for desk docks to be sold in a variety of form factors
+ with different orientations. Since we cannot always tell these docks
+ apart and the docks cannot report their true orientation on their own,
+ we rely on gravity to determine the effective orientation. -->
+ <bool name="config_deskDockEnablesAccelerometer">true</bool>
<!-- Control whether being in the car dock should enable accelerometer based
screen orientation. This defaults to true because putting a device in
diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h
index 27d863d..15c2bab 100644
--- a/include/gui/SurfaceTexture.h
+++ b/include/gui/SurfaceTexture.h
@@ -202,6 +202,10 @@
// getCurrentScalingMode returns the scaling mode of the current buffer
uint32_t getCurrentScalingMode() const;
+ // isSynchronousMode returns whether the SurfaceTexture is currently in
+ // synchronous mode.
+ bool isSynchronousMode() const;
+
// abandon frees all the buffers and puts the SurfaceTexture into the
// 'abandoned' state. Once put in this state the SurfaceTexture can never
// leave it. When in the 'abandoned' state, all methods of the
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 8c1c593..446720b 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -153,6 +153,9 @@
bool mStarted;
int32_t mNumFramesEncoded;
+ // Time between capture of two frames.
+ int64_t mTimeBetweenFrameCaptureUs;
+
CameraSource(const sp<ICamera>& camera, const sp<ICameraRecordingProxy>& proxy,
int32_t cameraId,
Size videoSize, int32_t frameRate,
diff --git a/include/media/stagefright/CameraSourceTimeLapse.h b/include/media/stagefright/CameraSourceTimeLapse.h
index 0e264c7..b060691 100644
--- a/include/media/stagefright/CameraSourceTimeLapse.h
+++ b/include/media/stagefright/CameraSourceTimeLapse.h
@@ -57,10 +57,6 @@
int32_t mVideoWidth;
int32_t mVideoHeight;
- // Time between capture of two frames during time lapse recording
- // Negative value indicates that timelapse is disabled.
- int64_t mTimeBetweenTimeLapseFrameCaptureUs;
-
// Time between two frames in final video (1/frameRate)
int64_t mTimeBetweenTimeLapseVideoFramesUs;
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index c21d19d..84f8282 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -336,6 +336,10 @@
int64_t retrieveDecodingTimeUs(bool isCodecSpecific);
+ status_t parseAVCCodecSpecificData(
+ const void *data, size_t size,
+ unsigned *profile, unsigned *level);
+
OMXCodec(const OMXCodec &);
OMXCodec &operator=(const OMXCodec &);
};
diff --git a/libs/gui/Android.mk b/libs/gui/Android.mk
index 2d716c7..b7e3ee3 100644
--- a/libs/gui/Android.mk
+++ b/libs/gui/Android.mk
@@ -32,6 +32,10 @@
LOCAL_MODULE:= libgui
+ifeq ($(TARGET_BOARD_PLATFORM), tegra)
+ LOCAL_CFLAGS += -DALLOW_DEQUEUE_CURRENT_BUFFER
+endif
+
include $(BUILD_SHARED_LIBRARY)
ifeq (,$(ONE_SHOT_MAKEFILE))
diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp
index 175de69..b4d01a9 100644
--- a/libs/gui/SurfaceTexture.cpp
+++ b/libs/gui/SurfaceTexture.cpp
@@ -36,8 +36,12 @@
#include <utils/Log.h>
#include <utils/String8.h>
-
-#define ALLOW_DEQUEUE_CURRENT_BUFFER false
+#ifdef ALLOW_DEQUEUE_CURRENT_BUFFER
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER true
+#warning "ALLOW_DEQUEUE_CURRENT_BUFFER enabled"
+#else
+#define FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER false
+#endif
// Macros for including the SurfaceTexture name in log messages
#define ST_LOGV(x, ...) ALOGV("[%s] "x, mName.string(), ##__VA_ARGS__)
@@ -325,7 +329,7 @@
LOGW_IF((state == BufferSlot::FREE) && (mCurrentTexture==i),
"dequeueBuffer: buffer %d is both FREE and current!", i);
- if (ALLOW_DEQUEUE_CURRENT_BUFFER) {
+ if (FLAG_ALLOW_DEQUEUE_CURRENT_BUFFER) {
if (state == BufferSlot::FREE || i == mCurrentTexture) {
foundSync = i;
if (i != mCurrentTexture) {
@@ -642,8 +646,9 @@
Mutex::Autolock lock(mMutex);
if (mAbandoned) {
- ST_LOGE("disconnect: SurfaceTexture has been abandoned!");
- return NO_INIT;
+ // it is not really an error to disconnect after the surface
+ // has been abandoned, it should just be a no-op.
+ return NO_ERROR;
}
int err = NO_ERROR;
@@ -1005,6 +1010,11 @@
return mCurrentScalingMode;
}
+bool SurfaceTexture::isSynchronousMode() const {
+ Mutex::Autolock lock(mMutex);
+ return mSynchronousMode;
+}
+
int SurfaceTexture::query(int what, int* outValue)
{
Mutex::Autolock lock(mMutex);
diff --git a/libs/gui/tests/SurfaceTexture_test.cpp b/libs/gui/tests/SurfaceTexture_test.cpp
index 4268782..c79e69a 100644
--- a/libs/gui/tests/SurfaceTexture_test.cpp
+++ b/libs/gui/tests/SurfaceTexture_test.cpp
@@ -396,7 +396,8 @@
1.0f, 1.0f,
};
- glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0, triangleVertices);
+ glVertexAttribPointer(mPositionHandle, 2, GL_FLOAT, GL_FALSE, 0,
+ triangleVertices);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
glEnableVertexAttribArray(mPositionHandle);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
@@ -410,13 +411,17 @@
// XXX: These calls are not needed for GL_TEXTURE_EXTERNAL_OES as
// they're setting the defautls for that target, but when hacking things
// to use GL_TEXTURE_2D they are needed to achieve the same behavior.
- glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MIN_FILTER,
+ GL_LINEAR);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_MAG_FILTER,
+ GL_LINEAR);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S,
+ GL_CLAMP_TO_EDGE);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
- glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T,
+ GL_CLAMP_TO_EDGE);
ASSERT_EQ(GLenum(GL_NO_ERROR), glGetError());
GLfloat texMatrix[16];
@@ -640,8 +645,8 @@
for (int i = 0; i < 5; i++) {
const android_native_rect_t& crop(crops[i]);
- SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }", crop.left,
- crop.top, crop.right, crop.bottom).string());
+ SCOPED_TRACE(String8::format("rect{ l: %d t: %d r: %d b: %d }",
+ crop.left, crop.top, crop.right, crop.bottom).string());
ASSERT_EQ(NO_ERROR, native_window_set_crop(mANW.get(), &crop));
@@ -650,13 +655,15 @@
ASSERT_TRUE(anb != NULL);
sp<GraphicBuffer> buf(new GraphicBuffer(anb, false));
- ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(), buf->getNativeBuffer()));
+ ASSERT_EQ(NO_ERROR, mANW->lockBuffer(mANW.get(),
+ buf->getNativeBuffer()));
uint8_t* img = NULL;
buf->lock(GRALLOC_USAGE_SW_WRITE_OFTEN, (void**)(&img));
fillYV12BufferRect(img, texWidth, texHeight, buf->getStride(), crop);
buf->unlock();
- ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), buf->getNativeBuffer()));
+ ASSERT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(),
+ buf->getNativeBuffer()));
mST->updateTexImage();
@@ -708,7 +715,8 @@
class ProducerThread : public Thread {
public:
- ProducerThread(const sp<ANativeWindow>& anw, const TestPixel* testPixels):
+ ProducerThread(const sp<ANativeWindow>& anw,
+ const TestPixel* testPixels):
mANW(anw),
mTestPixels(testPixels) {
}
@@ -940,82 +948,6 @@
EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35));
}
-TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) {
- const int texWidth = 64;
- const int texHeight = 64;
-
- mST->setDefaultBufferSize(texWidth, texHeight);
-
- // Do the producer side of things
- EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
-
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- glClearColor(0.6, 0.6, 0.6, 0.6);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glEnable(GL_SCISSOR_TEST);
- glScissor(4, 4, 4, 4);
- glClearColor(1.0, 0.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(24, 48, 4, 4);
- glClearColor(0.0, 1.0, 0.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glScissor(37, 17, 4, 4);
- glClearColor(0.0, 0.0, 1.0, 1.0);
- glClear(GL_COLOR_BUFFER_BIT);
-
- eglSwapBuffers(mEglDisplay, stcEglSurface);
-
- // Do the consumer side of things
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- glDisable(GL_SCISSOR_TEST);
-
- mST->updateTexImage();
-
- // We must wait until updateTexImage has been called to destroy the
- // EGLSurface because we're in synchronous mode.
- eglDestroySurface(mEglDisplay, stcEglSurface);
-
- glClearColor(0.2, 0.2, 0.2, 0.2);
- glClear(GL_COLOR_BUFFER_BIT);
-
- glViewport(0, 0, texWidth, texHeight);
- drawTexture();
-
- EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
-
- EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255));
- EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255));
- EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255));
- EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
- EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
-}
-
TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) {
class ProducerThread : public Thread {
public:
@@ -1093,13 +1025,284 @@
reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError());
}
+TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) {
+ int texHeight = 16;
+ ANativeWindowBuffer* anb;
+
+ GLint maxTextureSize;
+ glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
+
+ // make sure it works with small textures
+ mST->setDefaultBufferSize(16, texHeight);
+ EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ EXPECT_EQ(16, anb->width);
+ EXPECT_EQ(texHeight, anb->height);
+ EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
+ EXPECT_EQ(NO_ERROR, mST->updateTexImage());
+
+ // make sure it works with GL_MAX_TEXTURE_SIZE
+ mST->setDefaultBufferSize(maxTextureSize, texHeight);
+ EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ EXPECT_EQ(maxTextureSize, anb->width);
+ EXPECT_EQ(texHeight, anb->height);
+ EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
+ EXPECT_EQ(NO_ERROR, mST->updateTexImage());
+
+ // make sure it fails with GL_MAX_TEXTURE_SIZE+1
+ mST->setDefaultBufferSize(maxTextureSize+1, texHeight);
+ EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
+ EXPECT_EQ(maxTextureSize+1, anb->width);
+ EXPECT_EQ(texHeight, anb->height);
+ EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
+ ASSERT_NE(NO_ERROR, mST->updateTexImage());
+}
+
/*
- * This test is for testing GL -> GL texture streaming via SurfaceTexture. It
- * contains functionality to create a producer thread that will perform GL
- * rendering to an ANativeWindow that feeds frames to a SurfaceTexture.
- * Additionally it supports interlocking the producer and consumer threads so
- * that a specific sequence of calls can be deterministically created by the
- * test.
+ * This test fixture is for testing GL -> GL texture streaming. It creates an
+ * EGLSurface and an EGLContext for the image producer to use.
+ */
+class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+protected:
+ SurfaceTextureGLToGLTest():
+ mProducerEglSurface(EGL_NO_SURFACE),
+ mProducerEglContext(EGL_NO_CONTEXT) {
+ }
+
+ virtual void SetUp() {
+ SurfaceTextureGLTest::SetUp();
+
+ EGLConfig myConfig = {0};
+ EGLint numConfigs = 0;
+ EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
+ 1, &numConfigs));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
+ mANW.get(), NULL);
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
+
+ mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
+ EGL_NO_CONTEXT, getContextAttribs());
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
+ }
+
+ virtual void TearDown() {
+ if (mProducerEglContext != EGL_NO_CONTEXT) {
+ eglDestroyContext(mEglDisplay, mProducerEglContext);
+ }
+ if (mProducerEglSurface != EGL_NO_SURFACE) {
+ eglDestroySurface(mEglDisplay, mProducerEglSurface);
+ }
+ SurfaceTextureGLTest::TearDown();
+ }
+
+ EGLSurface mProducerEglSurface;
+ EGLContext mProducerEglContext;
+};
+
+TEST_F(SurfaceTextureGLToGLTest, TexturingFromGLFilledRGBABufferPow2) {
+ const int texWidth = 64;
+ const int texHeight = 64;
+
+ mST->setDefaultBufferSize(texWidth, texHeight);
+
+ // Do the producer side of things
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // This is needed to ensure we pick up a buffer of the correct size.
+ eglSwapBuffers(mEglDisplay, mProducerEglSurface);
+
+ glClearColor(0.6, 0.6, 0.6, 0.6);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_SCISSOR_TEST);
+ glScissor(4, 4, 4, 4);
+ glClearColor(1.0, 0.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glScissor(24, 48, 4, 4);
+ glClearColor(0.0, 1.0, 0.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glScissor(37, 17, 4, 4);
+ glClearColor(0.0, 0.0, 1.0, 1.0);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ eglSwapBuffers(mEglDisplay, mProducerEglSurface);
+
+ // Do the consumer side of things
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ glDisable(GL_SCISSOR_TEST);
+
+ mST->updateTexImage(); // Skip the first frame, which was empty
+ mST->updateTexImage();
+
+ glClearColor(0.2, 0.2, 0.2, 0.2);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ glViewport(0, 0, texWidth, texHeight);
+ drawTexture();
+
+ EXPECT_TRUE(checkPixel( 0, 0, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(63, 0, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(63, 63, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 0, 63, 153, 153, 153, 153));
+
+ EXPECT_TRUE(checkPixel( 4, 7, 255, 0, 0, 255));
+ EXPECT_TRUE(checkPixel(25, 51, 0, 255, 0, 255));
+ EXPECT_TRUE(checkPixel(40, 19, 0, 0, 255, 255));
+ EXPECT_TRUE(checkPixel(29, 51, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 5, 32, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(13, 8, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(46, 3, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(30, 33, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 6, 52, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(55, 33, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(16, 29, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 1, 30, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(41, 37, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(46, 29, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel(15, 25, 153, 153, 153, 153));
+ EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153));
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) {
+ sp<GraphicBuffer> buffers[3];
+
+ // This test requires async mode to run on a single thread.
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ for (int i = 0; i < 3; i++) {
+ // Produce a frame
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ glClear(GL_COLOR_BUFFER_BIT);
+ eglSwapBuffers(mEglDisplay, mProducerEglSurface);
+
+ // Consume a frame
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ mST->updateTexImage();
+ buffers[i] = mST->getCurrentBuffer();
+ }
+
+ // Destroy the GL texture object to release its ref on buffers[2].
+ GLuint texID = TEX_ID;
+ glDeleteTextures(1, &texID);
+
+ // Destroy the EGLSurface
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // Release the ref that the SurfaceTexture has on buffers[2].
+ mST->abandon();
+
+ EXPECT_EQ(1, buffers[0]->getStrongCount());
+ EXPECT_EQ(1, buffers[1]->getStrongCount());
+
+ // Depending on how lazily the GL driver dequeues buffers, we may end up
+ // with either two or three total buffers. If there are three, make sure
+ // the last one was properly down-ref'd.
+ if (buffers[2] != buffers[0]) {
+ EXPECT_EQ(1, buffers[2]->getStrongCount());
+ }
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
+ sp<GraphicBuffer> buffers[3];
+
+ // This test requires async mode to run on a single thread.
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ EXPECT_TRUE(eglSwapInterval(mEglDisplay, 0));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ for (int i = 0; i < 3; i++) {
+ // Produce a frame
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // Consume a frame
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+ buffers[i] = mST->getCurrentBuffer();
+ }
+
+ // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
+ // on buffers[2].
+ mST->abandon();
+
+ // Destroy the GL texture object to release its ref on buffers[2].
+ GLuint texID = TEX_ID;
+ glDeleteTextures(1, &texID);
+
+ // Destroy the EGLSurface.
+ EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ EXPECT_EQ(1, buffers[0]->getStrongCount());
+ EXPECT_EQ(1, buffers[1]->getStrongCount());
+
+ // Depending on how lazily the GL driver dequeues buffers, we may end up
+ // with either two or three total buffers. If there are three, make sure
+ // the last one was properly down-ref'd.
+ if (buffers[2] != buffers[0]) {
+ EXPECT_EQ(1, buffers[2]->getStrongCount());
+ }
+}
+
+TEST_F(SurfaceTextureGLToGLTest, EglSurfaceDefaultsToSynchronousMode) {
+ // This test requires 3 buffers to run on a single thread.
+ mST->setBufferCountServer(3);
+
+ ASSERT_TRUE(mST->isSynchronousMode());
+
+ for (int i = 0; i < 10; i++) {
+ // Produce a frame
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface,
+ mProducerEglSurface, mProducerEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ glClear(GL_COLOR_BUFFER_BIT);
+ EXPECT_TRUE(eglSwapBuffers(mEglDisplay, mProducerEglSurface));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+
+ // Consume a frame
+ EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
+ mEglContext));
+ ASSERT_EQ(EGL_SUCCESS, eglGetError());
+ ASSERT_EQ(NO_ERROR, mST->updateTexImage());
+ }
+
+ ASSERT_TRUE(mST->isSynchronousMode());
+}
+
+/*
+ * This test fixture is for testing GL -> GL texture streaming from one thread
+ * to another. It contains functionality to create a producer thread that will
+ * perform GL rendering to an ANativeWindow that feeds frames to a
+ * SurfaceTexture. Additionally it supports interlocking the producer and
+ * consumer threads so that a specific sequence of calls can be
+ * deterministically created by the test.
*
* The intended usage is as follows:
*
@@ -1122,7 +1325,7 @@
* }
*
*/
-class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest {
+class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest {
protected:
// ProducerThread is an abstract base class to simplify the creation of
@@ -1223,30 +1426,8 @@
Condition mFrameFinishCondition;
};
- SurfaceTextureGLToGLTest():
- mProducerEglSurface(EGL_NO_SURFACE),
- mProducerEglContext(EGL_NO_CONTEXT) {
- }
-
virtual void SetUp() {
- SurfaceTextureGLTest::SetUp();
-
- EGLConfig myConfig = {0};
- EGLint numConfigs = 0;
- EXPECT_TRUE(eglChooseConfig(mEglDisplay, getConfigAttribs(), &myConfig,
- 1, &numConfigs));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- mProducerEglSurface = eglCreateWindowSurface(mEglDisplay, myConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, mProducerEglSurface);
-
- mProducerEglContext = eglCreateContext(mEglDisplay, myConfig,
- EGL_NO_CONTEXT, getContextAttribs());
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_CONTEXT, mProducerEglContext);
-
+ SurfaceTextureGLToGLTest::SetUp();
mFC = new FrameCondition();
mST->setFrameAvailableListener(mFC);
}
@@ -1255,15 +1436,9 @@
if (mProducerThread != NULL) {
mProducerThread->requestExitAndWait();
}
- if (mProducerEglContext != EGL_NO_CONTEXT) {
- eglDestroyContext(mEglDisplay, mProducerEglContext);
- }
- if (mProducerEglSurface != EGL_NO_SURFACE) {
- eglDestroySurface(mEglDisplay, mProducerEglSurface);
- }
mProducerThread.clear();
mFC.clear();
- SurfaceTextureGLTest::TearDown();
+ SurfaceTextureGLToGLTest::TearDown();
}
void runProducerThread(const sp<ProducerThread> producerThread) {
@@ -1274,13 +1449,12 @@
producerThread->run();
}
- EGLSurface mProducerEglSurface;
- EGLContext mProducerEglContext;
sp<ProducerThread> mProducerThread;
sp<FrameCondition> mFC;
};
-TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+ UpdateTexImageBeforeFrameFinishedCompletes) {
class PT : public ProducerThread {
virtual void render() {
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1298,7 +1472,8 @@
// TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
-TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+ UpdateTexImageAfterFrameFinishedCompletes) {
class PT : public ProducerThread {
virtual void render() {
glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
@@ -1316,7 +1491,8 @@
// TODO: Add frame verification once RGB TEX_EXTERNAL_OES is supported!
}
-TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageBeforeFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+ RepeatedUpdateTexImageBeforeFrameFinishedCompletes) {
enum { NUM_ITERATIONS = 1024 };
class PT : public ProducerThread {
@@ -1344,7 +1520,8 @@
}
}
-TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+ RepeatedUpdateTexImageAfterFrameFinishedCompletes) {
enum { NUM_ITERATIONS = 1024 };
class PT : public ProducerThread {
@@ -1373,7 +1550,8 @@
}
// XXX: This test is disabled because it is currently hanging on some devices.
-TEST_F(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) {
+TEST_F(SurfaceTextureGLThreadToGLTest,
+ DISABLED_RepeatedSwapBuffersWhileDequeueStalledCompletes) {
enum { NUM_ITERATIONS = 64 };
class PT : public ProducerThread {
@@ -1438,118 +1616,4 @@
}
}
-TEST_F(SurfaceTextureGLTest, EglDestroySurfaceUnrefsBuffers) {
- EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
-
- sp<GraphicBuffer> buffers[3];
-
- for (int i = 0; i < 3; i++) {
- // Produce a frame
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- glClear(GL_COLOR_BUFFER_BIT);
- eglSwapBuffers(mEglDisplay, stcEglSurface);
-
- // Consume a frame
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- mST->updateTexImage();
- buffers[i] = mST->getCurrentBuffer();
- }
-
- // Destroy the GL texture object to release its ref on buffers[2].
- GLuint texID = TEX_ID;
- glDeleteTextures(1, &texID);
-
- // Destroy the EGLSurface
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Release the ref that the SurfaceTexture has on buffers[2].
- mST->abandon();
-
- EXPECT_EQ(1, buffers[0]->getStrongCount());
- EXPECT_EQ(1, buffers[1]->getStrongCount());
- EXPECT_EQ(1, buffers[2]->getStrongCount());
-}
-
-TEST_F(SurfaceTextureGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) {
- EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig,
- mANW.get(), NULL);
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_NE(EGL_NO_SURFACE, stcEglSurface);
-
- sp<GraphicBuffer> buffers[3];
-
- for (int i = 0; i < 3; i++) {
- // Produce a frame
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- glClear(GL_COLOR_BUFFER_BIT);
- EXPECT_TRUE(eglSwapBuffers(mEglDisplay, stcEglSurface));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- // Consume a frame
- EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface,
- mEglContext));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
- ASSERT_EQ(NO_ERROR, mST->updateTexImage());
- buffers[i] = mST->getCurrentBuffer();
- }
-
- // Abandon the SurfaceTexture, releasing the ref that the SurfaceTexture has
- // on buffers[2].
- mST->abandon();
-
- // Destroy the GL texture object to release its ref on buffers[2].
- GLuint texID = TEX_ID;
- glDeleteTextures(1, &texID);
-
- // Destroy the EGLSurface.
- EXPECT_TRUE(eglDestroySurface(mEglDisplay, stcEglSurface));
- ASSERT_EQ(EGL_SUCCESS, eglGetError());
-
- EXPECT_EQ(1, buffers[0]->getStrongCount());
- EXPECT_EQ(1, buffers[1]->getStrongCount());
- EXPECT_EQ(1, buffers[2]->getStrongCount());
-}
-
-TEST_F(SurfaceTextureGLTest, InvalidWidthOrHeightFails) {
- int texHeight = 16;
- ANativeWindowBuffer* anb;
-
- GLint maxTextureSize;
- glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
-
- // make sure it works with small textures
- mST->setDefaultBufferSize(16, texHeight);
- EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
- EXPECT_EQ(16, anb->width);
- EXPECT_EQ(texHeight, anb->height);
- EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
- EXPECT_EQ(NO_ERROR, mST->updateTexImage());
-
- // make sure it works with GL_MAX_TEXTURE_SIZE
- mST->setDefaultBufferSize(maxTextureSize, texHeight);
- EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
- EXPECT_EQ(maxTextureSize, anb->width);
- EXPECT_EQ(texHeight, anb->height);
- EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
- EXPECT_EQ(NO_ERROR, mST->updateTexImage());
-
- // make sure it fails with GL_MAX_TEXTURE_SIZE+1
- mST->setDefaultBufferSize(maxTextureSize+1, texHeight);
- EXPECT_EQ(NO_ERROR, mANW->dequeueBuffer(mANW.get(), &anb));
- EXPECT_EQ(maxTextureSize+1, anb->width);
- EXPECT_EQ(texHeight, anb->height);
- EXPECT_EQ(NO_ERROR, mANW->queueBuffer(mANW.get(), anb));
- ASSERT_NE(NO_ERROR, mST->updateTexImage());
-}
-
} // namespace android
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 70f1b7a..0b5262d 100644
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -208,7 +208,7 @@
glDisable(GL_DITHER);
- glBindFramebuffer(GL_FRAMEBUFFER, getTargetFbo());
+ glBindFramebuffer(GL_FRAMEBUFFER, mSnapshot->fbo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
mCaches.blend = true;
diff --git a/libs/hwui/Snapshot.h b/libs/hwui/Snapshot.h
index d51154d..aff7b93 100644
--- a/libs/hwui/Snapshot.h
+++ b/libs/hwui/Snapshot.h
@@ -213,7 +213,8 @@
Layer* layer;
/**
- * Only set when the flag kFlagIsFboLayer is set.
+ * Target FBO used for rendering. Set to 0 when rendering directly
+ * into the framebuffer.
*/
GLuint fbo;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 092ae23..927e122 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -33,6 +33,8 @@
namespace android {
+static const int64_t CAMERA_SOURCE_TIMEOUT_NS = 3000000000LL;
+
struct CameraSourceListener : public CameraListener {
CameraSourceListener(const sp<CameraSource> &source);
@@ -156,6 +158,7 @@
mLastFrameTimestampUs(0),
mStarted(false),
mNumFramesEncoded(0),
+ mTimeBetweenFrameCaptureUs(0),
mFirstFrameTimeUs(0),
mNumFramesDropped(0),
mNumGlitches(0),
@@ -644,7 +647,8 @@
releaseQueuedFrames();
while (!mFramesBeingEncoded.empty()) {
if (NO_ERROR !=
- mFrameCompleteCondition.waitRelative(mLock, 3000000000LL)) {
+ mFrameCompleteCondition.waitRelative(mLock,
+ mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
LOGW("Timed out waiting for outstanding frames being encoded: %d",
mFramesBeingEncoded.size());
}
@@ -736,7 +740,8 @@
Mutex::Autolock autoLock(mLock);
while (mStarted && mFramesReceived.empty()) {
if (NO_ERROR !=
- mFrameAvailableCondition.waitRelative(mLock, 1000000000LL)) {
+ mFrameAvailableCondition.waitRelative(mLock,
+ mTimeBetweenFrameCaptureUs * 1000LL + CAMERA_SOURCE_TIMEOUT_NS)) {
if (mCameraRecordingProxy != 0 &&
!mCameraRecordingProxy->asBinder()->isBinderAlive()) {
LOGW("camera recording proxy is gone");
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 23e6e35..4f6c8d1 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -39,12 +39,12 @@
Size videoSize,
int32_t videoFrameRate,
const sp<Surface>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs) {
+ int64_t timeBetweenFrameCaptureUs) {
CameraSourceTimeLapse *source = new
CameraSourceTimeLapse(camera, proxy, cameraId,
videoSize, videoFrameRate, surface,
- timeBetweenTimeLapseFrameCaptureUs);
+ timeBetweenFrameCaptureUs);
if (source != NULL) {
if (source->initCheck() != OK) {
@@ -62,15 +62,15 @@
Size videoSize,
int32_t videoFrameRate,
const sp<Surface>& surface,
- int64_t timeBetweenTimeLapseFrameCaptureUs)
+ int64_t timeBetweenFrameCaptureUs)
: CameraSource(camera, proxy, cameraId, videoSize, videoFrameRate, surface, true),
- mTimeBetweenTimeLapseFrameCaptureUs(timeBetweenTimeLapseFrameCaptureUs),
mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate),
mLastTimeLapseFrameRealTimestampUs(0),
mSkipCurrentFrame(false) {
+ mTimeBetweenFrameCaptureUs = timeBetweenFrameCaptureUs;
LOGD("starting time lapse mode: %lld us",
- mTimeBetweenTimeLapseFrameCaptureUs);
+ mTimeBetweenFrameCaptureUs);
mVideoWidth = videoSize.width;
mVideoHeight = videoSize.height;
@@ -271,14 +271,14 @@
// The first 2 output frames from the encoder are: decoder specific info and
// the compressed video frame data for the first input video frame.
if (mNumFramesEncoded >= 1 && *timestampUs <
- (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenTimeLapseFrameCaptureUs)) {
+ (mLastTimeLapseFrameRealTimestampUs + mTimeBetweenFrameCaptureUs)) {
// Skip all frames from last encoded frame until
- // sufficient time (mTimeBetweenTimeLapseFrameCaptureUs) has passed.
+ // sufficient time (mTimeBetweenFrameCaptureUs) has passed.
// Tell the camera to release its recording frame and return.
ALOGV("dataCallbackTimestamp timelapse: skipping intermediate frame");
return true;
} else {
- // Desired frame has arrived after mTimeBetweenTimeLapseFrameCaptureUs time:
+ // Desired frame has arrived after mTimeBetweenFrameCaptureUs time:
// - Reset mLastTimeLapseFrameRealTimestampUs to current time.
// - Artificially modify timestampUs to be one frame time (1/framerate) ahead
// of the last encoded frame's time stamp.
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 97d011e..f9f92d2 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -520,6 +520,85 @@
return NULL;
}
+status_t OMXCodec::parseAVCCodecSpecificData(
+ const void *data, size_t size,
+ unsigned *profile, unsigned *level) {
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ // verify minimum size and configurationVersion == 1.
+ if (size < 7 || ptr[0] != 1) {
+ return ERROR_MALFORMED;
+ }
+
+ *profile = ptr[1];
+ *level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ if (size < 1) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ if (size < 2) {
+ return ERROR_MALFORMED;
+ }
+
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ if (size < length) {
+ return ERROR_MALFORMED;
+ }
+
+ addCodecSpecificData(ptr, length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ return OK;
+}
+
status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
ALOGV("configureCodec protected=%d",
(mFlags & kEnableGrallocUsageProtected) ? 1 : 0);
@@ -542,66 +621,17 @@
} else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
- const uint8_t *ptr = (const uint8_t *)data;
-
- CHECK(size >= 7);
- CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
- uint8_t profile = ptr[1];
- uint8_t level = ptr[3];
-
- // There is decodable content out there that fails the following
- // assertion, let's be lenient for now...
- // CHECK((ptr[4] >> 2) == 0x3f); // reserved
-
- size_t lengthSize = 1 + (ptr[4] & 3);
-
- // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
- // violates it...
- // CHECK((ptr[5] >> 5) == 7); // reserved
-
- size_t numSeqParameterSets = ptr[5] & 31;
-
- ptr += 6;
- size -= 6;
-
- for (size_t i = 0; i < numSeqParameterSets; ++i) {
- CHECK(size >= 2);
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- CHECK(size >= length);
-
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
- }
-
- CHECK(size >= 1);
- size_t numPictureParameterSets = *ptr;
- ++ptr;
- --size;
-
- for (size_t i = 0; i < numPictureParameterSets; ++i) {
- CHECK(size >= 2);
- size_t length = U16_AT(ptr);
-
- ptr += 2;
- size -= 2;
-
- CHECK(size >= length);
-
- addCodecSpecificData(ptr, length);
-
- ptr += length;
- size -= length;
+ unsigned profile, level;
+ status_t err;
+ if ((err = parseAVCCodecSpecificData(
+ data, size, &profile, &level)) != OK) {
+ LOGE("Malformed AVC codec specific data.");
+ return err;
}
CODEC_LOGI(
- "AVC profile = %d (%s), level = %d",
- (int)profile, AVCProfileToString(profile), level);
+ "AVC profile = %u (%s), level = %u",
+ profile, AVCProfileToString(profile), level);
if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
&& (profile != kAVCProfileBaseline || level > 30)) {
diff --git a/opengl/java/android/opengl/ManagedEGLContext.java b/opengl/java/android/opengl/ManagedEGLContext.java
index d3a3662..61fa565 100644
--- a/opengl/java/android/opengl/ManagedEGLContext.java
+++ b/opengl/java/android/opengl/ManagedEGLContext.java
@@ -43,12 +43,13 @@
* of the currently created EGL contexts in the process are being managed
* through this class, then they will all be asked to terminate through the
* call to {@link #onTerminate}.
+ *
+ * @hide
*/
public abstract class ManagedEGLContext {
static final String TAG = "ManagedEGLContext";
- static final ArrayList<ManagedEGLContext> sActive
- = new ArrayList<ManagedEGLContext>();
+ static final ArrayList<ManagedEGLContext> sActive = new ArrayList<ManagedEGLContext>();
final EGLContext mContext;
@@ -127,7 +128,7 @@
sActive.clear();
}
- for (int i=0; i<active.size(); i++) {
+ for (int i = 0; i < active.size(); i++) {
active.get(i).execTerminate();
}
diff --git a/opengl/libs/Android.mk b/opengl/libs/Android.mk
index 5855b63..9c1a10e 100644
--- a/opengl/libs/Android.mk
+++ b/opengl/libs/Android.mk
@@ -44,10 +44,17 @@
LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER
endif
+ifneq ($(MAX_EGL_CACHE_ENTRY_SIZE),)
+ LOCAL_CFLAGS += -DMAX_EGL_CACHE_ENTRY_SIZE=$(MAX_EGL_CACHE_ENTRY_SIZE)
+endif
+
+ifneq ($(MAX_EGL_CACHE_SIZE),)
+ LOCAL_CFLAGS += -DMAX_EGL_CACHE_SIZE=$(MAX_EGL_CACHE_SIZE)
+endif
+
include $(BUILD_SHARED_LIBRARY)
installed_libEGL := $(LOCAL_INSTALLED_MODULE)
-
# OpenGL drivers config file
ifneq ($(BOARD_EGL_CFG),)
diff --git a/opengl/libs/EGL/eglApi.cpp b/opengl/libs/EGL/eglApi.cpp
index 2237eb6..a63d5b0 100644
--- a/opengl/libs/EGL/eglApi.cpp
+++ b/opengl/libs/EGL/eglApi.cpp
@@ -370,6 +370,11 @@
}
}
+ // the EGL spec requires that a new EGLSurface default to swap interval
+ // 1, so explicitly set that on the window here.
+ ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
+ anw->setSwapInterval(anw, 1);
+
EGLSurface surface = cnx->egl.eglCreateWindowSurface(
iDpy, iConfig, window, attrib_list);
if (surface != EGL_NO_SURFACE) {
diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp
index fe32d43..c4a7466 100644
--- a/opengl/libs/EGL/egl_cache.cpp
+++ b/opengl/libs/EGL/egl_cache.cpp
@@ -25,10 +25,18 @@
#include <sys/types.h>
#include <unistd.h>
+#ifndef MAX_EGL_CACHE_ENTRY_SIZE
+#define MAX_EGL_CACHE_ENTRY_SIZE (16 * 1024);
+#endif
+
+#ifndef MAX_EGL_CACHE_SIZE
+#define MAX_EGL_CACHE_SIZE (64 * 1024);
+#endif
+
// Cache size limits.
static const size_t maxKeySize = 1024;
-static const size_t maxValueSize = 4096;
-static const size_t maxTotalSize = 64 * 1024;
+static const size_t maxValueSize = MAX_EGL_CACHE_ENTRY_SIZE;
+static const size_t maxTotalSize = MAX_EGL_CACHE_SIZE;
// Cache file header
static const char* cacheFileMagic = "EGL$";
diff --git a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java
index 20a1c50..1db2a7f 100644
--- a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java
+++ b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java
@@ -30,12 +30,17 @@
com.android.internal.R.string.config_defaultDreamComponent);
}
if (component != null) {
+ // dismiss the notification shade, recents, etc.
+ context.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
ComponentName cn = ComponentName.unflattenFromString(component);
Intent zzz = new Intent(Intent.ACTION_MAIN)
.setComponent(cn)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+ | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
+ | Intent.FLAG_ACTIVITY_NO_USER_ACTION
+ | Intent.FLAG_FROM_BACKGROUND
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
);
Slog.v(TAG, "Starting screen saver on dock event: " + component);
context.startActivity(zzz);
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
index 2837366..72856b8 100755
--- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
+++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java
@@ -3478,12 +3478,17 @@
component = mContext.getResources().getString(R.string.config_defaultDreamComponent);
}
if (component != null) {
+ // dismiss the notification shade, recents, etc.
+ mContext.sendBroadcast(new Intent(Intent.ACTION_CLOSE_SYSTEM_DIALOGS));
+
ComponentName cn = ComponentName.unflattenFromString(component);
Intent intent = new Intent(Intent.ACTION_MAIN)
.setComponent(cn)
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
| Intent.FLAG_ACTIVITY_NO_USER_ACTION
+ | Intent.FLAG_FROM_BACKGROUND
+ | Intent.FLAG_ACTIVITY_NO_HISTORY
);
mContext.startActivity(intent);
} else {
diff --git a/voip/java/com/android/server/sip/SipHelper.java b/voip/java/com/android/server/sip/SipHelper.java
index dc628e0..113f007 100644
--- a/voip/java/com/android/server/sip/SipHelper.java
+++ b/voip/java/com/android/server/sip/SipHelper.java
@@ -73,7 +73,7 @@
*/
class SipHelper {
private static final String TAG = SipHelper.class.getSimpleName();
- private static final boolean DEBUG = true;
+ private static final boolean DEBUG = false;
private static final boolean DEBUG_PING = false;
private SipStack mSipStack;
diff --git a/voip/java/com/android/server/sip/SipService.java b/voip/java/com/android/server/sip/SipService.java
index 119ed54..38a683e 100644
--- a/voip/java/com/android/server/sip/SipService.java
+++ b/voip/java/com/android/server/sip/SipService.java
@@ -68,8 +68,7 @@
*/
public final class SipService extends ISipService.Stub {
static final String TAG = "SipService";
- static final boolean DEBUGV = false;
- static final boolean DEBUG = true;
+ static final boolean DEBUG = false;
private static final int EXPIRY_TIME = 3600;
private static final int SHORT_EXPIRY_TIME = 10;
private static final int MIN_EXPIRY_TIME = 60;
@@ -581,7 +580,7 @@
@Override
public void onRinging(ISipSession s, SipProfile caller,
String sessionDescription) {
- if (DEBUGV) Log.d(TAG, "<<<<< onRinging()");
+ if (DEBUG) Log.d(TAG, "<<<<< onRinging()");
SipSessionGroup.SipSessionImpl session =
(SipSessionGroup.SipSessionImpl) s;
synchronized (SipService.this) {
@@ -778,7 +777,6 @@
private void restartLater() {
synchronized (SipService.this) {
int interval = NAT_MEASUREMENT_RETRY_INTERVAL;
- Log.d(TAG, "Retry measurement " + interval + "s later.");
mTimer.cancel(this);
mTimer.set(interval * 1000, this);
}
@@ -788,7 +786,7 @@
private class AutoRegistrationProcess extends SipSessionAdapter
implements Runnable, SipSessionGroup.KeepAliveProcessCallback {
private static final int MIN_KEEPALIVE_SUCCESS_COUNT = 10;
- private String TAG = "SipAudoReg";
+ private String TAG = "SipAutoReg";
private SipSessionGroup.SipSessionImpl mSession;
private SipSessionGroup.SipSessionImpl mKeepAliveSession;
@@ -820,13 +818,12 @@
// in registration to avoid adding duplicate entries to server
mMyWakeLock.acquire(mSession);
mSession.unregister();
- if (DEBUG) TAG = mSession.getLocalProfile().getUriString();
- if (DEBUG) Log.d(TAG, "start AutoRegistrationProcess");
+ TAG = "SipAutoReg:" + mSession.getLocalProfile().getUriString();
}
}
private void startKeepAliveProcess(int interval) {
- Log.d(TAG, "start keepalive w interval=" + interval);
+ if (DEBUG) Log.d(TAG, "start keepalive w interval=" + interval);
if (mKeepAliveSession == null) {
mKeepAliveSession = mSession.duplicate();
} else {
@@ -864,9 +861,11 @@
mKeepAliveSuccessCount = 0;
}
} else {
- Log.i(TAG, "keep keepalive going with interval "
- + interval + ", past success count="
- + mKeepAliveSuccessCount);
+ if (DEBUG) {
+ Log.i(TAG, "keep keepalive going with interval "
+ + interval + ", past success count="
+ + mKeepAliveSuccessCount);
+ }
mKeepAliveSuccessCount /= 2;
}
} else {
@@ -894,7 +893,9 @@
// SipSessionGroup.KeepAliveProcessCallback
@Override
public void onError(int errorCode, String description) {
- Log.e(TAG, "keepalive error: " + description);
+ if (DEBUG) {
+ Log.e(TAG, "keepalive error: " + description);
+ }
onResponse(true); // re-register immediately
}
@@ -917,7 +918,7 @@
public void onKeepAliveIntervalChanged() {
if (mKeepAliveSession != null) {
int newInterval = getKeepAliveInterval();
- if (DEBUGV) {
+ if (DEBUG) {
Log.v(TAG, "restart keepalive w interval=" + newInterval);
}
mKeepAliveSuccessCount = 0;
@@ -987,7 +988,7 @@
}
private void restart(int duration) {
- if (DEBUG) Log.d(TAG, "Refresh registration " + duration + "s later.");
+ Log.d(TAG, "Refresh registration " + duration + "s later.");
mTimer.cancel(this);
mTimer.set(duration * 1000, this);
}
diff --git a/voip/java/com/android/server/sip/SipSessionGroup.java b/voip/java/com/android/server/sip/SipSessionGroup.java
index 06cdaf2..877a0a4 100644
--- a/voip/java/com/android/server/sip/SipSessionGroup.java
+++ b/voip/java/com/android/server/sip/SipSessionGroup.java
@@ -89,8 +89,8 @@
*/
class SipSessionGroup implements SipListener {
private static final String TAG = "SipSession";
- private static final boolean DEBUG = true;
- private static final boolean DEBUG_PING = DEBUG && false;
+ private static final boolean DEBUG = false;
+ private static final boolean DEBUG_PING = false;
private static final String ANONYMOUS = "anonymous";
// Limit the size of thread pool to 1 for the order issue when the phone is
// waken up from sleep and there are many packets to be processed in the SIP
@@ -205,7 +205,9 @@
}
synchronized void resetExternalAddress() {
- Log.d(TAG, " reset external addr on " + mSipStack);
+ if (DEBUG) {
+ Log.d(TAG, " reset external addr on " + mSipStack);
+ }
mExternalIp = null;
mExternalPort = 0;
}
@@ -362,7 +364,7 @@
+ SipSession.State.toString(session.mState));
}
} catch (Throwable e) {
- Log.w(TAG, "event process error: " + event, e);
+ Log.w(TAG, "event process error: " + event, getRootCause(e));
session.onError(e);
}
}
@@ -393,11 +395,22 @@
if ((rport > 0) && (externalIp != null)) {
mExternalIp = externalIp;
mExternalPort = rport;
- Log.d(TAG, " got external addr " + externalIp + ":" + rport
- + " on " + mSipStack);
+ if (DEBUG) {
+ Log.d(TAG, " got external addr " + externalIp + ":" + rport
+ + " on " + mSipStack);
+ }
}
}
+ private Throwable getRootCause(Throwable exception) {
+ Throwable cause = exception.getCause();
+ while (cause != null) {
+ exception = cause;
+ cause = exception.getCause();
+ }
+ return exception;
+ }
+
private SipSessionImpl createNewSession(RequestEvent event,
ISipSessionListener listener, ServerTransaction transaction,
int newState) throws SipException {
@@ -890,7 +903,9 @@
if (expires != null && time < expires.getExpires()) {
time = expires.getExpires();
}
- Log.v(TAG, "Expiry time = " + time);
+ if (DEBUG) {
+ Log.v(TAG, "Expiry time = " + time);
+ }
return time;
}
@@ -1409,15 +1424,6 @@
}
}
- private Throwable getRootCause(Throwable exception) {
- Throwable cause = exception.getCause();
- while (cause != null) {
- exception = cause;
- cause = exception.getCause();
- }
- return exception;
- }
-
private int getErrorCode(Throwable exception) {
String message = exception.getMessage();
if (exception instanceof UnknownHostException) {
@@ -1555,8 +1561,10 @@
try {
sendKeepAlive();
} catch (Throwable t) {
- Log.w(TAG, "keepalive error: "
- + mLocalProfile.getUriString(), getRootCause(t));
+ if (DEBUG) {
+ Log.w(TAG, "keepalive error: "
+ + mLocalProfile.getUriString(), getRootCause(t));
+ }
// It's possible that the keepalive process is being stopped
// during session.sendKeepAlive() so need to check mRunning
// again here.
diff --git a/voip/java/com/android/server/sip/SipWakeLock.java b/voip/java/com/android/server/sip/SipWakeLock.java
index 52bc094..0c4d14c 100644
--- a/voip/java/com/android/server/sip/SipWakeLock.java
+++ b/voip/java/com/android/server/sip/SipWakeLock.java
@@ -22,8 +22,8 @@
import java.util.HashSet;
class SipWakeLock {
- private static final boolean DEBUGV = SipService.DEBUGV;
- private static final String TAG = SipService.TAG;
+ private static final boolean DEBUG = false;
+ private static final String TAG = "SipWakeLock";
private PowerManager mPowerManager;
private PowerManager.WakeLock mWakeLock;
private PowerManager.WakeLock mTimerWakeLock;
@@ -34,9 +34,9 @@
}
synchronized void reset() {
+ if (DEBUG) Log.v(TAG, "reset count=" + mHolders.size());
mHolders.clear();
release(null);
- if (DEBUGV) Log.v(TAG, "~~~ hard reset wakelock");
}
synchronized void acquire(long timeout) {
@@ -55,8 +55,7 @@
PowerManager.PARTIAL_WAKE_LOCK, "SipWakeLock");
}
if (!mWakeLock.isHeld()) mWakeLock.acquire();
- if (DEBUGV) Log.v(TAG, "acquire wakelock: holder count="
- + mHolders.size());
+ if (DEBUG) Log.v(TAG, "acquire count=" + mHolders.size());
}
synchronized void release(Object holder) {
@@ -65,7 +64,6 @@
&& mWakeLock.isHeld()) {
mWakeLock.release();
}
- if (DEBUGV) Log.v(TAG, "release wakelock: holder count="
- + mHolders.size());
+ if (DEBUG) Log.v(TAG, "release count=" + mHolders.size());
}
}