diff options
53 files changed, 1477 insertions, 959 deletions
diff --git a/api/current.txt b/api/current.txt index 62291f75decf..4cf23ebf922f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14244,13 +14244,6 @@ package android.opengl { 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 @@ package android.renderscript { 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 4e3801141467..7ca615504934 100644 --- a/core/java/android/hardware/Camera.java +++ b/core/java/android/hardware/Camera.java @@ -22,12 +22,9 @@ import android.graphics.ImageFormat; 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 @@ public class Camera { 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 class Camera { 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 @@ public class Camera { 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 000000000000..32de0cde5918 --- /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 fc6a44a3799b..397a12a35b22 100644 --- a/core/java/android/net/DhcpStateMachine.java +++ b/core/java/android/net/DhcpStateMachine.java @@ -347,21 +347,25 @@ public class DhcpStateMachine extends StateMachine { if (success) { if (DBG) Log.d(TAG, "DHCP succeeded on " + mInterfaceName); - 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); + long leaseDuration = dhcpInfoInternal.leaseDuration; //int to long conversion + + //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 273ee916e7f5..02096f2a716e 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -357,8 +357,11 @@ public final class NfcAdapter { 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 6ec2e219d5c4..2bbed57482a1 100644 --- a/core/java/android/nfc/NfcManager.java +++ b/core/java/android/nfc/NfcManager.java @@ -40,6 +40,10 @@ public final class NfcManager { 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 33625755408c..e1bc275bc6f2 100644 --- a/core/java/android/os/Process.java +++ b/core/java/android/os/Process.java @@ -101,7 +101,7 @@ public class Process { * 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 28251a6245c9..5282e610c30e 100644 --- a/core/java/android/service/textservice/SpellCheckerService.java +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -138,6 +138,25 @@ public abstract class SpellCheckerService extends Service { } /** + * @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 @@ public abstract class SpellCheckerService extends Service { } @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 8e39d6edf345..f77cf7e8398b 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -162,13 +162,21 @@ public abstract class HardwareRenderer { 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 @@ public abstract class HardwareRenderer { } /** - * 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 @@ public abstract class HardwareRenderer { 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 @@ public abstract class HardwareRenderer { } } - 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 @@ public abstract class HardwareRenderer { 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 @@ public abstract class HardwareRenderer { 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 @@ public abstract class HardwareRenderer { } } - 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 @@ public abstract class HardwareRenderer { } } } + + @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 @@ public abstract class HardwareRenderer { 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 169738257b3b..74916f051635 100644 --- a/core/java/android/view/TextureView.java +++ b/core/java/android/view/TextureView.java @@ -203,7 +203,10 @@ public class TextureView extends View { @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); + destroySurface(); + } + private void destroySurface() { if (mLayer != null) { boolean shouldRelease = true; if (mListener != null) { @@ -300,6 +303,17 @@ public class TextureView extends View { 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 65b1bd04910d..74c1ce8e4e60 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -10129,7 +10129,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal 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 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal 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 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal 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 723846afbc5b..18d5c40fc804 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -577,6 +577,13 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } + 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 660e3f44f065..dfd1d5538030 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -425,7 +425,7 @@ public class WindowManagerImpl implements WindowManager { 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 489587ed36d1..1d66cbe81c32 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -88,14 +88,17 @@ public class SpellCheckerSession { * 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 @@ public class SpellCheckerSession { 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 @@ public class SpellCheckerSession { * @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 @@ public class SpellCheckerSession { mTextServicesManager = tsm; mIsUsed = true; mSpellCheckerSessionListener = listener; + mSubtype = subtype; } /** @@ -167,6 +175,14 @@ public class SpellCheckerSession { } /** + * @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 @@ public class SpellCheckerSession { 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 @@ public class SpellCheckerSession { case TASK_CLOSE: processClose(); break; + case TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE: + processGetSuggestionsMultipleForSentence(scp); + break; } } @@ -266,6 +290,15 @@ public class SpellCheckerSession { 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 @@ public class SpellCheckerSession { } } + 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 @@ public class SpellCheckerSession { * @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 class SpellCheckerSession { 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 aeb3ba62a780..1bbaf6c53881 100644 --- a/core/java/android/view/textservice/SpellCheckerSubtype.java +++ b/core/java/android/view/textservice/SpellCheckerSubtype.java @@ -21,9 +21,11 @@ import android.content.pm.ApplicationInfo; 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 @@ import java.util.Locale; * 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 @@ public final class SpellCheckerSubtype implements Parcelable { 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 ddd0361ca97f..9b99770246d7 100644 --- a/core/java/android/view/textservice/SuggestionsInfo.java +++ b/core/java/android/view/textservice/SuggestionsInfo.java @@ -21,11 +21,14 @@ import com.android.internal.util.ArrayUtils; 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 final class SuggestionsInfo implements Parcelable { 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 @@ public final class SuggestionsInfo implements Parcelable { * @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 final class SuggestionsInfo implements Parcelable { */ 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 @@ public final class SuggestionsInfo implements Parcelable { 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 @@ public final class SuggestionsInfo implements Parcelable { dest.writeInt(mCookie); dest.writeInt(mSequence); dest.writeInt(mSuggestionsAvailable ? 1 : 0); + dest.writeIntArray(mStartPosArray); + dest.writeIntArray(mLengthArray); } /** @@ -191,4 +227,24 @@ public final class SuggestionsInfo implements Parcelable { 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 69f88a5e1532..fc59e6ec5549 100644 --- a/core/java/android/view/textservice/TextServicesManager.java +++ b/core/java/android/view/textservice/TextServicesManager.java @@ -157,7 +157,8 @@ public final class TextServicesManager { 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 f29aff29bf05..cb555ea55d09 100644 --- a/core/java/android/webkit/HTML5VideoFullScreen.java +++ b/core/java/android/webkit/HTML5VideoFullScreen.java @@ -261,6 +261,8 @@ public class HTML5VideoFullScreen extends HTML5VideoView 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 3ec7e2f96d92..7bbad78d1e7e 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.speech.tts.TextToSpeech; 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.ViewConfiguration; 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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout "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 @@ public class WebView extends AbsoluteLayout 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 @@ public class WebView extends AbsoluteLayout // 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 @@ public class WebView extends AbsoluteLayout 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 278bd6ee9879..42b9eab99996 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -2330,7 +2330,8 @@ public final class WebViewCore { / 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 2184297195f6..d03db10a8998 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -249,6 +249,12 @@ public class SpellChecker implements SpellCheckerSessionListener { } @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 3e96c815e9b6..fec4cbc9d402 100644 --- a/core/java/com/android/internal/os/BatteryStatsImpl.java +++ b/core/java/com/android/internal/os/BatteryStatsImpl.java @@ -1275,7 +1275,7 @@ public final class BatteryStatsImpl extends BatteryStats { // 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 3c61968664f3..ba0aa1a44fb3 100644 --- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl +++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl @@ -24,6 +24,7 @@ import android.view.textservice.TextInfo; 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 796b06eb06d6..b44dbc896b00 100644 --- a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl +++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl @@ -23,4 +23,5 @@ import android.view.textservice.SuggestionsInfo; */ 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 b7bc36644cb6..25b0065fd7fe 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.text.TextUtils.TruncateAt; 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 @@ public class ScrollingTabContainerView extends HorizontalScrollView 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 e02070de1fde..d78081e62650 100644 --- a/core/jni/android/graphics/HarfbuzzSkia.cpp +++ b/core/jni/android/graphics/HarfbuzzSkia.cpp @@ -47,25 +47,14 @@ extern "C" { 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 HB_Bool stringToGlyphs(HB_Font hbFont, const HB_UChar16* characters, hb_u 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 @@ static void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 n 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 void glyphsToAdvances(HB_Font hbFont, const HB_Glyph* glyphs, hb_uint32 n 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_Bool canRender(HB_Font hbFont, const HB_UChar16* characters, hb_uint32 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 HB_Error getOutlinePoint(HB_Font hbFont, HB_Glyph glyph, int flags, hb_ui 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 void getGlyphMetrics(HB_Font hbFont, HB_Glyph glyph, HB_GlyphMetrics* met 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 @@ const HB_FontClass harfbuzzSkiaClass = { 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 99b389a7ad4a..2772f4d0661d 100644 --- a/core/jni/android/graphics/HarfbuzzSkia.h +++ b/core/jni/android/graphics/HarfbuzzSkia.h @@ -47,15 +47,6 @@ static inline HB_Fixed SkScalarToHBFixed(SkScalar value) { 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 2ab7da9ca3db..883940b68bed 100644 --- a/core/jni/android/graphics/Paint.cpp +++ b/core/jni/android/graphics/Paint.cpp @@ -484,8 +484,8 @@ public: 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 5fed2f349f2e..17492aa43d62 100644 --- a/core/jni/android/graphics/TextLayoutCache.cpp +++ b/core/jni/android/graphics/TextLayoutCache.cpp @@ -34,15 +34,10 @@ namespace android { #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 @@ sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint, 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 @@ sp<TextLayoutCacheValue> TextLayoutCache::getValue(SkPaint* paint, 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 @@ size_t TextLayoutCacheKey::getSize() const { /** * 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 @@ uint32_t TextLayoutCacheValue::getElapsedTime() { return mElapsedTime; } -void TextLayoutCacheValue::computeValues(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); -#if DEBUG_ADVANCES - LOGD("Advances - start=%d, count=%d, contextCount=%d, totalAdvance=%f", start, count, - contextCount, mTotalAdvance); -#endif -} +//HB_ShaperItem TextLayoutEngine::mShaperItem; +//HB_FontRec TextLayoutEngine::mFontRec; +//SkPaint TextLayoutEngine::mShapingPaint; -size_t TextLayoutCacheValue::getSize() const { - return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() + - sizeof(jchar) * mGlyphs.capacity(); -} +TextLayoutEngine::TextLayoutEngine() : mShaperItemGlyphArraySize(0), + mShaperItemLogClustersArraySize(0) { + mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal); + mArabicTypeface = NULL; + mHebrewRegularTypeface = NULL; + mHebrewBoldTypeface = NULL; -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; + 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. - font->x_ppem = 1; - font->y_ppem = 1; - font->x_scale = 1; - font->y_scale = 1; - - // Reset kerning - shaperItem.kerning_applied = false; + mFontRec.x_ppem = 1; + mFontRec.y_ppem = 1; + mFontRec.x_scale = 1; + mFontRec.y_scale = 1; - // Define font data - fontData->textSize = paint->getTextSize(); - fontData->textSkewX = paint->getTextSkewX(); - fontData->textScaleX = paint->getTextScaleX(); - fontData->flags = paint->getFlags(); - fontData->hinting = paint->getHinting(); + memset(&mShaperItem, 0, sizeof(mShaperItem)); - 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; + mShaperItem.font = &mFontRec; + mShaperItem.font->userData = &mShapingPaint; } -void TextLayoutCacheValue::freeShaperItem(HB_ShaperItem& shaperItem) { - deleteGlyphArrays(shaperItem); - delete[] shaperItem.log_clusters; - HB_FreeFace(shaperItem.face); +TextLayoutEngine::~TextLayoutEngine() { + // FIXME should free fonts and caches but since this class is a singleton, + // we don't bother at the moment } -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; -} +void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars, + size_t start, size_t count, size_t contextCount, int dirFlags) { -SkTypeface* TextLayoutCacheValue::getCachedTypeface(SkTypeface** typeface, const char path[]) { - if (!*typeface) { - *typeface = SkTypeface::CreateFromFile(path); -#if DEBUG_GLYPHS - LOGD("Created SkTypeface from file: %s", path); + computeValuesWithHarfbuzz(paint, chars, start, count, contextCount, dirFlags, + &value->mAdvances, &value->mTotalAdvance, &value->mGlyphs); +#if DEBUG_ADVANCES + LOGD("Advances - start=%d, count=%d, contextCount=%d, totalAdvance=%f", start, count, + contextCount, mTotalAdvance); #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 @@ void TextLayoutCacheValue::computeValuesWithHarfbuzz(SkPaint* paint, const UChar 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 @@ static void logGlyphs(HB_ShaperItem shaperItem) { } } -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; - - ssize_t startFontRun = 0; - ssize_t endFontRun = 0; + // Set the string properties + mShaperItem.string = chars; + mShaperItem.stringLength = count; + + // 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 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UC 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 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UC #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 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UC } // 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 @@ void TextLayoutCacheValue::computeRunValuesWithHarfbuzz(SkPaint* paint, const UC // 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 TextLayoutEngine::ensureShaperItemGlyphArrays(size_t size) { + if (size > mShaperItemGlyphArraySize) { + deleteShaperItemGlyphArrays(); + createShaperItemGlyphArrays(size); + } } -void TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem& shaperItem, int 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 5c938f7582a8..dfdcd03a9ced 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 @@ inline int compare_type(const TextLayoutCacheKey& lhs, const TextLayoutCacheKey& */ 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 @@ public: 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 @@ private: 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 @@ private: }; // 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 25f7d2565c92..326f1866681c 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 27d863de58ae..15c2bab5946a 100644 --- a/include/gui/SurfaceTexture.h +++ b/include/gui/SurfaceTexture.h @@ -202,6 +202,10 @@ public: // 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 8c1c59351e90..446720b88cd5 100644 --- a/include/media/stagefright/CameraSource.h +++ b/include/media/stagefright/CameraSource.h @@ -153,6 +153,9 @@ protected: 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 0e264c7be02a..b0606914c11d 100644 --- a/include/media/stagefright/CameraSourceTimeLapse.h +++ b/include/media/stagefright/CameraSourceTimeLapse.h @@ -57,10 +57,6 @@ private: 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 c21d19d45851..84f8282f64b7 100644 --- a/include/media/stagefright/OMXCodec.h +++ b/include/media/stagefright/OMXCodec.h @@ -336,6 +336,10 @@ private: 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 2d716c7ed3ff..b7e3ee32c62f 100644 --- a/libs/gui/Android.mk +++ b/libs/gui/Android.mk @@ -32,6 +32,10 @@ LOCAL_SHARED_LIBRARIES := \ 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 175de69038cc..b4d01a961f71 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 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, 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 @@ status_t SurfaceTexture::disconnect(int api) { 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 @@ uint32_t SurfaceTexture::getCurrentScalingMode() const { 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 4268782220dc..c79e69a9b8a1 100644 --- a/libs/gui/tests/SurfaceTexture_test.cpp +++ b/libs/gui/tests/SurfaceTexture_test.cpp @@ -396,7 +396,8 @@ protected: 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 @@ protected: // 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 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { 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 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BufferWithCrop) { 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 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledYV12BuffersRepeatedly) { 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,21 +948,173 @@ TEST_F(SurfaceTextureGLTest, TexturingFromCpuFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 35, 231, 35, 35)); } -TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { +TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { + class ProducerThread : public Thread { + public: + ProducerThread(const sp<ANativeWindow>& anw): + mANW(anw), + mDequeueError(NO_ERROR) { + } + + virtual ~ProducerThread() { + } + + virtual bool threadLoop() { + Mutex::Autolock lock(mMutex); + ANativeWindowBuffer* anb; + + // Frame 1 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 2 + if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { + return false; + } + if (anb == NULL) { + return false; + } + if (mANW->queueBuffer(mANW.get(), anb) + != NO_ERROR) { + return false; + } + + // Frame 3 - error expected + mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); + return false; + } + + status_t getDequeueError() { + Mutex::Autolock lock(mMutex); + return mDequeueError; + } + + private: + sp<ANativeWindow> mANW; + status_t mDequeueError; + Mutex mMutex; + }; + + sp<FrameWaiter> fw(new FrameWaiter); + mST->setFrameAvailableListener(fw); + ASSERT_EQ(OK, mST->setSynchronousMode(true)); + ASSERT_EQ(OK, mST->setBufferCountServer(2)); + + sp<Thread> pt(new ProducerThread(mANW)); + pt->run(); + + fw->waitForFrame(); + fw->waitForFrame(); + + // Sleep for 100ms to allow the producer thread's dequeueBuffer call to + // block waiting for a buffer to become available. + usleep(100000); + + mST->abandon(); + + pt->requestExitAndWait(); + ASSERT_EQ(NO_INIT, + 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 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 - EGLSurface stcEglSurface = eglCreateWindowSurface(mEglDisplay, mGlConfig, - mANW.get(), NULL); + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mProducerEglSurface, + mProducerEglSurface, mProducerEglContext)); ASSERT_EQ(EGL_SUCCESS, eglGetError()); - ASSERT_NE(EGL_NO_SURFACE, stcEglSurface); - EXPECT_TRUE(eglMakeCurrent(mEglDisplay, stcEglSurface, stcEglSurface, - mEglContext)); - 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); @@ -972,7 +1132,7 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { glClearColor(0.0, 0.0, 1.0, 1.0); glClear(GL_COLOR_BUFFER_BIT); - eglSwapBuffers(mEglDisplay, stcEglSurface); + eglSwapBuffers(mEglDisplay, mProducerEglSurface); // Do the consumer side of things EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, @@ -981,12 +1141,9 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { glDisable(GL_SCISSOR_TEST); + mST->updateTexImage(); // Skip the first frame, which was empty 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); @@ -1016,90 +1173,136 @@ TEST_F(SurfaceTextureGLTest, TexturingFromGLFilledRGBABufferPow2) { EXPECT_TRUE(checkPixel( 3, 52, 153, 153, 153, 153)); } -TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { - class ProducerThread : public Thread { - public: - ProducerThread(const sp<ANativeWindow>& anw): - mANW(anw), - mDequeueError(NO_ERROR) { - } +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; - virtual ~ProducerThread() { - } + // 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()); - virtual bool threadLoop() { - Mutex::Autolock lock(mMutex); - ANativeWindowBuffer* anb; + 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); - // Frame 1 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb) - != NO_ERROR) { - return false; - } + // Consume a frame + EXPECT_TRUE(eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, + mEglContext)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); + mST->updateTexImage(); + buffers[i] = mST->getCurrentBuffer(); + } - // Frame 2 - if (mANW->dequeueBuffer(mANW.get(), &anb) != NO_ERROR) { - return false; - } - if (anb == NULL) { - return false; - } - if (mANW->queueBuffer(mANW.get(), anb) - != NO_ERROR) { - return false; - } + // Destroy the GL texture object to release its ref on buffers[2]. + GLuint texID = TEX_ID; + glDeleteTextures(1, &texID); - // Frame 3 - error expected - mDequeueError = mANW->dequeueBuffer(mANW.get(), &anb); - return false; - } + // Destroy the EGLSurface + EXPECT_TRUE(eglDestroySurface(mEglDisplay, mProducerEglSurface)); + ASSERT_EQ(EGL_SUCCESS, eglGetError()); - status_t getDequeueError() { - Mutex::Autolock lock(mMutex); - return mDequeueError; - } + // Release the ref that the SurfaceTexture has on buffers[2]. + mST->abandon(); - private: - sp<ANativeWindow> mANW; - status_t mDequeueError; - Mutex mMutex; - }; + EXPECT_EQ(1, buffers[0]->getStrongCount()); + EXPECT_EQ(1, buffers[1]->getStrongCount()); - sp<FrameWaiter> fw(new FrameWaiter); - mST->setFrameAvailableListener(fw); - ASSERT_EQ(OK, mST->setSynchronousMode(true)); - ASSERT_EQ(OK, mST->setBufferCountServer(2)); + // 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()); + } +} - sp<Thread> pt(new ProducerThread(mANW)); - pt->run(); +TEST_F(SurfaceTextureGLToGLTest, EglDestroySurfaceAfterAbandonUnrefsBuffers) { + sp<GraphicBuffer> buffers[3]; - fw->waitForFrame(); - fw->waitForFrame(); + // 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()); - // Sleep for 100ms to allow the producer thread's dequeueBuffer call to - // block waiting for a buffer to become available. - usleep(100000); + 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(); - pt->requestExitAndWait(); - ASSERT_EQ(NO_INIT, - reinterpret_cast<ProducerThread*>(pt.get())->getDequeueError()); + // 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 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 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 @@ TEST_F(SurfaceTextureGLTest, AbandonUnblocksDequeueBuffer) { * } * */ -class SurfaceTextureGLToGLTest : public SurfaceTextureGLTest { +class SurfaceTextureGLThreadToGLTest : public SurfaceTextureGLToGLTest { protected: // ProducerThread is an abstract base class to simplify the creation of @@ -1223,30 +1426,8 @@ protected: 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 @@ protected: 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 @@ protected: 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 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageBeforeFrameFinishedCompletes) { // 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 @@ TEST_F(SurfaceTextureGLToGLTest, UpdateTexImageAfterFrameFinishedCompletes) { // 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, RepeatedUpdateTexImageBeforeFrameFinishedComple } } -TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedCompletes) { +TEST_F(SurfaceTextureGLThreadToGLTest, + RepeatedUpdateTexImageAfterFrameFinishedCompletes) { enum { NUM_ITERATIONS = 1024 }; class PT : public ProducerThread { @@ -1373,7 +1550,8 @@ TEST_F(SurfaceTextureGLToGLTest, RepeatedUpdateTexImageAfterFrameFinishedComplet } // 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(SurfaceTextureGLToGLTest, DISABLED_RepeatedSwapBuffersWhileDequeueStalled } } -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 70f1b7a4cc3d..0b5262dbf4b9 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -208,7 +208,7 @@ void OpenGLRenderer::resume() { 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 d51154d0710f..aff7b9364a90 100644 --- a/libs/hwui/Snapshot.h +++ b/libs/hwui/Snapshot.h @@ -213,7 +213,8 @@ public: 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 092ae2335502..927e1223fdff 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 @@ CameraSource::CameraSource( mLastFrameTimestampUs(0), mStarted(false), mNumFramesEncoded(0), + mTimeBetweenFrameCaptureUs(0), mFirstFrameTimeUs(0), mNumFramesDropped(0), mNumGlitches(0), @@ -644,7 +647,8 @@ status_t CameraSource::stop() { 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 @@ status_t CameraSource::read( 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 23e6e359c261..4f6c8d171c93 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -39,12 +39,12 @@ CameraSourceTimeLapse *CameraSourceTimeLapse::CreateFromCamera( 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 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( 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 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { // 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 97d011e58293..f9f92d262492 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -520,6 +520,85 @@ sp<MediaSource> OMXCodec::Create( 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 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) { } 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 d3a3662c8f4b..61fa565a049c 100644 --- a/opengl/java/android/opengl/ManagedEGLContext.java +++ b/opengl/java/android/opengl/ManagedEGLContext.java @@ -43,12 +43,13 @@ import com.google.android.gles_jni.EGLImpl; * 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 @@ public abstract class ManagedEGLContext { 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 5855b635b053..9c1a10e21438 100644 --- a/opengl/libs/Android.mk +++ b/opengl/libs/Android.mk @@ -44,10 +44,17 @@ ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) 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 2237eb6c0873..a63d5b054d00 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -370,6 +370,11 @@ EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, } } + // 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 fe32d4358994..c4a7466ec537 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 20a1c5026d8f..1db2a7feca00 100644 --- a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java +++ b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java @@ -30,12 +30,17 @@ public class DreamsDockLauncher extends Activity { 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 2837366b7ae5..72856b8eda24 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 @@ public class PhoneWindowManager implements WindowManagerPolicy { 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 dc628e027989..113f0070da8b 100644 --- a/voip/java/com/android/server/sip/SipHelper.java +++ b/voip/java/com/android/server/sip/SipHelper.java @@ -73,7 +73,7 @@ import javax.sip.message.Response; */ 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 119ed5412c94..38a683ed6a0d 100644 --- a/voip/java/com/android/server/sip/SipService.java +++ b/voip/java/com/android/server/sip/SipService.java @@ -68,8 +68,7 @@ import javax.sip.SipException; */ 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 @@ public final class SipService extends ISipService.Stub { @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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { // 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 @@ public final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { // 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 final class SipService extends ISipService.Stub { 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 @@ public final class SipService extends ISipService.Stub { } 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 06cdaf249ce5..877a0a44d23b 100644 --- a/voip/java/com/android/server/sip/SipSessionGroup.java +++ b/voip/java/com/android/server/sip/SipSessionGroup.java @@ -89,8 +89,8 @@ import javax.sip.message.Response; */ 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 @@ class SipSessionGroup implements SipListener { } 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 @@ class SipSessionGroup implements SipListener { + 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,9 +395,20 @@ class SipSessionGroup implements SipListener { 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, @@ -890,7 +903,9 @@ class SipSessionGroup implements SipListener { 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 @@ class SipSessionGroup implements SipListener { } } - 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 @@ class SipSessionGroup implements SipListener { 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 52bc094afab4..0c4d14c77184 100644 --- a/voip/java/com/android/server/sip/SipWakeLock.java +++ b/voip/java/com/android/server/sip/SipWakeLock.java @@ -22,8 +22,8 @@ import android.util.Log; 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 @@ class SipWakeLock { } 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 @@ class SipWakeLock { 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 @@ class SipWakeLock { && mWakeLock.isHeld()) { mWakeLock.release(); } - if (DEBUGV) Log.v(TAG, "release wakelock: holder count=" - + mHolders.size()); + if (DEBUG) Log.v(TAG, "release count=" + mHolders.size()); } } |