diff options
324 files changed, 10395 insertions, 7869 deletions
diff --git a/Android.mk b/Android.mk index 586eb4ac01ec..4cd85ece2959 100644 --- a/Android.mk +++ b/Android.mk @@ -329,20 +329,36 @@ non_base_dirs := \ ../../external/apache-http/src/org/apache/http # These are relative to frameworks/base -dirs_to_document := \ - $(fwbase_dirs_to_document) \ +dirs_to_check_apis := \ + $(fwbase_dirs_to_document) \ $(non_base_dirs) +# These are relative to frameworks/base +# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk +dirs_to_document := \ + $(dirs_to_check_apis) \ + $(addprefix ../../, $(FRAMEWORKS_SUPPORT_JAVA_SRC_DIRS)) + +# These are relative to frameworks/base html_dirs := \ $(FRAMEWORKS_BASE_SUBDIRS) \ $(non_base_dirs) +# Common sources for doc check and api check +common_src_files := \ + $(call find-other-html-files, $(html_dirs)) \ + $(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore)) \ + $(addprefix ../../system/media/mca/, $(call libfilterfw_to_document, $(LOCAL_PATH)/../../system/media/mca)) \ + # These are relative to frameworks/base framework_docs_LOCAL_SRC_FILES := \ $(call find-other-java-files, $(dirs_to_document)) \ - $(call find-other-html-files, $(html_dirs)) \ - $(addprefix ../../libcore/, $(call libcore_to_document, $(LOCAL_PATH)/../../libcore)) \ - $(addprefix ../../system/media/mca/, $(call libfilterfw_to_document, $(LOCAL_PATH)/../../system/media/mca)) + $(common_src_files) + +# These are relative to frameworks/base +framework_docs_LOCAL_API_CHECK_SRC_FILES := \ + $(call find-other-java-files, $(dirs_to_check_apis)) \ + $(common_src_files) # This is used by ide.mk as the list of source files that are # always included. @@ -412,6 +428,8 @@ web_docs_sample_code_flags := \ resources/samples/BackupRestore "Backup and Restore" \ -samplecode $(sample_dir)/BluetoothChat \ resources/samples/BluetoothChat "Bluetooth Chat" \ + -samplecode $(sample_dir)/BluetoothHDP \ + resources/samples/BluetoothHDP "Bluetooth HDP Demo" \ -samplecode $(sample_dir)/BusinessCard \ resources/samples/BusinessCard "Business Card" \ -samplecode $(sample_dir)/ContactManager \ @@ -493,7 +511,7 @@ framework_docs_LOCAL_DROIDDOC_OPTIONS += \ # ==== the api stubs and current.xml =========================== include $(CLEAR_VARS) -LOCAL_SRC_FILES:=$(framework_docs_LOCAL_SRC_FILES) +LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES) LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES) LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS) diff --git a/api/current.txt b/api/current.txt index 1bd2c096d823..4cf23ebf922f 100644 --- a/api/current.txt +++ b/api/current.txt @@ -83,6 +83,7 @@ package android { field public static final java.lang.String READ_PHONE_STATE = "android.permission.READ_PHONE_STATE"; field public static final java.lang.String READ_PROFILE = "android.permission.READ_PROFILE"; field public static final java.lang.String READ_SMS = "android.permission.READ_SMS"; + field public static final java.lang.String READ_SOCIAL_STREAM = "android.permission.READ_SOCIAL_STREAM"; field public static final java.lang.String READ_SYNC_SETTINGS = "android.permission.READ_SYNC_SETTINGS"; field public static final java.lang.String READ_SYNC_STATS = "android.permission.READ_SYNC_STATS"; field public static final java.lang.String REBOOT = "android.permission.REBOOT"; @@ -127,6 +128,7 @@ package android { field public static final java.lang.String WRITE_SECURE_SETTINGS = "android.permission.WRITE_SECURE_SETTINGS"; field public static final java.lang.String WRITE_SETTINGS = "android.permission.WRITE_SETTINGS"; field public static final java.lang.String WRITE_SMS = "android.permission.WRITE_SMS"; + field public static final java.lang.String WRITE_SOCIAL_STREAM = "android.permission.WRITE_SOCIAL_STREAM"; field public static final java.lang.String WRITE_SYNC_SETTINGS = "android.permission.WRITE_SYNC_SETTINGS"; } @@ -13632,16 +13634,19 @@ package android.opengl { field public static final int GL_RENDERBUFFER_RED_SIZE_OES = 36176; // 0x8d50 field public static final int GL_RENDERBUFFER_STENCIL_SIZE_OES = 36181; // 0x8d55 field public static final int GL_RENDERBUFFER_WIDTH_OES = 36162; // 0x8d42 + field public static final int GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES = 36200; // 0x8d68 field public static final int GL_RGB565_OES = 36194; // 0x8d62 field public static final int GL_RGB5_A1_OES = 32855; // 0x8057 field public static final int GL_RGB8_OES = 32849; // 0x8051 field public static final int GL_RGBA4_OES = 32854; // 0x8056 field public static final int GL_RGBA8_OES = 32856; // 0x8058 + field public static final int GL_SAMPLER_EXTERNAL_OES = 36198; // 0x8d66 field public static final int GL_STENCIL_ATTACHMENT_OES = 36128; // 0x8d20 field public static final int GL_STENCIL_INDEX1_OES = 36166; // 0x8d46 field public static final int GL_STENCIL_INDEX4_OES = 36167; // 0x8d47 field public static final int GL_STENCIL_INDEX8_OES = 36168; // 0x8d48 field public static final int GL_TEXTURE_BINDING_CUBE_MAP_OES = 34068; // 0x8514 + field public static final int GL_TEXTURE_BINDING_EXTERNAL_OES = 36199; // 0x8d67 field public static final int GL_TEXTURE_CROP_RECT_OES = 35741; // 0x8b9d field public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_X_OES = 34070; // 0x8516 field public static final int GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_OES = 34072; // 0x8518 @@ -13650,6 +13655,7 @@ package android.opengl { field public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_X_OES = 34069; // 0x8515 field public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Y_OES = 34071; // 0x8517 field public static final int GL_TEXTURE_CUBE_MAP_POSITIVE_Z_OES = 34073; // 0x8519 + field public static final int GL_TEXTURE_EXTERNAL_OES = 36197; // 0x8d65 field public static final int GL_TEXTURE_GEN_MODE_OES = 9472; // 0x2500 field public static final int GL_TEXTURE_GEN_STR_OES = 36192; // 0x8d60 field public static final int GL_TEXTURE_MATRIX_FLOAT_AS_INT_BITS_OES = 35215; // 0x898f @@ -25161,7 +25167,7 @@ package android.webkit { method public synchronized java.lang.String getFixedFontFamily(); method public synchronized boolean getJavaScriptCanOpenWindowsAutomatically(); method public synchronized boolean getJavaScriptEnabled(); - method public deprecated synchronized android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm(); + method public synchronized android.webkit.WebSettings.LayoutAlgorithm getLayoutAlgorithm(); method public boolean getLightTouchEnabled(); method public boolean getLoadWithOverviewMode(); method public synchronized boolean getLoadsImagesAutomatically(); @@ -25208,7 +25214,7 @@ package android.webkit { method public synchronized void setGeolocationEnabled(boolean); method public synchronized void setJavaScriptCanOpenWindowsAutomatically(boolean); method public synchronized void setJavaScriptEnabled(boolean); - method public deprecated synchronized void setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm); + method public synchronized void setLayoutAlgorithm(android.webkit.WebSettings.LayoutAlgorithm); method public void setLightTouchEnabled(boolean); method public void setLoadWithOverviewMode(boolean); method public synchronized void setLoadsImagesAutomatically(boolean); @@ -25243,12 +25249,12 @@ package android.webkit { field public static final int LOAD_NO_CACHE = 2; // 0x2 } - public static final deprecated class WebSettings.LayoutAlgorithm extends java.lang.Enum { + public static final class WebSettings.LayoutAlgorithm extends java.lang.Enum { method public static android.webkit.WebSettings.LayoutAlgorithm valueOf(java.lang.String); method public static final android.webkit.WebSettings.LayoutAlgorithm[] values(); enum_constant public static final android.webkit.WebSettings.LayoutAlgorithm NARROW_COLUMNS; enum_constant public static final android.webkit.WebSettings.LayoutAlgorithm NORMAL; - enum_constant public static final android.webkit.WebSettings.LayoutAlgorithm SINGLE_COLUMN; + enum_constant public static final deprecated android.webkit.WebSettings.LayoutAlgorithm SINGLE_COLUMN; } public static final class WebSettings.PluginState extends java.lang.Enum { diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index 7c03a2ff2020..140222ee7130 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -121,6 +121,10 @@ public class Am { runProfile(); } else if (op.equals("dumpheap")) { runDumpHeap(); + } else if (op.equals("set-debug-app")) { + runSetDebugApp(); + } else if (op.equals("clear-debug-app")) { + runClearDebugApp(); } else if (op.equals("monitor")) { runMonitor(); } else if (op.equals("screen-compat")) { @@ -666,6 +670,31 @@ public class Am { } } + private void runSetDebugApp() throws Exception { + boolean wait = false; + boolean persistent = false; + + String opt; + while ((opt=nextOption()) != null) { + if (opt.equals("-w")) { + wait = true; + } else if (opt.equals("--persistent")) { + persistent = true; + } else { + System.err.println("Error: Unknown option: " + opt); + showUsage(); + return; + } + } + + String pkg = nextArgRequired(); + mAm.setDebugApp(pkg, wait, persistent); + } + + private void runClearDebugApp() throws Exception { + mAm.setDebugApp(null, false, true); + } + class MyActivityController extends IActivityController.Stub { final String mGdbPort; @@ -1199,6 +1228,8 @@ public class Am { " am profile [looper] start <PROCESS> <FILE>\n" + " am profile [looper] stop [<PROCESS>]\n" + " am dumpheap [flags] <PROCESS> <FILE>\n" + + " am set-debug-app [-w] [--persistent] <PACKAGE>\n" + + " am clear-debug-app\n" + " am monitor [--gdb <port>]\n" + " am screen-compat [on|off] <PACKAGE>\n" + " am display-size [reset|MxN]\n" + @@ -1240,6 +1271,12 @@ public class Am { "am dumpheap: dump the heap of a process. Options are:\n" + " -n: dump native heap instead of managed heap\n" + "\n" + + "am set-debug-app: set application <PACKAGE> to debug. Options are:\n" + + " -w: wait for debugger when application starts\n" + + " --persistent: retain this value\n" + + "\n" + + "am clear-debug-app: clear the previously set-debug-app.\n" + + "\n" + "am monitor: start monitoring for crashes or ANRs.\n" + " --gdb: start gdbserv on the given port at crash/ANR\n" + "\n" + diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 1e746cd5c691..399e6686d351 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -121,6 +121,12 @@ static void dumpstate() { dump_file("NETWORK ROUTES", "/proc/net/route"); dump_file("NETWORK ROUTES IPV6", "/proc/net/ipv6_route"); + run_command("IP RULES", 10, "ip", "rule", "show", NULL); + run_command("IP RULES v6", 10, "ip", "-6", "rule", "show", NULL); + run_command("ROUTE TABLE 60", 10, "ip", "route", "show", "table", "60", NULL); + run_command("ROUTE TABLE 61 v6", 10, "ip", "-6", "route", "show", "table", "60", NULL); + run_command("ROUTE TABLE 61", 10, "ip", "route", "show", "table", "61", NULL); + run_command("ROUTE TABLE 61 v6", 10, "ip", "-6", "route", "show", "table", "61", NULL); dump_file("ARP CACHE", "/proc/net/arp"); run_command("IPTABLES", 10, "su", "root", "iptables", "-L", "-nvx", NULL); run_command("IP6TABLES", 10, "su", "root", "ip6tables", "-L", "-nvx", NULL); @@ -146,8 +152,7 @@ static void dumpstate() { "su", "root", "wlutil", "counters", NULL); #endif -#ifdef BROKEN_VRIL_IS_FIXED_B_4442803 - char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0}; + char ril_dumpstate_timeout[PROPERTY_VALUE_MAX] = {0}; property_get("ril.dumpstate.timeout", ril_dumpstate_timeout, "30"); if (strnlen(ril_dumpstate_timeout, PROPERTY_VALUE_MAX - 1) > 0) { if (0 == strncmp(build_type, "user", PROPERTY_VALUE_MAX - 1)) { @@ -161,7 +166,6 @@ static void dumpstate() { "su", "root", "vril-dump", NULL); } } -#endif print_properties(); diff --git a/core/java/android/accounts/ChooseTypeAndAccountActivity.java b/core/java/android/accounts/ChooseTypeAndAccountActivity.java index c3c9d16e56c7..136c68c13466 100644 --- a/core/java/android/accounts/ChooseTypeAndAccountActivity.java +++ b/core/java/android/accounts/ChooseTypeAndAccountActivity.java @@ -216,7 +216,7 @@ public class ChooseTypeAndAccountActivity extends Activity if (mPendingRequest == REQUEST_NULL) { // If there are no allowable accounts go directly to add account - if (mAccountInfos.isEmpty()) { + if (shouldSkipToChooseAccountTypeFlow()) { startChooseAccountTypeActivity(); return; } @@ -265,6 +265,12 @@ public class ChooseTypeAndAccountActivity extends Activity mPendingRequest = REQUEST_NULL; if (resultCode == RESULT_CANCELED) { + // if cancelling out of addAccount and the original state caused us to skip this, + // finish this activity + if (shouldSkipToChooseAccountTypeFlow()) { + setResult(Activity.RESULT_CANCELED); + finish(); + } return; } @@ -318,6 +324,14 @@ public class ChooseTypeAndAccountActivity extends Activity finish(); } + /** + * convenience method to check if we should skip the accounts list display and immediately + * jump to the flow that asks the user to select from the account type list + */ + private boolean shouldSkipToChooseAccountTypeFlow() { + return mAccountInfos.isEmpty(); + } + protected void runAddAccountForAuthenticator(String type) { if (Log.isLoggable(TAG, Log.VERBOSE)) { Log.v(TAG, "runAddAccountForAuthenticator: " + type); diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java index ea5c3db86571..d97165256534 100644 --- a/core/java/android/bluetooth/BluetoothAdapter.java +++ b/core/java/android/bluetooth/BluetoothAdapter.java @@ -533,6 +533,7 @@ public final class BluetoothAdapter { * @hide */ public ParcelUuid[] getUuids() { + if (getState() != STATE_ON) return null; try { return mService.getUuids(); } catch (RemoteException e) {Log.e(TAG, "", e);} diff --git a/core/java/android/hardware/Camera.java b/core/java/android/hardware/Camera.java index c2a757f06808..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; } /** @@ -2512,13 +2499,16 @@ public class Camera { /** * Sets the white balance. Changing the setting will release the - * auto-white balance lock. + * auto-white balance lock. It is recommended not to change white + * balance and AWB lock at the same time. * * @param value new white balance. * @see #getWhiteBalance() * @see #setAutoWhiteBalanceLock(boolean) */ public void setWhiteBalance(String value) { + String oldValue = get(KEY_WHITE_BALANCE); + if (same(value, oldValue)) return; set(KEY_WHITE_BALANCE, value); set(KEY_AUTO_WHITEBALANCE_LOCK, FALSE); } @@ -3493,195 +3483,11 @@ public class Camera { return result; } - }; - - /** - * <p>The set of default system sounds for camera actions. Use this with - * {@link #playSound} to play an appropriate sound when implementing a - * custom still or video recording mechanism through the preview - * callbacks.</p> - * - * <p>There is no need to play sounds when using {@link #takePicture} or - * {@link android.media.MediaRecorder} for still images or video, - * respectively, as these play their own sounds when needed.</p> - * - * @see #playSound - * @hide - */ - public static class Sound { - /** - * The sound used by {@link android.hardware.Camera#takePicture} to - * indicate still image capture. - */ - public static final int SHUTTER_CLICK = 0; - - /** - * A sound to indicate that focusing has completed. Because deciding - * when this occurs is application-dependent, this sound is not used by - * any methods in the Camera class. - */ - public static final int FOCUS_COMPLETE = 1; - - /** - * The sound used by {@link android.media.MediaRecorder#start} to - * indicate the start of video recording. - */ - public static final int START_VIDEO_RECORDING = 2; - - /** - * The sound used by {@link android.media.MediaRecorder#stop} to - * indicate the end of video recording. - */ - public static final int STOP_VIDEO_RECORDING = 3; - - private static final int NUM_SOUNDS = 4; - }; - - /** - * <p>Play one of the predefined platform sounds for camera actions.</p> - * - * <p>Use this method to play a platform-specific sound for various camera - * actions. The sound playing is done asynchronously, with the same behavior - * and content as the sounds played by {@link #takePicture takePicture}, - * {@link android.media.MediaRecorder#start MediaRecorder.start}, and - * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p> - * - * <p>Using this method makes it easy to match the default device sounds - * when recording or capturing data through the preview callbacks - * ({@link #setPreviewCallback setPreviewCallback}, - * {@link #setPreviewTexture setPreviewTexture}).</p> - * - * @param soundId The type of sound to play, selected from the options in - * {@link android.hardware.Camera.Sound} - * @see android.hardware.Camera.Sound - * @see #takePicture - * @see android.media.MediaRecorder - * @hide - */ - public void playSound(int soundId) { - if (mReleased) return; - if (mCameraSoundPlayers == null) { - mCameraSoundPlayers = new CameraSoundPlayer[Sound.NUM_SOUNDS]; - } - if (mCameraSoundPlayers[soundId] == null) { - mCameraSoundPlayers[soundId] = new CameraSoundPlayer(soundId); - } - mCameraSoundPlayers[soundId].play(); - } - - private CameraSoundPlayer[] mCameraSoundPlayers; - - private static class CameraSoundPlayer implements Runnable { - private int mSoundId; - private int mAudioStreamType; - private MediaPlayer mPlayer; - private Thread mThread; - private boolean mExit; - private int mPlayCount; - - private static final String mShutterSound = - "/system/media/audio/ui/camera_click.ogg"; - private static final String mFocusSound = - "/system/media/audio/ui/camera_focus.ogg"; - private static final String mVideoStartSound = - "/system/media/audio/ui/VideoRecord.ogg"; - private static final String mVideoStopSound = - "/system/media/audio/ui/VideoRecord.ogg"; - - @Override - public void run() { - String soundFilePath; - switch (mSoundId) { - case Sound.SHUTTER_CLICK: - soundFilePath = mShutterSound; - break; - case Sound.FOCUS_COMPLETE: - soundFilePath = mFocusSound; - break; - case Sound.START_VIDEO_RECORDING: - soundFilePath = mVideoStartSound; - break; - case Sound.STOP_VIDEO_RECORDING: - soundFilePath = mVideoStopSound; - break; - default: - Log.e(TAG, "Unknown sound " + mSoundId + " requested."); - return; - } - mPlayer = new MediaPlayer(); - try { - mPlayer.setAudioStreamType(mAudioStreamType); - mPlayer.setDataSource(soundFilePath); - mPlayer.setLooping(false); - mPlayer.prepare(); - } catch(IOException e) { - Log.e(TAG, "Error setting up sound " + mSoundId, e); - return; - } - - while(true) { - try { - synchronized (this) { - while(true) { - if (mExit) { - return; - } else if (mPlayCount <= 0) { - wait(); - } else { - mPlayCount--; - break; - } - } - } - mPlayer.start(); - } catch (Exception e) { - Log.e(TAG, "Error playing sound " + mSoundId, e); - } - } - } - - public CameraSoundPlayer(int soundId) { - mSoundId = soundId; - if (SystemProperties.get("ro.camera.sound.forced", "0").equals("0")) { - mAudioStreamType = AudioManager.STREAM_MUSIC; - } else { - mAudioStreamType = AudioManager.STREAM_SYSTEM_ENFORCED; - } - } - public void play() { - if (mThread == null) { - mThread = new Thread(this); - mThread.start(); - } - synchronized (this) { - mPlayCount++; - notifyAll(); - } + private boolean same(String s1, String s2) { + if (s1 == null && s2 == null) return true; + if (s1 != null && s1.equals(s2)) return true; + return false; } - - 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 dec262bb31c5..02096f2a716e 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -357,9 +357,16 @@ 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 + return null; + } return manager.getDefaultAdapter(); } 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/provider/ContactsContract.java b/core/java/android/provider/ContactsContract.java index 83acef83ad1d..d724d5697844 100644 --- a/core/java/android/provider/ContactsContract.java +++ b/core/java/android/provider/ContactsContract.java @@ -6747,6 +6747,39 @@ public final class ContactsContract { */ public static final String NAMESPACE = DataColumns.DATA2; } + + /** + * <p> + * Convenient functionalities for "callable" data. Note that, this is NOT a separate data + * kind. + * </p> + * <p> + * This URI allows the ContactsProvider to return a unified result for "callable" data + * that users can use for calling purposes. {@link Phone} and {@link SipAddress} are the + * current examples for "callable", but may be expanded to the other types. + * </p> + * <p> + * Each returned row may have a different MIMETYPE and thus different interpretation for + * each column. For example the meaning for {@link Phone}'s type is different than + * {@link SipAddress}'s. + * </p> + * + * @hide + */ + public static final class Callable implements DataColumnsWithJoins, CommonColumns { + /** + * Similar to {@link Phone#CONTENT_URI}, but returns callable data instead of only + * phone numbers. + */ + public static final Uri CONTENT_URI = Uri.withAppendedPath(Data.CONTENT_URI, + "callables"); + /** + * Similar to {@link Phone#CONTENT_FILTER_URI}, but allows users to filter callable + * data. + */ + public static final Uri CONTENT_FILTER_URI = Uri.withAppendedPath(CONTENT_URI, + "filter"); + } } /** diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java index 4349063fbaf3..1b5d73e3f4b8 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -4040,21 +4040,26 @@ public final class Settings { "setup_prepaid_detection_redir_host"; /** - * The user's preferred "dream" (interactive screensaver) component. + * Whether the screensaver is enabled. + * @hide + */ + public static final String SCREENSAVER_ENABLED = "screensaver_enabled"; + + /** + * The user's chosen screensaver component. * - * This component will be launched by the PhoneWindowManager after the user's chosen idle - * timeout (specified by {@link #DREAM_TIMEOUT}). + * This component will be launched by the PhoneWindowManager after a timeout when not on + * battery, or upon dock insertion (if SCREENSAVER_ACTIVATE_ON_DOCK is set to 1). * @hide */ - public static final String DREAM_COMPONENT = - "dream_component"; + public static final String SCREENSAVER_COMPONENT = "screensaver_component"; /** - * The delay before a "dream" is started (set to 0 to disable). + * Whether the screensaver should be automatically launched when the device is inserted + * into a (desk) dock. * @hide */ - public static final String DREAM_TIMEOUT = - "dream_timeout"; + public static final String SCREENSAVER_ACTIVATE_ON_DOCK = "screensaver_activate_on_dock"; /** {@hide} */ public static final String NETSTATS_ENABLED = "netstats_enabled"; diff --git a/core/java/android/server/BluetoothEventLoop.java b/core/java/android/server/BluetoothEventLoop.java index aa62cd70190e..a2038c932156 100644 --- a/core/java/android/server/BluetoothEventLoop.java +++ b/core/java/android/server/BluetoothEventLoop.java @@ -784,12 +784,12 @@ class BluetoothEventLoop { // machine. We don't handle AVCTP signals currently. We only send // intents for AVDTP state changes. We need to handle both of them in // some cases. For now, just don't move to incoming state in this case. - mBluetoothService.notifyIncomingA2dpConnection(address, true); + mBluetoothService.notifyIncomingA2dpConnection(address, false); } else { Log.i(TAG, "" + authorized + "Incoming A2DP / AVRCP connection from " + address); mA2dp.allowIncomingConnect(device, authorized); - mBluetoothService.notifyIncomingA2dpConnection(address, false); + mBluetoothService.notifyIncomingA2dpConnection(address, true); } } else if (BluetoothUuid.isInputDevice(uuid)) { // We can have more than 1 input device connected. diff --git a/core/java/android/server/BluetoothService.java b/core/java/android/server/BluetoothService.java index d604a01a759e..28e231e9d2a8 100755 --- a/core/java/android/server/BluetoothService.java +++ b/core/java/android/server/BluetoothService.java @@ -810,7 +810,7 @@ public class BluetoothService extends IBluetooth.Stub { } } - /*package*/ synchronized String getProperty(String name, boolean checkState) { + /*package*/ String getProperty(String name, boolean checkState) { // If checkState is false, check if the event loop is running. // before making the call to Bluez if (checkState) { @@ -854,14 +854,14 @@ public class BluetoothService extends IBluetooth.Stub { return getProperty("Name", false); } - public synchronized ParcelUuid[] getUuids() { + public ParcelUuid[] getUuids() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); String value = getProperty("UUIDs", true); if (value == null) return null; return convertStringToParcelUuid(value); } - private synchronized ParcelUuid[] convertStringToParcelUuid(String value) { + private ParcelUuid[] convertStringToParcelUuid(String value) { String[] uuidStrings = null; // The UUIDs are stored as a "," separated string. uuidStrings = value.split(","); @@ -934,7 +934,7 @@ public class BluetoothService extends IBluetooth.Stub { * @return The discoverability window of the device, in seconds. A negative * value indicates an error. */ - public synchronized int getDiscoverableTimeout() { + public int getDiscoverableTimeout() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); String timeout = getProperty("DiscoverableTimeout", true); if (timeout != null) @@ -943,7 +943,7 @@ public class BluetoothService extends IBluetooth.Stub { return -1; } - public synchronized int getScanMode() { + public int getScanMode() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); if (!isEnabledInternal()) return BluetoothAdapter.SCAN_MODE_NONE; @@ -969,7 +969,7 @@ public class BluetoothService extends IBluetooth.Stub { return stopDiscoveryNative(); } - public synchronized boolean isDiscovering() { + public boolean isDiscovering() { mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); String discoveringProperty = getProperty("Discovering", false); @@ -2387,7 +2387,7 @@ public class BluetoothService extends IBluetooth.Stub { mDeviceProfileState.remove(address); } - synchronized String[] getKnownDevices() { + String[] getKnownDevices() { String[] bonds = null; String val = getProperty("Devices", true); if (val != null) { 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/speech/tts/AudioPlaybackHandler.java b/core/java/android/speech/tts/AudioPlaybackHandler.java index 0194240adc29..fd00dcea6dcf 100644 --- a/core/java/android/speech/tts/AudioPlaybackHandler.java +++ b/core/java/android/speech/tts/AudioPlaybackHandler.java @@ -118,12 +118,26 @@ class AudioPlaybackHandler { if (current != null && TextUtils.equals(callingApp, current.getCallingApp())) { stop(current); } + + final MessageParams lastSynthesis = mLastSynthesisRequest; + + if (lastSynthesis != null && lastSynthesis != current && + TextUtils.equals(callingApp, lastSynthesis.getCallingApp())) { + stop(lastSynthesis); + } } synchronized public void removeAllItems() { if (DBG_THREADING) Log.d(TAG, "Removing all items"); removeAllMessages(); - stop(getCurrentParams()); + + final MessageParams current = getCurrentParams(); + final MessageParams lastSynthesis = mLastSynthesisRequest; + stop(current); + + if (lastSynthesis != null && lastSynthesis != current) { + stop(lastSynthesis); + } } /** @@ -350,7 +364,7 @@ class AudioPlaybackHandler { // extra trouble to clean the data to prevent the AudioTrack resources // from being leaked. if (mLastSynthesisRequest != null) { - Log.w(TAG, "Error : Missing call to done() for request : " + + Log.e(TAG, "Error : Missing call to done() for request : " + mLastSynthesisRequest); handleSynthesisDone(mLastSynthesisRequest); } @@ -443,7 +457,11 @@ class AudioPlaybackHandler { audioTrack.release(); params.setAudioTrack(null); } - params.getDispatcher().dispatchOnDone(); + if (params.isError()) { + params.getDispatcher().dispatchOnError(); + } else { + params.getDispatcher().dispatchOnDone(); + } mLastSynthesisRequest = null; params.mLogger.onWriteData(); } diff --git a/core/java/android/speech/tts/PlaybackSynthesisCallback.java b/core/java/android/speech/tts/PlaybackSynthesisCallback.java index ce3522be2d86..91a3452916cb 100644 --- a/core/java/android/speech/tts/PlaybackSynthesisCallback.java +++ b/core/java/android/speech/tts/PlaybackSynthesisCallback.java @@ -80,27 +80,23 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { @Override void stop() { + stopImpl(false); + } + + void stopImpl(boolean wasError) { if (DBG) Log.d(TAG, "stop()"); // Note that mLogger.mError might be true too at this point. mLogger.onStopped(); - SynthesisMessageParams token = null; + SynthesisMessageParams token; synchronized (mStateLock) { if (mStopped) { Log.w(TAG, "stop() called twice"); return; } - // mToken will be null if the engine encounters - // an error before it called start(). - if (mToken == null) { - // In all other cases, mAudioTrackHandler.stop() will - // result in onComplete being called. - mLogger.onWriteData(); - } else { - token = mToken; - } + token = mToken; mStopped = true; } @@ -109,7 +105,24 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // point it will write an additional buffer to the token - but we // won't worry about that because the audio playback queue will be cleared // soon after (see SynthHandler#stop(String). + token.setIsError(wasError); token.clearBuffers(); + if (wasError) { + // Also clean up the audio track if an error occurs. + mAudioTrackHandler.enqueueSynthesisDone(token); + } + } else { + // This happens when stop() or error() were called before start() was. + + // In all other cases, mAudioTrackHandler.stop() will + // result in onSynthesisDone being called, and we will + // write data there. + mLogger.onWriteData(); + + if (wasError) { + // We have to dispatch the error ourselves. + mDispatcher.dispatchOnError(); + } } } @@ -219,7 +232,7 @@ class PlaybackSynthesisCallback extends AbstractSynthesisCallback { // Currently, this call will not be logged if error( ) is called // before start. mLogger.onError(); - stop(); + stopImpl(true); } } diff --git a/core/java/android/speech/tts/SynthesisMessageParams.java b/core/java/android/speech/tts/SynthesisMessageParams.java index 0c0f033fa066..ed66420df499 100644 --- a/core/java/android/speech/tts/SynthesisMessageParams.java +++ b/core/java/android/speech/tts/SynthesisMessageParams.java @@ -51,6 +51,7 @@ final class SynthesisMessageParams extends MessageParams { int mAudioBufferSize; // Always synchronized on "this". int mUnconsumedBytes; + volatile boolean mIsError; private final LinkedList<ListEntry> mDataBufferList = new LinkedList<ListEntry>(); @@ -74,6 +75,7 @@ final class SynthesisMessageParams extends MessageParams { mAudioTrack = null; mBytesWritten = 0; mAudioBufferSize = 0; + mIsError = false; } @Override @@ -120,6 +122,14 @@ final class SynthesisMessageParams extends MessageParams { return mAudioTrack; } + void setIsError(boolean isError) { + mIsError = isError; + } + + boolean isError() { + return mIsError; + } + // Must be called synchronized on this. private long getUnconsumedAudioLengthMs() { final int unconsumedFrames = mUnconsumedBytes / mBytesPerFrame; diff --git a/core/java/android/speech/tts/TextToSpeech.java b/core/java/android/speech/tts/TextToSpeech.java index 38699eaad44f..a220615c0079 100755 --- a/core/java/android/speech/tts/TextToSpeech.java +++ b/core/java/android/speech/tts/TextToSpeech.java @@ -490,6 +490,7 @@ public class TextToSpeech { private final Map<String, Uri> mUtterances; private final Bundle mParams = new Bundle(); private final TtsEngines mEnginesHelper; + private final String mPackageName; private volatile String mCurrentEngine = null; /** @@ -518,19 +519,36 @@ public class TextToSpeech { * @param engine Package name of the TTS engine to use. */ public TextToSpeech(Context context, OnInitListener listener, String engine) { + this(context, listener, engine, null); + } + + /** + * Used by the framework to instantiate TextToSpeech objects with a supplied + * package name, instead of using {@link android.content.Context#getPackageName()} + * + * @hide + */ + public TextToSpeech(Context context, OnInitListener listener, String engine, + String packageName) { mContext = context; mInitListener = listener; mRequestedEngine = engine; mEarcons = new HashMap<String, Uri>(); mUtterances = new HashMap<String, Uri>(); + mUtteranceProgressListener = null; mEnginesHelper = new TtsEngines(mContext); + if (packageName != null) { + mPackageName = packageName; + } else { + mPackageName = mContext.getPackageName(); + } initTts(); } private String getPackageName() { - return mContext.getPackageName(); + return mPackageName; } private <R> R runActionNoReconnect(Action<R> action, R errorResult, String method) { diff --git a/core/java/android/speech/tts/TextToSpeechService.java b/core/java/android/speech/tts/TextToSpeechService.java index 39922da68b34..aee678a2ffaa 100644 --- a/core/java/android/speech/tts/TextToSpeechService.java +++ b/core/java/android/speech/tts/TextToSpeechService.java @@ -450,7 +450,7 @@ public abstract class TextToSpeechService extends Service { @Override public void dispatchOnDone() { final String utteranceId = getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { + if (utteranceId != null) { mCallbacks.dispatchOnDone(getCallingApp(), utteranceId); } } @@ -458,7 +458,7 @@ public abstract class TextToSpeechService extends Service { @Override public void dispatchOnStart() { final String utteranceId = getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { + if (utteranceId != null) { mCallbacks.dispatchOnStart(getCallingApp(), utteranceId); } } @@ -466,7 +466,7 @@ public abstract class TextToSpeechService extends Service { @Override public void dispatchOnError() { final String utteranceId = getUtteranceId(); - if (!TextUtils.isEmpty(utteranceId)) { + if (utteranceId != null) { mCallbacks.dispatchOnError(getCallingApp(), utteranceId); } } @@ -509,6 +509,7 @@ public abstract class TextToSpeechService extends Service { } class SynthesisSpeechItem extends SpeechItem { + // Never null. private final String mText; private final SynthesisRequest mSynthesisRequest; private final String[] mDefaultLocale; @@ -532,8 +533,8 @@ public abstract class TextToSpeechService extends Service { @Override public boolean isValid() { - if (TextUtils.isEmpty(mText)) { - Log.w(TAG, "Got empty text"); + if (mText == null) { + Log.wtf(TAG, "Got null text"); return false; } if (mText.length() >= MAX_SPEECH_ITEM_CHAR_LENGTH) { diff --git a/core/java/android/speech/tts/UtteranceProgressListener.java b/core/java/android/speech/tts/UtteranceProgressListener.java index a04458a4ddf7..cf0d22cd0670 100644 --- a/core/java/android/speech/tts/UtteranceProgressListener.java +++ b/core/java/android/speech/tts/UtteranceProgressListener.java @@ -57,12 +57,16 @@ public abstract class UtteranceProgressListener { listener.onUtteranceCompleted(utteranceId); } - // The following methods are left unimplemented. @Override - public void onStart(String utteranceId) { } + public void onError(String utteranceId) { + listener.onUtteranceCompleted(utteranceId); + } @Override - public void onError(String utteranceId) { } + public void onStart(String utteranceId) { + // Left unimplemented, has no equivalent in the old + // API. + } }; } } diff --git a/core/java/android/text/format/DateUtils.java b/core/java/android/text/format/DateUtils.java index 22a7528e2172..da103111a8ff 100644 --- a/core/java/android/text/format/DateUtils.java +++ b/core/java/android/text/format/DateUtils.java @@ -1412,7 +1412,7 @@ public class DateUtils } } - if (noMonthDay && startMonthNum == endMonthNum) { + if (noMonthDay && startMonthNum == endMonthNum && startYear == endYear) { // Example: "January, 2008" return formatter.format("%s", startDate.format(defaultDateFormat)); } diff --git a/core/java/android/util/LruCache.java b/core/java/android/util/LruCache.java index 5540000eff43..f1014a7ae1b1 100644 --- a/core/java/android/util/LruCache.java +++ b/core/java/android/util/LruCache.java @@ -54,6 +54,10 @@ import java.util.Map; * <p>This class does not allow null to be used as a key or value. A return * value of null from {@link #get}, {@link #put} or {@link #remove} is * unambiguous: the key was not in the cache. + * + * <p>This class appeared in Android 3.1 (Honeycomb MR1); it's available as part + * of <a href="http://developer.android.com/sdk/compatibility-library.html">Android's + * Support Package</a> for earlier releases. */ public class LruCache<K, V> { private final LinkedHashMap<K, V> map; diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index e0167d82bef0..f77cf7e8398b 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -22,6 +22,9 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.opengl.GLUtils; +import android.opengl.ManagedEGLContext; +import android.os.Handler; +import android.os.Looper; import android.os.SystemClock; import android.os.SystemProperties; import android.util.Log; @@ -159,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. * @@ -345,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. @@ -409,7 +411,8 @@ public abstract class HardwareRenderer { static final Object[] sEglLock = new Object[0]; int mWidth = -1, mHeight = -1; - static final ThreadLocal<EGLContext> sEglContextStorage = new ThreadLocal<EGLContext>(); + static final ThreadLocal<Gl20Renderer.Gl20RendererEglContext> sEglContextStorage + = new ThreadLocal<Gl20Renderer.Gl20RendererEglContext>(); EGLContext mEglContext; Thread mEglThread; @@ -561,12 +564,13 @@ public abstract class HardwareRenderer { } } - mEglContext = 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(mEglContext); + sEglContextStorage.set(new Gl20Renderer.Gl20RendererEglContext(mEglContext)); } } @@ -904,6 +908,52 @@ public abstract class HardwareRenderer { private static EGLSurface sPbuffer; private static final Object[] sPbufferLock = new Object[0]; + static class Gl20RendererEglContext extends ManagedEGLContext { + final Handler mHandler = new Handler(); + + public Gl20RendererEglContext(EGLContext context) { + super(context); + } + + @Override + public void onTerminate(final EGLContext eglContext) { + // Make sure we do this on the correct thread. + if (mHandler.getLooper() != Looper.myLooper()) { + mHandler.post(new Runnable() { + @Override public void run() { + onTerminate(eglContext); + } + }); + return; + } + + synchronized (sEglLock) { + if (sEgl == null) return; + + if (EGLImpl.getInitCount(sEglDisplay) == 1) { + usePbufferSurface(eglContext); + GLES20Canvas.terminateCaches(); + + sEgl.eglDestroyContext(sEglDisplay, eglContext); + sEglContextStorage.remove(); + + sEgl.eglDestroySurface(sEglDisplay, sPbuffer); + sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, + EGL_NO_SURFACE, EGL_NO_CONTEXT); + + sEgl.eglReleaseThread(); + sEgl.eglTerminate(sEglDisplay); + + sEgl = null; + sEglDisplay = null; + sEglConfig = null; + sPbuffer = null; + sEglContextStorage.set(null); + } + } + } + } + Gl20Renderer(boolean translucent) { super(2, translucent); } @@ -996,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; @@ -1009,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()) { @@ -1020,12 +1099,12 @@ public abstract class HardwareRenderer { static void trimMemory(int level) { if (sEgl == null || sEglConfig == null) return; - EGLContext eglContext = sEglContextStorage.get(); + Gl20RendererEglContext managedContext = sEglContextStorage.get(); // We do not have OpenGL objects - if (eglContext == null) { + if (managedContext == null) { return; } else { - usePbufferSurface(eglContext); + usePbufferSurface(managedContext.getContext()); } switch (level) { @@ -1052,33 +1131,5 @@ public abstract class HardwareRenderer { } sEgl.eglMakeCurrent(sEglDisplay, sPbuffer, sPbuffer, eglContext); } - - static void terminate() { - synchronized (sEglLock) { - if (sEgl == null) return; - - if (EGLImpl.getInitCount(sEglDisplay) == 1) { - EGLContext eglContext = sEglContextStorage.get(); - if (eglContext == null) return; - - usePbufferSurface(eglContext); - GLES20Canvas.terminateCaches(); - - sEgl.eglDestroyContext(sEglDisplay, eglContext); - sEglContextStorage.remove(); - - sEgl.eglDestroySurface(sEglDisplay, sPbuffer); - sEgl.eglMakeCurrent(sEglDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); - - sEgl.eglReleaseThread(); - sEgl.eglTerminate(sEglDisplay); - - sEgl = null; - sEglDisplay = null; - sEglConfig = null; - sPbuffer = null; - } - } - } } } diff --git a/core/java/android/view/InputDevice.java b/core/java/android/view/InputDevice.java index bfc7c31e5a82..8115b362680e 100755 --- a/core/java/android/view/InputDevice.java +++ b/core/java/android/view/InputDevice.java @@ -19,7 +19,6 @@ package android.view; import android.os.Parcel; import android.os.Parcelable; import android.os.RemoteException; -import android.os.ServiceManager; import java.util.ArrayList; import java.util.List; @@ -44,6 +43,7 @@ public final class InputDevice implements Parcelable { private String mName; private int mSources; private int mKeyboardType; + private String mKeyCharacterMapFile; private final ArrayList<MotionRange> mMotionRanges = new ArrayList<MotionRange>(); @@ -360,6 +360,10 @@ public final class InputDevice implements Parcelable { return KeyCharacterMap.load(mId); } + String getKeyCharacterMapFile() { + return mKeyCharacterMapFile; + } + /** * Gets information about the range of values for a particular {@link MotionEvent} axis. * If the device supports multiple sources, the same axis may have different meanings @@ -532,6 +536,7 @@ public final class InputDevice implements Parcelable { mName = in.readString(); mSources = in.readInt(); mKeyboardType = in.readInt(); + mKeyCharacterMapFile = in.readString(); for (;;) { int axis = in.readInt(); @@ -549,6 +554,7 @@ public final class InputDevice implements Parcelable { out.writeString(mName); out.writeInt(mSources); out.writeInt(mKeyboardType); + out.writeString(mKeyCharacterMapFile); final int numRanges = mMotionRanges.size(); for (int i = 0; i < numRanges; i++) { @@ -587,6 +593,8 @@ public final class InputDevice implements Parcelable { } description.append("\n"); + description.append(" Key Character Map: ").append(mKeyCharacterMapFile).append("\n"); + description.append(" Sources: 0x").append(Integer.toHexString(mSources)).append(" ("); appendSourceDescriptionIfApplicable(description, SOURCE_KEYBOARD, "keyboard"); appendSourceDescriptionIfApplicable(description, SOURCE_DPAD, "dpad"); diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java index 885a75f64168..575af3bc10e7 100644 --- a/core/java/android/view/KeyCharacterMap.java +++ b/core/java/android/view/KeyCharacterMap.java @@ -20,7 +20,6 @@ import android.text.method.MetaKeyKeyListener; import android.util.AndroidRuntimeException; import android.util.SparseIntArray; import android.os.RemoteException; -import android.os.ServiceManager; import android.util.SparseArray; import java.lang.Character; @@ -140,7 +139,7 @@ public class KeyCharacterMap { private final int mDeviceId; private int mPtr; - private static native int nativeLoad(int id); + private static native int nativeLoad(String file); private static native void nativeDispose(int ptr); private static native char nativeGetCharacter(int ptr, int keyCode, int metaState); @@ -178,7 +177,17 @@ public class KeyCharacterMap { synchronized (sInstances) { KeyCharacterMap map = sInstances.get(deviceId); if (map == null) { - int ptr = nativeLoad(deviceId); // might throw + String kcm = null; + if (deviceId != VIRTUAL_KEYBOARD) { + InputDevice device = InputDevice.getDevice(deviceId); + if (device != null) { + kcm = device.getKeyCharacterMapFile(); + } + } + if (kcm == null || kcm.length() == 0) { + kcm = "/system/usr/keychars/Virtual.kcm"; + } + int ptr = nativeLoad(kcm); // might throw map = new KeyCharacterMap(deviceId, ptr); sInstances.put(deviceId, map); } 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 2095e91fc00c..74c1ce8e4e60 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -1488,7 +1488,8 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal | AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED | AccessibilityEvent.TYPE_VIEW_HOVER_ENTER | AccessibilityEvent.TYPE_VIEW_HOVER_EXIT - | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED; + | AccessibilityEvent.TYPE_VIEW_TEXT_CHANGED + | AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED; /** * Temporary Rect currently for use in setBackground(). This will probably @@ -10128,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; @@ -10195,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); @@ -10290,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 d89bc36ad8d4..dfd1d5538030 100644 --- a/core/java/android/view/WindowManagerImpl.java +++ b/core/java/android/view/WindowManagerImpl.java @@ -21,6 +21,7 @@ import android.content.ComponentCallbacks2; import android.content.res.CompatibilityInfo; import android.content.res.Configuration; import android.graphics.PixelFormat; +import android.opengl.ManagedEGLContext; import android.os.IBinder; import android.util.AndroidRuntimeException; import android.util.Log; @@ -424,11 +425,11 @@ 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 - HardwareRenderer.terminate(); + ManagedEGLContext.doTerminate(); break; } // high end gfx devices fall through to next case diff --git a/core/java/android/view/accessibility/AccessibilityEvent.java b/core/java/android/view/accessibility/AccessibilityEvent.java index c6f778f953fa..34b4dcc5ebb6 100644 --- a/core/java/android/view/accessibility/AccessibilityEvent.java +++ b/core/java/android/view/accessibility/AccessibilityEvent.java @@ -220,15 +220,6 @@ import java.util.List; * <li>{@link #isEnabled()} - Whether the source is enabled.</li> * <li>{@link #getContentDescription()} - The content description of the source.</li> * </ul> - * <em>Note:</em> This event type is not dispatched to descendants though - * {@link android.view.View#dispatchPopulateAccessibilityEvent(AccessibilityEvent) - * View.dispatchPopulateAccessibilityEvent(AccessibilityEvent)}, hence the event - * source {@link android.view.View} and the sub-tree rooted at it will not receive - * calls to {@link android.view.View#onPopulateAccessibilityEvent(AccessibilityEvent) - * View.onPopulateAccessibilityEvent(AccessibilityEvent)}. The preferred way to add - * text content to such events is by setting the - * {@link android.R.styleable#View_contentDescription contentDescription} of the source - * view.</br> * </p> * <p> * <b>View scrolled</b> - represents the event of scrolling a view. If diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index 0eb6e27f5d29..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,13 +211,18 @@ 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 final Handler mHandler; + private Handler mHandler; private boolean mOpened; private ISpellCheckerSession mISpellCheckerSession; @@ -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"); @@ -334,6 +367,7 @@ public class SpellCheckerSession { try { mISpellCheckerSession.onClose(); mISpellCheckerSession = null; + mHandler = null; } catch (RemoteException e) { Log.e(TAG, "Failed to close " + e); } @@ -354,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)); + } } /** @@ -369,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 { @@ -410,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/WebSettings.java b/core/java/android/webkit/WebSettings.java index f240a2e243b5..f947f95f9d9a 100644 --- a/core/java/android/webkit/WebSettings.java +++ b/core/java/android/webkit/WebSettings.java @@ -42,12 +42,14 @@ public class WebSettings { * SINGLE_COLUMN moves all content into one column that is the width of the * view. * NARROW_COLUMNS makes all columns no wider than the screen if possible. - * @deprecated This enum is now obsolete. */ // XXX: These must match LayoutAlgorithm in Settings.h in WebCore. - @Deprecated public enum LayoutAlgorithm { NORMAL, + /** + * @deprecated This algorithm is now obsolete. + */ + @Deprecated SINGLE_COLUMN, NARROW_COLUMNS } @@ -799,7 +801,7 @@ public class WebSettings { public void setDefaultZoom(ZoomDensity zoom) { if (mDefaultZoom != zoom) { mDefaultZoom = zoom; - mWebView.updateDefaultZoomDensity(zoom.value); + mWebView.adjustDefaultZoomDensity(zoom.value); } } @@ -936,9 +938,7 @@ public class WebSettings { * WebView. * @param l A LayoutAlgorithm enum specifying the algorithm to use. * @see WebSettings.LayoutAlgorithm - * @deprecated This method is now obsolete. */ - @Deprecated public synchronized void setLayoutAlgorithm(LayoutAlgorithm l) { // XXX: This will only be affective if libwebcore was built with // ANDROID_LAYOUT defined. @@ -953,9 +953,7 @@ public class WebSettings { * @return LayoutAlgorithm enum value describing the layout algorithm * being used. * @see WebSettings.LayoutAlgorithm - * @deprecated This method is now obsolete. */ - @Deprecated public synchronized LayoutAlgorithm getLayoutAlgorithm() { return mLayoutAlgorithm; } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 8aafc3d078fe..f18a39679c9b 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -17,8 +17,10 @@ package android.webkit; import android.content.Context; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Paint.Style; import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.os.Bundle; @@ -49,6 +51,7 @@ import android.view.ViewGroup; import android.view.inputmethod.EditorInfo; import android.view.inputmethod.InputConnection; import android.view.inputmethod.InputMethodManager; +import android.widget.AbsoluteLayout; import android.widget.AbsoluteLayout.LayoutParams; import android.widget.AdapterView; import android.widget.ArrayAdapter; @@ -71,6 +74,9 @@ import java.util.ArrayList; static final String LOGTAG = "webtextview"; + private Paint mRingPaint; + private int mRingInset; + private WebView mWebView; private boolean mSingle; private int mWidthSpec; @@ -201,7 +207,13 @@ import java.util.ArrayList; } } }; + float ringWidth = 4f * context.getResources().getDisplayMetrics().density; mReceiver = new MyResultReceiver(mHandler); + mRingPaint = new Paint(); + mRingPaint.setColor(0x6633b5e5); + mRingPaint.setStrokeWidth(ringWidth); + mRingPaint.setStyle(Style.FILL); + mRingInset = (int) ringWidth; } public void setAutoFillable(int queryId) { @@ -211,6 +223,40 @@ import java.util.ArrayList; } @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + if (isFocused()) { + final int ib = getHeight() - mRingInset; + canvas.drawRect(0, 0, getWidth(), mRingInset, mRingPaint); + canvas.drawRect(0, ib, getWidth(), getHeight(), mRingPaint); + canvas.drawRect(0, mRingInset, mRingInset, ib, mRingPaint); + canvas.drawRect(getWidth() - mRingInset, mRingInset, getWidth(), ib, mRingPaint); + } + } + + private void growOrShrink(boolean grow) { + AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams) getLayoutParams(); + if (grow) { + Log.i("webtextview", "grow"); + lp.x -= mRingInset; + lp.y -= mRingInset; + lp.width += 2 * mRingInset; + lp.height += 2 * mRingInset; + setPadding(getPaddingLeft() + mRingInset, getPaddingTop() + mRingInset, + getPaddingRight() + mRingInset, getPaddingBottom() + mRingInset); + } else { + Log.i("webtextview", "shrink"); + lp.x += mRingInset; + lp.y += mRingInset; + lp.width -= 2 * mRingInset; + lp.height -= 2 * mRingInset; + setPadding(getPaddingLeft() - mRingInset, getPaddingTop() - mRingInset, + getPaddingRight() - mRingInset, getPaddingBottom() - mRingInset); + } + setLayoutParams(lp); + } + + @Override public boolean dispatchKeyEvent(KeyEvent event) { if (event.isSystem()) { return super.dispatchKeyEvent(event); @@ -511,6 +557,7 @@ import java.util.ArrayList; } else if (!mInsideRemove) { mWebView.setActive(false); } + growOrShrink(focused); mFromFocusChange = false; } @@ -567,7 +614,6 @@ import java.util.ArrayList; mPreChange.substring(0, mMaxLength).equals(postChange))) { return; } - mPreChange = postChange; if (0 == count) { if (before > 0) { // For this and all changes to the text, update our cache @@ -605,9 +651,9 @@ import java.util.ArrayList; // Prefer sending javascript events, so when adding one character, // don't replace the unchanged text. if (count > 1 && before == count - 1) { - String replaceButOne = s.subSequence(start, + String replaceButOne = mPreChange.subSequence(start, start + before).toString(); - String replacedString = getText().subSequence(start, + String replacedString = s.subSequence(start, start + before).toString(); if (replaceButOne.equals(replacedString)) { // we're just adding one character @@ -616,6 +662,7 @@ import java.util.ArrayList; count = 1; } } + mPreChange = postChange; // Find the last character being replaced. If it can be represented by // events, we will pass them to native so we can see javascript events. // Otherwise, replace the text being changed in the textfield. diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index bb0be34331d0..1f948b1bae1e 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; @@ -119,6 +122,11 @@ import java.util.Vector; import java.util.regex.Matcher; import java.util.regex.Pattern; +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; +import static javax.microedition.khronos.egl.EGL10.*; + /** * <p>A View that displays web pages. This class is the basis upon which you * can roll your own web browser or simply display some online content within your Activity. @@ -361,6 +369,7 @@ public class WebView extends AbsoluteLayout private final Rect mGLRectViewport = new Rect(); private final Rect mViewRectViewport = new Rect(); + private final RectF mVisibleContentRect = new RectF(); private boolean mGLViewportEmpty = false; /** @@ -733,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; @@ -788,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, @@ -909,6 +921,9 @@ public class WebView extends AbsoluteLayout private Rect mScrollingLayerBounds = new Rect(); private boolean mSentAutoScrollMessage = false; + // Temporary hack to work around the context removal upon memory pressure + private static boolean mIncrementEGLContextHack = false; + // used for serializing asynchronously handled touch events. private final TouchEventQueue mTouchEventQueue = new TouchEventQueue(); @@ -927,7 +942,11 @@ public class WebView extends AbsoluteLayout * Notify the listener that the picture has changed. * @param view The WebView that owns the picture. * @param picture The new picture. - * @deprecated This method is now obsolete. + * @deprecated Due to internal changes, the picture does not include + * composited layers such as fixed position elements or scrollable divs. + * While the PictureListener API can still be used to detect changes in + * the WebView content, you are advised against its usage until a replacement + * is provided in a future Android release */ @Deprecated public void onNewPicture(WebView view, Picture picture); @@ -1287,8 +1306,15 @@ public class WebView extends AbsoluteLayout if (AccessibilityManager.getInstance(mContext).isEnabled() && getSettings().getJavaScriptEnabled()) { // exposing the TTS for now ... - mTextToSpeech = new TextToSpeech(getContext(), null); - addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE); + final Context ctx = getContext(); + if (ctx != null) { + final String packageName = ctx.getPackageName(); + if (packageName != null) { + mTextToSpeech = new TextToSpeech(getContext(), null, null, + packageName + ".**webview**"); + addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE); + } + } } } @@ -1341,9 +1367,13 @@ public class WebView extends AbsoluteLayout } } - /* package */void updateDefaultZoomDensity(int zoomDensity) { + /* package */ void adjustDefaultZoomDensity(int zoomDensity) { final float density = mContext.getResources().getDisplayMetrics().density * 100 / zoomDensity; + updateDefaultZoomDensity(density); + } + + /* package */ void updateDefaultZoomDensity(float density) { mNavSlop = (int) (16 * density); mZoomManager.updateDefaultZoomDensity(density); } @@ -1605,6 +1635,14 @@ public class WebView extends AbsoluteLayout clearTextEntry(); clearActionModes(); dismissFullScreenMode(); + cancelSelectDialog(); + } + + private void cancelSelectDialog() { + if (mListBoxDialog != null) { + mListBoxDialog.cancel(); + mListBoxDialog = null; + } } /** @@ -2469,7 +2507,9 @@ public class WebView extends AbsoluteLayout * Set the initial scale for the WebView. 0 means default. If * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the * way. Otherwise it starts with 100%. If initial scale is greater than 0, - * WebView starts will this value as initial scale. + * WebView starts with this value as initial scale. + * Please note that unlike the scale properties in the viewport meta tag, + * this method doesn't take the screen density into account. * * @param scaleInPercent The initial scale in percent. */ @@ -3254,6 +3294,8 @@ public class WebView extends AbsoluteLayout if (mNativeClass != 0) { nativeSetPauseDrawing(mNativeClass, true); } + + cancelSelectDialog(); } } @@ -4224,6 +4266,13 @@ public class WebView extends AbsoluteLayout } if (canvas.isHardwareAccelerated()) { + if (mIncrementEGLContextHack == false) { + mIncrementEGLContextHack = true; + EGL10 egl = (EGL10) EGLContext.getEGL(); + EGLDisplay eglDisplay = egl.eglGetDisplay(EGL_DEFAULT_DISPLAY); + int[] version = new int[2]; + egl.eglInitialize(eglDisplay, version); + } mZoomManager.setHardwareAccelerated(); } @@ -4591,11 +4640,14 @@ public class WebView extends AbsoluteLayout + " extras=" + extras); } + calcOurContentVisibleRectF(mVisibleContentRect); if (canvas.isHardwareAccelerated()) { - int functor = nativeGetDrawGLFunction(mNativeClass, - mGLViewportEmpty ? null : mGLRectViewport, mGLViewportEmpty ? null : mViewRectViewport, getScale(), extras); - ((HardwareCanvas) canvas).callDrawGLFunction(functor); + Rect glRectViewport = mGLViewportEmpty ? null : mGLRectViewport; + Rect viewRectViewport = mGLViewportEmpty ? null : mViewRectViewport; + int functor = nativeGetDrawGLFunction(mNativeClass, glRectViewport, + viewRectViewport, mVisibleContentRect, getScale(), extras); + ((HardwareCanvas) canvas).callDrawGLFunction(functor); if (mHardwareAccelSkia != getSettings().getHardwareAccelSkiaEnabled()) { mHardwareAccelSkia = getSettings().getHardwareAccelSkiaEnabled(); nativeUseHardwareAccelSkia(mHardwareAccelSkia); @@ -4611,7 +4663,8 @@ public class WebView extends AbsoluteLayout canvas.setDrawFilter(df); // XXX: Revisit splitting content. Right now it causes a // synchronization problem with layers. - int content = nativeDraw(canvas, color, extras, false); + int content = nativeDraw(canvas, mVisibleContentRect, color, + extras, false); canvas.setDrawFilter(null); if (!mBlockWebkitViewMessages && content != 0) { mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0); @@ -5649,13 +5702,13 @@ public class WebView extends AbsoluteLayout if (hasFocus()) { // If our window regained focus, and we have focus, then begin // drawing the cursor ring - mDrawCursorRing = true; + mDrawCursorRing = !inEditingMode(); setFocusControllerActive(true); } else { + mDrawCursorRing = false; if (!inEditingMode()) { // If our window gained focus, but we do not have it, do not // draw the cursor ring. - mDrawCursorRing = false; setFocusControllerActive(false); } // We do not call recordButtons here because we assume @@ -5730,7 +5783,7 @@ public class WebView extends AbsoluteLayout // When we regain focus, if we have window focus, resume drawing // the cursor ring if (hasWindowFocus()) { - mDrawCursorRing = true; + mDrawCursorRing = !inEditingMode(); setFocusControllerActive(true); //} else { // The WebView has gained focus while we do not have @@ -5740,8 +5793,8 @@ public class WebView extends AbsoluteLayout } else { // When we lost focus, unless focus went to the TextView (which is // true if we are in editing mode), stop drawing the cursor ring. + mDrawCursorRing = false; if (!inEditingMode()) { - mDrawCursorRing = false; setFocusControllerActive(false); } mKeysPressed.clear(); @@ -5767,8 +5820,9 @@ public class WebView extends AbsoluteLayout } else { mGLViewportEmpty = true; } + calcOurContentVisibleRectF(mVisibleContentRect); nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport, - mGLViewportEmpty ? null : mViewRectViewport); + mGLViewportEmpty ? null : mViewRectViewport, mVisibleContentRect); } /** @@ -8401,6 +8455,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; @@ -8415,7 +8474,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; @@ -8765,7 +8828,6 @@ public class WebView extends AbsoluteLayout mSendScrollEvent = false; recordNewContentSize(draw.mContentSize.x, draw.mContentSize.y, updateLayout); - if (isPictureAfterFirstLayout) { // Reset the last sent data here since dealing with new page. mLastWidthSent = 0; @@ -9336,7 +9398,8 @@ public class WebView extends AbsoluteLayout * @hide only needs to be accessible to Browser and testing */ public void drawPage(Canvas canvas) { - nativeDraw(canvas, 0, 0, false); + calcOurContentVisibleRectF(mVisibleContentRect); + nativeDraw(canvas, mVisibleContentRect, 0, 0, false); } /** @@ -9444,7 +9507,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(); @@ -9467,13 +9530,14 @@ public class WebView extends AbsoluteLayout * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the * native allocation can be freed. */ - private native int nativeDraw(Canvas canvas, int color, int extra, - boolean splitIfNeeded); + private native int nativeDraw(Canvas canvas, RectF visibleRect, + int color, int extra, boolean splitIfNeeded); private native void nativeDumpDisplayTree(String urlOrNull); private native boolean nativeEvaluateLayersAnimations(int nativeInstance); private native int nativeGetDrawGLFunction(int nativeInstance, Rect rect, - Rect viewRect, float scale, int extras); - private native void nativeUpdateDrawGLFunction(Rect rect, Rect viewRect); + Rect viewRect, RectF visibleRect, float scale, int extras); + private native void nativeUpdateDrawGLFunction(Rect rect, Rect viewRect, + RectF visibleRect); private native void nativeExtendSelection(int x, int y); private native int nativeFindAll(String findLower, String findUpper, boolean sameAsLastSearch); @@ -9514,7 +9578,6 @@ public class WebView extends AbsoluteLayout private native void nativeHideCursor(); private native boolean nativeHitSelection(int x, int y); private native String nativeImageURI(int x, int y); - private native void nativeInstrumentReport(); private native Rect nativeLayerBounds(int layer); /* package */ native boolean nativeMoveCursorToNextTextInput(); // return true if the page has been scrolled diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 3d85482e675c..42b9eab99996 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -594,8 +594,6 @@ public final class WebViewCore { private native void nativeDumpNavTree(); - private native void nativeDumpV8Counters(); - private native void nativeSetJsFlags(String flags); /** @@ -2331,6 +2329,10 @@ public final class WebViewCore { adjust = (float) mContext.getResources().getDisplayMetrics().densityDpi / mViewportDensityDpi; } + if (adjust != mWebView.getDefaultZoomScale()) { + Message.obtain(mWebView.mPrivateHandler, + WebView.UPDATE_ZOOM_DENSITY, adjust).sendToTarget(); + } int defaultScale = (int) (adjust * 100); if (mViewportInitialScale > 0) { @@ -2541,7 +2543,7 @@ public final class WebViewCore { // called by JNI private void restoreScale(float scale, float textWrapScale) { if (mBrowserFrame.firstLayoutDone() == false) { - mIsRestored = scale > 0; + mIsRestored = true; mRestoredScale = scale; if (mSettings.getUseWideViewPort()) { mRestoredTextWrapScale = textWrapScale; diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index e1392ae14225..695c154acba0 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -42,7 +42,7 @@ public class WebViewDatabase { // log tag protected static final String LOGTAG = "webviewdatabase"; - private static final int DATABASE_VERSION = 10; + private static final int DATABASE_VERSION = 11; // 2 -> 3 Modified Cache table to allow cache of redirects // 3 -> 4 Added Oma-Downloads table // 4 -> 5 Modified Cache table to support persistent contentLength @@ -52,6 +52,9 @@ public class WebViewDatabase { // 7 -> 8 Move cache to its own db // 8 -> 9 Store both scheme and host when storing passwords // 9 -> 10 Update httpauth table UNIQUE + // 10 -> 11 Drop cookies and cache now managed by the chromium stack, + // and update the form data table to use the new format + // implemented for b/5265606. private static final int CACHE_DATABASE_VERSION = 4; // 1 -> 2 Add expires String // 2 -> 3 Add content-disposition @@ -204,7 +207,9 @@ public class WebViewDatabase { } initDatabase(context); - if (!JniUtil.useChromiumHttpStack()) { + if (JniUtil.useChromiumHttpStack()) { + context.deleteDatabase(CACHE_DATABASE_FILE); + } else { initCacheDatabase(context); } @@ -327,15 +332,59 @@ public class WebViewDatabase { } private static void upgradeDatabase() { + upgradeDatabaseToV10(); + upgradeDatabaseFromV10ToV11(); + // Add future database upgrade functions here, one version at a + // time. + mDatabase.setVersion(DATABASE_VERSION); + } + + private static void upgradeDatabaseFromV10ToV11() { + int oldVersion = mDatabase.getVersion(); + + if (oldVersion >= 11) { + // Nothing to do. + return; + } + + if (JniUtil.useChromiumHttpStack()) { + // Clear out old java stack cookies - this data is now stored in + // a separate database managed by the Chrome stack. + mDatabase.execSQL("DROP TABLE IF EXISTS " + mTableNames[TABLE_COOKIES_ID]); + + // Likewise for the old cache table. + mDatabase.execSQL("DROP TABLE IF EXISTS cache"); + } + + // Update form autocomplete URLs to match new ICS formatting. + Cursor c = mDatabase.query(mTableNames[TABLE_FORMURL_ID], null, null, + null, null, null, null); + while (c.moveToNext()) { + String urlId = Long.toString(c.getLong(c.getColumnIndex(ID_COL))); + String url = c.getString(c.getColumnIndex(FORMURL_URL_COL)); + ContentValues cv = new ContentValues(1); + cv.put(FORMURL_URL_COL, WebTextView.urlForAutoCompleteData(url)); + mDatabase.update(mTableNames[TABLE_FORMURL_ID], cv, ID_COL + "=?", + new String[] { urlId }); + } + c.close(); + } + + private static void upgradeDatabaseToV10() { int oldVersion = mDatabase.getVersion(); + + if (oldVersion >= 10) { + // Nothing to do. + return; + } + if (oldVersion != 0) { Log.i(LOGTAG, "Upgrading database from version " + oldVersion + " to " + DATABASE_VERSION + ", which will destroy old data"); } - boolean justPasswords = 8 == oldVersion && 9 == DATABASE_VERSION; - boolean justAuth = 9 == oldVersion && 10 == DATABASE_VERSION; - if (justAuth) { + + if (9 == oldVersion) { mDatabase.execSQL("DROP TABLE IF EXISTS " + mTableNames[TABLE_HTTPAUTH_ID]); mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] @@ -348,55 +397,49 @@ public class WebViewDatabase { return; } - if (!justPasswords) { - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_COOKIES_ID]); - mDatabase.execSQL("DROP TABLE IF EXISTS cache"); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_FORMURL_ID]); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_FORMDATA_ID]); - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_HTTPAUTH_ID]); - } + mDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_COOKIES_ID]); + mDatabase.execSQL("DROP TABLE IF EXISTS cache"); + mDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_FORMURL_ID]); + mDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_FORMDATA_ID]); + mDatabase.execSQL("DROP TABLE IF EXISTS " + + mTableNames[TABLE_HTTPAUTH_ID]); mDatabase.execSQL("DROP TABLE IF EXISTS " + mTableNames[TABLE_PASSWORD_ID]); - mDatabase.setVersion(DATABASE_VERSION); - - if (!justPasswords) { - // cookies - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_COOKIES_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + COOKIES_NAME_COL + " TEXT, " + COOKIES_VALUE_COL - + " TEXT, " + COOKIES_DOMAIN_COL + " TEXT, " - + COOKIES_PATH_COL + " TEXT, " + COOKIES_EXPIRES_COL - + " INTEGER, " + COOKIES_SECURE_COL + " INTEGER" + ");"); - mDatabase.execSQL("CREATE INDEX cookiesIndex ON " - + mTableNames[TABLE_COOKIES_ID] + " (path)"); - - // formurl - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL - + " TEXT" + ");"); - - // formdata - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL - + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE (" - + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", " - + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);"); + // cookies + mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_COOKIES_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + COOKIES_NAME_COL + " TEXT, " + COOKIES_VALUE_COL + + " TEXT, " + COOKIES_DOMAIN_COL + " TEXT, " + + COOKIES_PATH_COL + " TEXT, " + COOKIES_EXPIRES_COL + + " INTEGER, " + COOKIES_SECURE_COL + " INTEGER" + ");"); + mDatabase.execSQL("CREATE INDEX cookiesIndex ON " + + mTableNames[TABLE_COOKIES_ID] + " (path)"); + + // formurl + mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMURL_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + FORMURL_URL_COL + + " TEXT" + ");"); + + // formdata + mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_FORMDATA_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + FORMDATA_URLID_COL + " INTEGER, " + FORMDATA_NAME_COL + + " TEXT, " + FORMDATA_VALUE_COL + " TEXT," + " UNIQUE (" + + FORMDATA_URLID_COL + ", " + FORMDATA_NAME_COL + ", " + + FORMDATA_VALUE_COL + ") ON CONFLICT IGNORE);"); - // httpauth - mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] - + " (" + ID_COL + " INTEGER PRIMARY KEY, " - + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL - + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " - + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" - + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL - + ") ON CONFLICT REPLACE);"); - } + // httpauth + mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_HTTPAUTH_ID] + + " (" + ID_COL + " INTEGER PRIMARY KEY, " + + HTTPAUTH_HOST_COL + " TEXT, " + HTTPAUTH_REALM_COL + + " TEXT, " + HTTPAUTH_USERNAME_COL + " TEXT, " + + HTTPAUTH_PASSWORD_COL + " TEXT," + " UNIQUE (" + + HTTPAUTH_HOST_COL + ", " + HTTPAUTH_REALM_COL + + ") ON CONFLICT REPLACE);"); // passwords mDatabase.execSQL("CREATE TABLE " + mTableNames[TABLE_PASSWORD_ID] + " (" + ID_COL + " INTEGER PRIMARY KEY, " @@ -411,7 +454,7 @@ public class WebViewDatabase { if (oldVersion != 0) { Log.i(LOGTAG, "Upgrading cache database from version " + oldVersion + " to " - + DATABASE_VERSION + ", which will destroy all old data"); + + CACHE_DATABASE_VERSION + ", which will destroy all old data"); } mCacheDatabase.execSQL("DROP TABLE IF EXISTS cache"); mCacheDatabase.setVersion(CACHE_DATABASE_VERSION); @@ -1150,7 +1193,7 @@ public class WebViewDatabase { cursor = mDatabase.query(mTableNames[TABLE_FORMURL_ID], ID_PROJECTION, urlSelection, new String[] { url }, null, null, null); - if (cursor.moveToFirst()) { + while (cursor.moveToNext()) { long urlid = cursor.getLong(cursor.getColumnIndex(ID_COL)); Cursor dataCursor = null; try { diff --git a/core/java/android/webkit/ZoomManager.java b/core/java/android/webkit/ZoomManager.java index f599dbae057c..cf2c8a6778d7 100644 --- a/core/java/android/webkit/ZoomManager.java +++ b/core/java/android/webkit/ZoomManager.java @@ -349,7 +349,7 @@ class ZoomManager { } public final void setInitialScaleInPercent(int scaleInPercent) { - mInitialScale = scaleInPercent * mDisplayDensity * 0.01f; + mInitialScale = scaleInPercent * 0.01f; } public final float computeScaleWithLimits(float scale) { @@ -498,6 +498,11 @@ class ZoomManager { if (mZoomScale == 0) { // We've reached the end of the zoom animation. mInHWAcceleratedZoom = false; + + // Ensure that the zoom level is pushed to WebCore. This has not + // yet occurred because we prevent it from happening while + // mInHWAcceleratedZoom is true. + mWebView.sendViewSizeZoom(false); } } else { canvas.translate(tx, ty); 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/android/widget/TextView.java b/core/java/android/widget/TextView.java index c5421dc11f54..ee5fa64a78b8 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -3799,7 +3799,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return; } } - + // This is the handling for some default action. // Note that for backwards compatibility we don't do this // default handling if explicit ime options have not been given, @@ -5608,6 +5608,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener outAttrs.extras = mInputContentType.extras; } else { outAttrs.imeOptions = EditorInfo.IME_NULL; + // May not be defined otherwise and needed by onEditorAction + mInputContentType = new InputContentType(); } if (focusSearch(FOCUS_DOWN) != null) { outAttrs.imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; 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 e993e2aa195e..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,173 +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); +//HB_ShaperItem TextLayoutEngine::mShaperItem; +//HB_FontRec TextLayoutEngine::mFontRec; +//SkPaint TextLayoutEngine::mShapingPaint; - 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 -} +TextLayoutEngine::TextLayoutEngine() : mShaperItemGlyphArraySize(0), + mShaperItemLogClustersArraySize(0) { + mDefaultTypeface = SkFontHost::CreateTypeface(NULL, NULL, NULL, 0, SkTypeface::kNormal); + mArabicTypeface = NULL; + mHebrewRegularTypeface = NULL; + mHebrewBoldTypeface = NULL; -size_t TextLayoutCacheValue::getSize() const { - return sizeof(TextLayoutCacheValue) + sizeof(jfloat) * mAdvances.capacity() + - sizeof(jchar) * mGlyphs.capacity(); -} - -void TextLayoutCacheValue::initShaperItem(HB_ShaperItem& shaperItem, HB_FontRec* font, - FontData* fontData, SkPaint* paint, const UChar* chars, size_t count) { - font->klass = &harfbuzzSkiaClass; - font->userData = 0; + 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; - - // Define font data - fontData->textSize = paint->getTextSize(); - fontData->textSkewX = paint->getTextSkewX(); - fontData->textScaleX = paint->getTextScaleX(); - fontData->flags = paint->getFlags(); - fontData->hinting = paint->getHinting(); - - shaperItem.font = font; - shaperItem.font->userData = fontData; - - // We cannot know, ahead of time, how many glyphs a given script run - // will produce. We take a guess that script runs will not produce more - // than twice as many glyphs as there are code points plus a bit of - // padding and fallback if we find that we are wrong. - createGlyphArrays(shaperItem, (count + 2) * 2); + mFontRec.x_ppem = 1; + mFontRec.y_ppem = 1; + mFontRec.x_scale = 1; + mFontRec.y_scale = 1; - // Create log clusters array - shaperItem.log_clusters = new unsigned short[count]; + memset(&mShaperItem, 0, sizeof(mShaperItem)); - // 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); +void TextLayoutEngine::computeValues(TextLayoutCacheValue* value, SkPaint* paint, const UChar* chars, + size_t start, size_t count, size_t contextCount, int dirFlags) { -#if DEBUG_GLYPHS - LOGD("Run typeFace = %p", data->typeFace); - LOGD("Run typeFace->uniqueID = %d", data->typeFace->uniqueID()); + 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 - - // Shape - while (!HB_ShapeItem(&shaperItem)) { - // We overflowed our arrays. Resize and retry. - // HB_ShapeItem fills in shaperItem.num_glyphs with the needed size. - deleteGlyphArrays(shaperItem); - createGlyphArrays(shaperItem, shaperItem.num_glyphs << 1); - } - - return result; } -SkTypeface* TextLayoutCacheValue::getCachedTypeface(SkTypeface* typeface, const char path[]) { - if (!typeface) { - typeface = SkTypeface::CreateFromFile(path); - } - 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) { @@ -507,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) { @@ -624,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); @@ -670,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; @@ -696,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 @@ -705,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); } @@ -730,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 TextLayoutCacheValue::createGlyphArrays(HB_ShaperItem& shaperItem, int size) { +void TextLayoutEngine::ensureShaperItemGlyphArrays(size_t size) { + if (size > mShaperItemGlyphArraySize) { + deleteShaperItemGlyphArrays(); + createShaperItemGlyphArrays(size); + } +} + +void TextLayoutEngine::createShaperItemGlyphArrays(size_t size) { #if DEBUG_GLYPHS LOGD("createGlyphArrays -- size=%d", size); #endif - shaperItem.glyphs = new HB_Glyph[size]; - shaperItem.attributes = new HB_GlyphAttributes[size]; - shaperItem.advances = new HB_Fixed[size]; - shaperItem.offsets = new HB_FixedPoint[size]; - shaperItem.num_glyphs = size; + mShaperItemGlyphArraySize = size; + mShaperItem.glyphs = new HB_Glyph[size]; + mShaperItem.attributes = new HB_GlyphAttributes[size]; + mShaperItem.advances = new HB_Fixed[size]; + mShaperItem.offsets = new HB_FixedPoint[size]; +} + +void TextLayoutEngine::deleteShaperItemGlyphArrays() { + delete[] mShaperItem.glyphs; + delete[] mShaperItem.attributes; + delete[] mShaperItem.advances; + delete[] mShaperItem.offsets; +} + +void TextLayoutEngine::ensureShaperItemLogClustersArray(size_t size) { + if (size > mShaperItemLogClustersArraySize) { + deleteShaperItemLogClustersArray(); + createShaperItemLogClustersArray(size); + } +} + +void TextLayoutEngine::createShaperItemLogClustersArray(size_t size) { +#if DEBUG_GLYPHS + LOGD("createLogClustersArray -- size=%d", size); +#endif + mShaperItemLogClustersArraySize = size; + mShaperItem.log_clusters = new unsigned short[size]; +} + +void TextLayoutEngine::deleteShaperItemLogClustersArray() { + delete[] mShaperItem.log_clusters; +} + +SkTypeface* TextLayoutEngine::getCachedTypeface(SkTypeface** typeface, const char path[]) { + if (!*typeface) { + *typeface = SkTypeface::CreateFromFile(path); + (*typeface)->ref(); +#if DEBUG_GLYPHS + LOGD("Created SkTypeface from file: %s", path); +#endif + } + return *typeface; +} + +HB_Face TextLayoutEngine::getCachedHBFace(SkTypeface* typeface) { + SkFontID fontId = typeface->uniqueID(); + ssize_t index = mCachedHBFaces.indexOfKey(fontId); + if (index >= 0) { + return mCachedHBFaces.valueAt(index); + } + HB_Face face = HB_NewFace(typeface, harfbuzzSkiaGetTable); + if (face) { +#if DEBUG_GLYPHS + LOGD("Created HB_NewFace %p from paint typeface: %p", face, typeface); +#endif + mCachedHBFaces.add(fontId, face); + } + return face; } } // namespace android diff --git a/core/jni/android/graphics/TextLayoutCache.h b/core/jni/android/graphics/TextLayoutCache.h index 06c74fcecefd..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/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp index aba3a72d8b6f..b9f373882440 100644 --- a/core/jni/android_view_KeyCharacterMap.cpp +++ b/core/jni/android_view_KeyCharacterMap.cpp @@ -35,18 +35,25 @@ static struct { } gFallbackActionClassInfo; -static jint nativeLoad(JNIEnv *env, jobject clazz, jint deviceId) { +static jint nativeLoad(JNIEnv *env, jobject clazz, jstring fileStr) { + const char* file = env->GetStringUTFChars(fileStr, NULL); + KeyCharacterMap* map; - status_t status = KeyCharacterMap::loadByDeviceId(deviceId, &map); + status_t status = KeyCharacterMap::load(String8(file), &map); + jint result; if (status) { String8 msg; - msg.appendFormat("Could not load key character map for device %d due to error %d. " - "Refer to the log for details.", deviceId, status); + msg.appendFormat("Could not load key character map '%s' due to error %d. " + "Refer to the log for details.", file, status); jniThrowException(env, "android/view/KeyCharacterMap$KeyCharacterMapUnavailableException", msg.string()); - return 0; + result = 0; + } else { + result = reinterpret_cast<jint>(map); } - return reinterpret_cast<jint>(map); + + env->ReleaseStringUTFChars(fileStr, file); + return result; } static void nativeDispose(JNIEnv *env, jobject clazz, jint ptr) { @@ -141,7 +148,7 @@ static jobjectArray nativeGetEvents(JNIEnv *env, jobject clazz, jint ptr, jint d static JNINativeMethod g_methods[] = { /* name, signature, funcPtr */ - { "nativeLoad", "(I)I", + { "nativeLoad", "(Ljava/lang/String;)I", (void*)nativeLoad }, { "nativeDispose", "(I)V", (void*)nativeDispose }, diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 230df39f0840..97658a1186af 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -263,8 +263,7 @@ android:label="@string/permlab_writeProfile" android:description="@string/permdesc_writeProfile" /> - <!-- Allows an application to read from the user's social stream. - @hide --> + <!-- Allows an application to read from the user's social stream. --> <permission android:name="android.permission.READ_SOCIAL_STREAM" android:permissionGroup="android.permission-group.PERSONAL_INFO" android:protectionLevel="dangerous" @@ -272,8 +271,7 @@ android:description="@string/permdesc_readSocialStream" /> <!-- Allows an application to write (but not read) the user's - social stream data. - @hide --> + social stream data. --> <permission android:name="android.permission.WRITE_SOCIAL_STREAM" android:permissionGroup="android.permission-group.PERSONAL_INFO" android:protectionLevel="dangerous" diff --git a/core/res/res/layout/media_controller.xml b/core/res/res/layout/media_controller.xml index 582101a4959a..313806cdaa8e 100644 --- a/core/res/res/layout/media_controller.xml +++ b/core/res/res/layout/media_controller.xml @@ -17,13 +17,9 @@ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="#CC666666" + android:background="#CC000000" android:orientation="vertical"> - <ImageView android:layout_width="match_parent" - android:layout_height="1px" - android:background="@android:drawable/divider_horizontal_dark" /> - <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" diff --git a/core/res/res/values-ar-rEG/donottranslate-cldr.xml b/core/res/res/values-ar-rEG/donottranslate-cldr.xml index 5819afc5227d..57011d7683b2 100644 --- a/core/res/res/values-ar-rEG/donottranslate-cldr.xml +++ b/core/res/res/values-ar-rEG/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s، %3$s %2$s - %6$s، %8$s %7$s، %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE، d MMMM yyyy</string> </resources> diff --git a/core/res/res/values-ar/donottranslate-cldr.xml b/core/res/res/values-ar/donottranslate-cldr.xml index d662590d911d..0b9da23b765d 100644 --- a/core/res/res/values-ar/donottranslate-cldr.xml +++ b/core/res/res/values-ar/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s، %3$s %2$s - %6$s، %8$s %7$s، %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E d MMMM</string> + <string name="abbrev_wday_month_day_no_year">E d MMMM</string> <string name="abbrev_wday_month_day_year">EEE، d MMMM y</string> </resources> diff --git a/core/res/res/values-bg/donottranslate-cldr.xml b/core/res/res/values-bg/donottranslate-cldr.xml index dc8b3ad2b5ef..972d661af4f7 100644 --- a/core/res/res/values-bg/donottranslate-cldr.xml +++ b/core/res/res/values-bg/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %9$s, %1$s - %8$s %7$s y, %6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">d MMMM, EEEE</string> + <string name="abbrev_wday_month_day_no_year">d MMMM, EEE</string> <string name="abbrev_wday_month_day_year">d MMM yyyy, E</string> </resources> diff --git a/core/res/res/values-ca-rES/donottranslate-cldr.xml b/core/res/res/values-ca-rES/donottranslate-cldr.xml index 52c33d8059f8..113133dc71e4 100644 --- a/core/res/res/values-ca-rES/donottranslate-cldr.xml +++ b/core/res/res/values-ca-rES/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s - %6$s %8$s de %7$s de %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM yyyy</string> </resources> diff --git a/core/res/res/values-ca/donottranslate-cldr.xml b/core/res/res/values-ca/donottranslate-cldr.xml index a77aa383d23c..03cf69dcfd56 100644 --- a/core/res/res/values-ca/donottranslate-cldr.xml +++ b/core/res/res/values-ca/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s - %6$s %8$s de %7$s de %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E d MMMM</string> + <string name="abbrev_wday_month_day_no_year">E d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM y</string> </resources> diff --git a/core/res/res/values-cs/donottranslate-cldr.xml b/core/res/res/values-cs/donottranslate-cldr.xml index a0267348c8a0..59b82d33be84 100644 --- a/core/res/res/values-cs/donottranslate-cldr.xml +++ b/core/res/res/values-cs/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s - %6$s %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d. MMMM</string> <string name="abbrev_wday_month_day_year">E d. MMMM yyyy</string> </resources> diff --git a/core/res/res/values-da/donottranslate-cldr.xml b/core/res/res/values-da/donottranslate-cldr.xml index b0783852cb41..1dfc96cff71b 100644 --- a/core/res/res/values-da/donottranslate-cldr.xml +++ b/core/res/res/values-da/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s d. %3$s. %2$s - %6$s d. %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE d. MMM yyyy</string> </resources> diff --git a/core/res/res/values-de/donottranslate-cldr.xml b/core/res/res/values-de/donottranslate-cldr.xml index 6b4bf5c6fa7c..8b834db65b2e 100644 --- a/core/res/res/values-de/donottranslate-cldr.xml +++ b/core/res/res/values-de/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d. MMM yyyy</string> </resources> diff --git a/core/res/res/values-el/donottranslate-cldr.xml b/core/res/res/values-el/donottranslate-cldr.xml index 75c11b90abf8..1e7caa4154c2 100644 --- a/core/res/res/values-el/donottranslate-cldr.xml +++ b/core/res/res/values-el/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-en-rAU/donottranslate-cldr.xml b/core/res/res/values-en-rAU/donottranslate-cldr.xml index 02c4ece808df..557833ec496f 100644 --- a/core/res/res/values-en-rAU/donottranslate-cldr.xml +++ b/core/res/res/values-en-rAU/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-en-rCA/donottranslate-cldr.xml b/core/res/res/values-en-rCA/donottranslate-cldr.xml index 446855e8c09b..2aa7cbe58022 100644 --- a/core/res/res/values-en-rCA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rCA/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s - %6$s, %7$s %8$s, %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE, MMMM d</string> <string name="abbrev_wday_month_day_year">E, MMM d, yyyy</string> </resources> diff --git a/core/res/res/values-en-rGB/donottranslate-cldr.xml b/core/res/res/values-en-rGB/donottranslate-cldr.xml index 0879d0f7b4dd..cce895ed8cd1 100644 --- a/core/res/res/values-en-rGB/donottranslate-cldr.xml +++ b/core/res/res/values-en-rGB/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-en-rIE/donottranslate-cldr.xml b/core/res/res/values-en-rIE/donottranslate-cldr.xml index b611f7a0a3f1..46b13ec9470b 100644 --- a/core/res/res/values-en-rIE/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIE/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E d MMM yyyy</string> </resources> diff --git a/core/res/res/values-en-rIN/donottranslate-cldr.xml b/core/res/res/values-en-rIN/donottranslate-cldr.xml index 0a8ae5a3e7cf..177bd7ed0f9b 100644 --- a/core/res/res/values-en-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-en-rIN/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E d MMM yyyy</string> </resources> diff --git a/core/res/res/values-en-rNZ/donottranslate-cldr.xml b/core/res/res/values-en-rNZ/donottranslate-cldr.xml index 329b2682ae7c..41a00a5cd73d 100644 --- a/core/res/res/values-en-rNZ/donottranslate-cldr.xml +++ b/core/res/res/values-en-rNZ/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-en-rUS/donottranslate-cldr.xml b/core/res/res/values-en-rUS/donottranslate-cldr.xml index a94fb58dea33..15fcd8be989c 100644 --- a/core/res/res/values-en-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-en-rUS/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE, MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, MMM d, yyyy</string> </resources> diff --git a/core/res/res/values-en-rZA/donottranslate-cldr.xml b/core/res/res/values-en-rZA/donottranslate-cldr.xml index 753861175d80..7a5bdbd2a812 100644 --- a/core/res/res/values-en-rZA/donottranslate-cldr.xml +++ b/core/res/res/values-en-rZA/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E dd MMM yyyy</string> </resources> diff --git a/core/res/res/values-es-rUS/donottranslate-cldr.xml b/core/res/res/values-es-rUS/donottranslate-cldr.xml index 1a59c4bc14f6..e2c3254d90a2 100644 --- a/core/res/res/values-es-rUS/donottranslate-cldr.xml +++ b/core/res/res/values-es-rUS/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s al %6$s %8$s de %7$s de %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d \'de\' MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d \'de\' MMMM</string> <string name="abbrev_wday_month_day_year">E d \'de\' MMM \'de\' yyyy</string> </resources> diff --git a/core/res/res/values-es/donottranslate-cldr.xml b/core/res/res/values-es/donottranslate-cldr.xml index 206b56d99d5f..faf171ab3490 100644 --- a/core/res/res/values-es/donottranslate-cldr.xml +++ b/core/res/res/values-es/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s de %2$s – %6$s %8$s de %7$s de %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d \'de\' MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d \'de\' MMMM</string> <string name="abbrev_wday_month_day_year">EEE d \'de\' MMM \'de\' yyyy</string> </resources> diff --git a/core/res/res/values-fa/donottranslate-cldr.xml b/core/res/res/values-fa/donottranslate-cldr.xml index 83f49423d74b..11473fe98c7e 100644 --- a/core/res/res/values-fa/donottranslate-cldr.xml +++ b/core/res/res/values-fa/donottranslate-cldr.xml @@ -131,5 +131,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s LLL تا %6$s %8$s %2$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E d LLLL</string> + <string name="abbrev_wday_month_day_no_year">E d LLLL</string> <string name="abbrev_wday_month_day_year">E d MMM y</string> </resources> diff --git a/core/res/res/values-fi-rFI/donottranslate-cldr.xml b/core/res/res/values-fi-rFI/donottranslate-cldr.xml index ddcc0ea80261..e9e1a25d227f 100644 --- a/core/res/res/values-fi-rFI/donottranslate-cldr.xml +++ b/core/res/res/values-fi-rFI/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s – %6$s %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE d. MMM yyyy</string> </resources> diff --git a/core/res/res/values-fi/donottranslate-cldr.xml b/core/res/res/values-fi/donottranslate-cldr.xml index 5146a0aa2626..71cccf39b732 100644 --- a/core/res/res/values-fi/donottranslate-cldr.xml +++ b/core/res/res/values-fi/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s – %6$s %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">E d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE d. MMM y</string> </resources> diff --git a/core/res/res/values-fr/donottranslate-cldr.xml b/core/res/res/values-fr/donottranslate-cldr.xml index 86b8d19eec41..840f728aff81 100644 --- a/core/res/res/values-fr/donottranslate-cldr.xml +++ b/core/res/res/values-fr/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">du %1$s %3$s %2$s au %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM yyyy</string> </resources> diff --git a/core/res/res/values-hi-rIN/donottranslate-cldr.xml b/core/res/res/values-hi-rIN/donottranslate-cldr.xml index 00cb965e5008..6bfd3de2d704 100644 --- a/core/res/res/values-hi-rIN/donottranslate-cldr.xml +++ b/core/res/res/values-hi-rIN/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-hr-rHR/donottranslate-cldr.xml b/core/res/res/values-hr-rHR/donottranslate-cldr.xml index dfcded5676fd..a601d938f69c 100644 --- a/core/res/res/values-hr-rHR/donottranslate-cldr.xml +++ b/core/res/res/values-hr-rHR/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.%9$s.</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d.MMM.yyyy.</string> </resources> diff --git a/core/res/res/values-hr/donottranslate-cldr.xml b/core/res/res/values-hr/donottranslate-cldr.xml index 23332aa3441e..9b5186284570 100644 --- a/core/res/res/values-hr/donottranslate-cldr.xml +++ b/core/res/res/values-hr/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s.%2$s. - %6$s, %8$s.%7$s.%9$s.</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">E, d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d.MMM.y.</string> </resources> diff --git a/core/res/res/values-hu-rHU/donottranslate-cldr.xml b/core/res/res/values-hu-rHU/donottranslate-cldr.xml index 553667b3c3ad..afb767625d93 100644 --- a/core/res/res/values-hu-rHU/donottranslate-cldr.xml +++ b/core/res/res/values-hu-rHU/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s. %2$s %3$s., %1$s - %7$s %8$s., %6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE MMMM d</string> <string name="abbrev_wday_month_day_year">yyyy. MMM d., E</string> </resources> diff --git a/core/res/res/values-hu/donottranslate-cldr.xml b/core/res/res/values-hu/donottranslate-cldr.xml index 996eefbce4cd..28b65bcf4dd1 100644 --- a/core/res/res/values-hu/donottranslate-cldr.xml +++ b/core/res/res/values-hu/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s. %2$s %3$s., %1$s - %7$s %8$s., %6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">y. MMM d., E</string> </resources> diff --git a/core/res/res/values-in-rID/donottranslate-cldr.xml b/core/res/res/values-in-rID/donottranslate-cldr.xml index 4d4ebb226652..b79fe00bdcb3 100644 --- a/core/res/res/values-in-rID/donottranslate-cldr.xml +++ b/core/res/res/values-in-rID/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE MMMM d</string> <string name="abbrev_wday_month_day_year">E, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-in/donottranslate-cldr.xml b/core/res/res/values-in/donottranslate-cldr.xml index 7a58a198c46f..9a634cc72e64 100644 --- a/core/res/res/values-in/donottranslate-cldr.xml +++ b/core/res/res/values-in/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, y MMM d</string> </resources> diff --git a/core/res/res/values-it/donottranslate-cldr.xml b/core/res/res/values-it/donottranslate-cldr.xml index 8af25f2791bd..8cee828f2faf 100644 --- a/core/res/res/values-it/donottranslate-cldr.xml +++ b/core/res/res/values-it/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM yyyy</string> </resources> diff --git a/core/res/res/values-iw/donottranslate-cldr.xml b/core/res/res/values-iw/donottranslate-cldr.xml index d373a3430b20..02d1e9c2382e 100644 --- a/core/res/res/values-iw/donottranslate-cldr.xml +++ b/core/res/res/values-iw/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, y MMM d</string> </resources> diff --git a/core/res/res/values-ja/donottranslate-cldr.xml b/core/res/res/values-ja/donottranslate-cldr.xml index 7447abcd412f..450adc3309c1 100644 --- a/core/res/res/values-ja/donottranslate-cldr.xml +++ b/core/res/res/values-ja/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s年%2$s%3$s日 (%1$s)~%7$s%8$s日 (%6$s)</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">M月d日 (E)</string> + <string name="abbrev_wday_month_day_no_year">M月d日 (E)</string> <string name="abbrev_wday_month_day_year">yyyy年M月d日 (E)</string> </resources> diff --git a/core/res/res/values-ko/donottranslate-cldr.xml b/core/res/res/values-ko/donottranslate-cldr.xml index cdbd65e62971..5382871c1406 100644 --- a/core/res/res/values-ko/donottranslate-cldr.xml +++ b/core/res/res/values-ko/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s년 %2$s %3$s일 %1$s ~ %7$s %8$s일 %6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">MMMM d일 EEEE</string> + <string name="abbrev_wday_month_day_no_year">MMMM d일 EEE</string> <string name="abbrev_wday_month_day_year">yyyy년 MMM d일 EEE</string> </resources> diff --git a/core/res/res/values-lt-rLT/donottranslate-cldr.xml b/core/res/res/values-lt-rLT/donottranslate-cldr.xml index 982f993a6765..0c1d0aa1a252 100644 --- a/core/res/res/values-lt-rLT/donottranslate-cldr.xml +++ b/core/res/res/values-lt-rLT/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s m. %2$s %3$s d., %1$s-%7$s %8$s d., %6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">MMMM d \'d\'., EEEE</string> + <string name="abbrev_wday_month_day_no_year">MMMM d \'d\'., EEE</string> <string name="abbrev_wday_month_day_year">yyyy \'m\'. MMM d \'d\'., E</string> </resources> diff --git a/core/res/res/values-lt/donottranslate-cldr.xml b/core/res/res/values-lt/donottranslate-cldr.xml index 25458bdee026..8a2c0f4862eb 100644 --- a/core/res/res/values-lt/donottranslate-cldr.xml +++ b/core/res/res/values-lt/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s m. %2$s %3$s d.,%1$s - %7$s %8$s d.,%6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">y \'m\'. MMM d \'d\'.,E</string> </resources> diff --git a/core/res/res/values-lv-rLV/donottranslate-cldr.xml b/core/res/res/values-lv-rLV/donottranslate-cldr.xml index c4000b04344d..9dbc5e052be0 100644 --- a/core/res/res/values-lv-rLV/donottranslate-cldr.xml +++ b/core/res/res/values-lv-rLV/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s. gada %3$s. %2$s-%6$s, y. gada %8$s. %7$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE, yyyy. \'g\'. dd. MMM</string> </resources> diff --git a/core/res/res/values-lv/donottranslate-cldr.xml b/core/res/res/values-lv/donottranslate-cldr.xml index 2de93676d07c..10dcd35752ab 100644 --- a/core/res/res/values-lv/donottranslate-cldr.xml +++ b/core/res/res/values-lv/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s. gada %3$s. %2$s - %6$s, y. gada %8$s. %7$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">E, d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE, yyyy. \'g\'. dd. MMM</string> </resources> diff --git a/core/res/res/values-nb/donottranslate-cldr.xml b/core/res/res/values-nb/donottranslate-cldr.xml index 512eb0146ff5..637dd5e78602 100644 --- a/core/res/res/values-nb/donottranslate-cldr.xml +++ b/core/res/res/values-nb/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s–%6$s %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE d. MMM yyyy</string> </resources> diff --git a/core/res/res/values-nl/donottranslate-cldr.xml b/core/res/res/values-nl/donottranslate-cldr.xml index 3db9e6937e5b..ca3813ffd91f 100644 --- a/core/res/res/values-nl/donottranslate-cldr.xml +++ b/core/res/res/values-nl/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s - %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM yyyy</string> </resources> diff --git a/core/res/res/values-pl/donottranslate-cldr.xml b/core/res/res/values-pl/donottranslate-cldr.xml index baff79815955..2950e60b62f6 100644 --- a/core/res/res/values-pl/donottranslate-cldr.xml +++ b/core/res/res/values-pl/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s-%6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-pt-rPT/donottranslate-cldr.xml b/core/res/res/values-pt-rPT/donottranslate-cldr.xml index 25dc7b3e660b..54417a4f0749 100644 --- a/core/res/res/values-pt-rPT/donottranslate-cldr.xml +++ b/core/res/res/values-pt-rPT/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEE, d \'de\' MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d \'de\' MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d \'de\' MMM \'de\' yyyy</string> </resources> diff --git a/core/res/res/values-pt/donottranslate-cldr.xml b/core/res/res/values-pt/donottranslate-cldr.xml index 4431877a17e1..d999c06db1ee 100644 --- a/core/res/res/values-pt/donottranslate-cldr.xml +++ b/core/res/res/values-pt/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s de %2$s - %6$s, %8$s de %7$s de %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEE, d \'de\' MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d \'de\' MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d \'de\' MMM \'de\' yyyy</string> </resources> diff --git a/core/res/res/values-ro-rRO/donottranslate-cldr.xml b/core/res/res/values-ro-rRO/donottranslate-cldr.xml index 3e425f0f7984..732ae24f88e3 100644 --- a/core/res/res/values-ro-rRO/donottranslate-cldr.xml +++ b/core/res/res/values-ro-rRO/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-ro/donottranslate-cldr.xml b/core/res/res/values-ro/donottranslate-cldr.xml index ef341031d85a..e9a70dc28459 100644 --- a/core/res/res/values-ro/donottranslate-cldr.xml +++ b/core/res/res/values-ro/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s - %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">E, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM y</string> </resources> diff --git a/core/res/res/values-ru/donottranslate-cldr.xml b/core/res/res/values-ru/donottranslate-cldr.xml index 83994245a128..2fbcd4349e34 100644 --- a/core/res/res/values-ru/donottranslate-cldr.xml +++ b/core/res/res/values-ru/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s - %8$s %7$s %9$s г.</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">E, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-sk-rSK/donottranslate-cldr.xml b/core/res/res/values-sk-rSK/donottranslate-cldr.xml index 32272eab0e69..2843ae3334a3 100644 --- a/core/res/res/values-sk-rSK/donottranslate-cldr.xml +++ b/core/res/res/values-sk-rSK/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s. %2$s - %6$s %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE d. MMM yyyy</string> </resources> diff --git a/core/res/res/values-sk/donottranslate-cldr.xml b/core/res/res/values-sk/donottranslate-cldr.xml index b2ed9b778abc..dcdaad16a24f 100644 --- a/core/res/res/values-sk/donottranslate-cldr.xml +++ b/core/res/res/values-sk/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E, d. MMMM</string> + <string name="abbrev_wday_month_day_no_year">E, d. MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d. MMM y</string> </resources> diff --git a/core/res/res/values-sl-rSI/donottranslate-cldr.xml b/core/res/res/values-sl-rSI/donottranslate-cldr.xml index e4bc557d3006..cbabccd0f353 100644 --- a/core/res/res/values-sl-rSI/donottranslate-cldr.xml +++ b/core/res/res/values-sl-rSI/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s–%6$s, %8$s. %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE MMMM d</string> <string name="abbrev_wday_month_day_year">E, d. MMM yyyy</string> </resources> diff --git a/core/res/res/values-sl/donottranslate-cldr.xml b/core/res/res/values-sl/donottranslate-cldr.xml index 372b0d585607..b804b3fd1fbd 100644 --- a/core/res/res/values-sl/donottranslate-cldr.xml +++ b/core/res/res/values-sl/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s., %3$s. %2$s. – %6$s., %8$s. %7$s. %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">E., d. MMM. y</string> </resources> diff --git a/core/res/res/values-sr-rRS/donottranslate-cldr.xml b/core/res/res/values-sr-rRS/donottranslate-cldr.xml index 79752ab60566..7168bccfabba 100644 --- a/core/res/res/values-sr-rRS/donottranslate-cldr.xml +++ b/core/res/res/values-sr-rRS/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s.</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, d. MMM yyyy.</string> </resources> diff --git a/core/res/res/values-sr/donottranslate-cldr.xml b/core/res/res/values-sr/donottranslate-cldr.xml index 7c5c6a971061..7426bdcf1e56 100644 --- a/core/res/res/values-sr/donottranslate-cldr.xml +++ b/core/res/res/values-sr/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s. %2$s - %6$s, %8$s. %7$s %9$s.</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, d. MMM y.</string> </resources> diff --git a/core/res/res/values-sv/donottranslate-cldr.xml b/core/res/res/values-sv/donottranslate-cldr.xml index bf688ce2bc7f..8b7833fba022 100644 --- a/core/res/res/values-sv/donottranslate-cldr.xml +++ b/core/res/res/values-sv/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM yyyy</string> </resources> diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 921bcf4d6e57..431a502bd937 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -62,8 +62,8 @@ <!-- Default padding to apply to AppWidgetHostViews containing widgets targeting API level 14 and up. --> <dimen name="default_app_widget_padding_left">12dp</dimen> - <dimen name="default_app_widget_padding_top">12dp</dimen> - <dimen name="default_app_widget_padding_right">4dp</dimen> + <dimen name="default_app_widget_padding_top">4dp</dimen> + <dimen name="default_app_widget_padding_right">12dp</dimen> <dimen name="default_app_widget_padding_bottom">20dp</dimen> <!-- Minimum width for an action button in the menu area of an action bar --> diff --git a/core/res/res/values-th-rTH/donottranslate-cldr.xml b/core/res/res/values-th-rTH/donottranslate-cldr.xml index 85dc3b930500..7049c5291670 100644 --- a/core/res/res/values-th-rTH/donottranslate-cldr.xml +++ b/core/res/res/values-th-rTH/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM yyyy</string> </resources> diff --git a/core/res/res/values-th/donottranslate-cldr.xml b/core/res/res/values-th/donottranslate-cldr.xml index 4e6bafe30fc7..f3196d1277da 100644 --- a/core/res/res/values-th/donottranslate-cldr.xml +++ b/core/res/res/values-th/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s %3$s %2$s – %6$s %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E d MMMM</string> + <string name="abbrev_wday_month_day_no_year">E d MMMM</string> <string name="abbrev_wday_month_day_year">EEE d MMM y</string> </resources> diff --git a/core/res/res/values-tl/donottranslate-cldr.xml b/core/res/res/values-tl/donottranslate-cldr.xml index e3106c78ba66..6ff92e3d9e9f 100644 --- a/core/res/res/values-tl/donottranslate-cldr.xml +++ b/core/res/res/values-tl/donottranslate-cldr.xml @@ -119,5 +119,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %9$s-%2$s-%3$s – %6$s, yyyy-%7$s-%8$s</string> <string name="short_format_month">%B</string> <string name="full_wday_month_day_no_year">E MMMM d</string> + <string name="abbrev_wday_month_day_no_year">E MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, y MMM d</string> </resources> diff --git a/core/res/res/values-tr/donottranslate-cldr.xml b/core/res/res/values-tr/donottranslate-cldr.xml index 55b6d1f34378..92b0c72753f5 100644 --- a/core/res/res/values-tr/donottranslate-cldr.xml +++ b/core/res/res/values-tr/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%3$s %2$s %1$s - %8$s %7$s %6$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">dd MMMM EEEE</string> + <string name="abbrev_wday_month_day_no_year">dd MMMM EEE</string> <string name="abbrev_wday_month_day_year">dd MMM yyyy EEE</string> </resources> diff --git a/core/res/res/values-uk-rUA/donottranslate-cldr.xml b/core/res/res/values-uk-rUA/donottranslate-cldr.xml index 332f8456955c..caf46a389bc8 100644 --- a/core/res/res/values-uk-rUA/donottranslate-cldr.xml +++ b/core/res/res/values-uk-rUA/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s – %6$s, %8$s %7$s %9$s р.</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM yyyy \'р\'.</string> </resources> diff --git a/core/res/res/values-uk/donottranslate-cldr.xml b/core/res/res/values-uk/donottranslate-cldr.xml index 0a49c20ad120..46211ac2ea95 100644 --- a/core/res/res/values-uk/donottranslate-cldr.xml +++ b/core/res/res/values-uk/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %3$s %2$s – %6$s, %8$s %7$s %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E, d MMMM</string> + <string name="abbrev_wday_month_day_no_year">E, d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM y</string> </resources> diff --git a/core/res/res/values-vi-rVN/donottranslate-cldr.xml b/core/res/res/values-vi-rVN/donottranslate-cldr.xml index b7edeb3682ba..5d049a1b72fd 100644 --- a/core/res/res/values-vi-rVN/donottranslate-cldr.xml +++ b/core/res/res/values-vi-rVN/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, ngày %3$s %2$s - %6$s, ngày %8$s %7$s năm %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE d MMMM</string> + <string name="abbrev_wday_month_day_no_year">EEE d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM yyyy</string> </resources> diff --git a/core/res/res/values-vi/donottranslate-cldr.xml b/core/res/res/values-vi/donottranslate-cldr.xml index 30e088717c07..9a757cd5d4dc 100644 --- a/core/res/res/values-vi/donottranslate-cldr.xml +++ b/core/res/res/values-vi/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, ngày %3$s %2$s - %6$s, ngày %8$s %7$s năm %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">E d MMMM</string> + <string name="abbrev_wday_month_day_no_year">E d MMMM</string> <string name="abbrev_wday_month_day_year">EEE, d MMM y</string> </resources> diff --git a/core/res/res/values-zh-rCN/donottranslate-cldr.xml b/core/res/res/values-zh-rCN/donottranslate-cldr.xml index ec9d8c02a42a..9e6ef1c3fb39 100644 --- a/core/res/res/values-zh-rCN/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rCN/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s 年 %2$s %3$s 日%1$s - %7$s %8$s 日%6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">M 月 d 日 E</string> + <string name="abbrev_wday_month_day_no_year">M 月 d 日 E</string> <string name="abbrev_wday_month_day_year">yyyy 年 M 月 d 日 EEE</string> </resources> diff --git a/core/res/res/values-zh-rTW/donottranslate-cldr.xml b/core/res/res/values-zh-rTW/donottranslate-cldr.xml index a43f8572850b..907d82e03a45 100644 --- a/core/res/res/values-zh-rTW/donottranslate-cldr.xml +++ b/core/res/res/values-zh-rTW/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%9$s 年 %2$s %3$s 日%1$s至 %7$s %8$s 日%6$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">M 月 d 日E</string> + <string name="abbrev_wday_month_day_no_year">M 月 d 日E</string> <string name="abbrev_wday_month_day_year">yyyy 年 M 月 d 日EEE</string> </resources> diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index 767cafe01548..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 @@ -741,4 +744,7 @@ <string name="config_wimaxServiceClassname"></string> <!-- Name of the wimax state tracker clas --> <string name="config_wimaxStateTrackerClassname"></string> + + <!-- Name of screensaver components to look for if none has been chosen by the user --> + <string name="config_defaultDreamComponent">com.google.android.deskclock/com.android.deskclock.Screensaver</string> </resources> diff --git a/core/res/res/values/donottranslate-cldr.xml b/core/res/res/values/donottranslate-cldr.xml index a94fb58dea33..15fcd8be989c 100644 --- a/core/res/res/values/donottranslate-cldr.xml +++ b/core/res/res/values/donottranslate-cldr.xml @@ -145,5 +145,6 @@ <string name="same_year_wday1_mdy1_wday2_mdy2">%1$s, %2$s %3$s – %6$s, %7$s %8$s, %9$s</string> <string name="short_format_month">%b</string> <string name="full_wday_month_day_no_year">EEEE, MMMM d</string> + <string name="abbrev_wday_month_day_no_year">EEE, MMMM d</string> <string name="abbrev_wday_month_day_year">EEE, MMM d, yyyy</string> </resources> diff --git a/core/tests/bluetoothtests/AndroidManifest.xml b/core/tests/bluetoothtests/AndroidManifest.xml index 58f158ce410f..60b6dc18f0c4 100644 --- a/core/tests/bluetoothtests/AndroidManifest.xml +++ b/core/tests/bluetoothtests/AndroidManifest.xml @@ -19,6 +19,8 @@ <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /> + <uses-permission android:name="android.permission.BROADCAST_STICKY" /> + <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_SETTINGS" /> <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java index abd7d9af224a..755e7c4504c8 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothStressTest.java @@ -32,6 +32,8 @@ import android.test.InstrumentationTestCase; public class BluetoothStressTest extends InstrumentationTestCase { private static final String TAG = "BluetoothStressTest"; private static final String OUTPUT_FILE = "BluetoothStressTestOutput.txt"; + /** The amount of time to sleep between issuing start/stop SCO in ms. */ + private static final long SCO_SLEEP_TIME = 2 * 1000; private BluetoothTestUtils mTestUtils; @@ -380,11 +382,20 @@ public class BluetoothStressTest extends InstrumentationTestCase { for (int i = 0; i < iterations; i++) { mTestUtils.writeOutput("startStopSco iteration " + (i + 1) + " of " + iterations); mTestUtils.startSco(adapter, device); + sleep(SCO_SLEEP_TIME); mTestUtils.stopSco(adapter, device); + sleep(SCO_SLEEP_TIME); } mTestUtils.disconnectProfile(adapter, device, BluetoothProfile.HEADSET, null); mTestUtils.unpair(adapter, device); mTestUtils.disable(adapter); } + + private void sleep(long time) { + try { + Thread.sleep(time); + } catch (InterruptedException e) { + } + } } diff --git a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java index 42e5cd17f2a7..4858be8c2a8e 100644 --- a/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java +++ b/core/tests/bluetoothtests/src/android/bluetooth/BluetoothTestUtils.java @@ -1425,7 +1425,7 @@ public class BluetoothTestUtils extends Assert { } private StartStopScoReceiver getStartStopScoReceiver(int expectedFlags) { - String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED}; + String[] actions = {AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED}; StartStopScoReceiver receiver = new StartStopScoReceiver(expectedFlags); addReceiver(receiver, actions); return receiver; diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 9fe2dde14610..6cd07a31ccc5 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -137,6 +137,7 @@ <assign-permission name="android.permission.SET_ALWAYS_FINISH" uid="shell" /> <assign-permission name="android.permission.DUMP" uid="shell" /> <assign-permission name="android.permission.SIGNAL_PERSISTENT_PROCESSES" uid="shell" /> + <assign-permission name="android.permission.KILL_BACKGROUND_PROCESSES" uid="shell" /> <!-- Internal permissions granted to the shell. --> <assign-permission name="android.permission.FORCE_BACK" uid="shell" /> <assign-permission name="android.permission.BATTERY_STATS" uid="shell" /> diff --git a/docs/html/guide/appendix/market-filters.jd b/docs/html/guide/appendix/market-filters.jd index dc79d95c1d91..6610f5fe0949 100644 --- a/docs/html/guide/appendix/market-filters.jd +++ b/docs/html/guide/appendix/market-filters.jd @@ -25,7 +25,7 @@ manifest file to the configurations defined by the device, as well as other fact </ol> <h2>See also</h2> - <ol> + <ol> <li><a href="{@docRoot}guide/practices/compatibility.html">Android Compatibility</a></li> <li><code><a @@ -48,7 +48,7 @@ href="{@docRoot}guide/topics/manifest/uses-sdk-element.html"><uses-sdk></c <img id="rule" src="{@docRoot}assets/images/grad-rule-qv.png"> <div id="qv-sub-rule"> <img src="{@docRoot}assets/images/icon_market.jpg" style="float:left;margin:0;padding:0 5px;"> - <h2 style="color:#669999;">Interested in publishing your app on Android Market?</h2> + <h2 style="color:#669999;">Interested in publishing your app on Android Market?</h2> <p><a id="publish-link" href="http://market.android.com/publish">Go to Android Market</a> to create a publisher account and upload your app.</p></div> @@ -83,7 +83,7 @@ restrictions and dependencies expressed by the application's manifest file and publishing details. If the application is compatible with the device according to the filter rules, Market displays the application to the user. Otherwise, Market hides your application from search -results and category browsing, even if a user specifically requests +results and category browsing, even if a user specifically requests the app by clicking a deep link that points directly to the app's ID within Market..</p> <p class="note"><strong>Note:</strong> When users browse the <a @@ -108,7 +108,7 @@ available.</p> <p>Most Market filters are triggered by elements within an application's manifest file, <a -href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a> +href="{@docRoot}guide/topics/manifest/manifest-intro.html">AndroidManifest.xml</a> (although not everything in the manifest file can trigger filtering). Table 1 lists the manifest elements that you should use to trigger Android Market filtering, and explains how the filtering for each element works.</p> @@ -173,7 +173,7 @@ default.</li> </strong>The manifest declares <code><uses-sdk android:minSdkVersion="3" android:targetSdkVersion="4"></code> and does not include a <code><supports-screens></code> element. - <strong>Result</strong>: Android Market will show the app to users on all + <strong>Result</strong>: Android Market will show the app to users on all devices, unless other filters apply. </p> <p><strong>Example 3<br /> </strong>The manifest declares <code><uses-sdk android:minSdkVersion="4"></code> @@ -402,9 +402,9 @@ country (as determined by SIM carrier) in which paid apps are available.</p></td visible on devices that support that platform. For details about the NDK and using native libraries, see <a href="{@docRoot}sdk/ndk/index.html#overview">What is the Android NDK?</a></p> </tr> <tr> - <td valign="top">Forward-Locked Applications</td> <td valign="top"><p>To - forward lock an application, set copy protection to "On" when you upload the - application to Market. Market will not show copy-protected applications on + <td valign="top">Copy-Protected Applications</td> <td valign="top"><p>To + copy protect an application, set copy protection to "On" when you configure publishing +options for your application. Market will not show copy-protected applications on developer devices or unreleased devices.</p></td> </tr> </table> @@ -431,7 +431,7 @@ href="{@docRoot}guide/topics/manifest/supports-gl-texture-element.html">{@code <p>By using the <a href="{@docRoot}guide/topics/manifest/supports-screens-element.html">{@code <supports-screens>}</a> or <a href="{@docRoot}guide/topics/manifest/compatible-screens-element.html">{@code -<compatible-screens>}</a> element.</p> +<compatible-screens>}</a> element.</p> </li> <li>API level <p>By using the <a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code diff --git a/docs/html/guide/guide_toc.cs b/docs/html/guide/guide_toc.cs index 0338685e7049..3eb7286edcdb 100644 --- a/docs/html/guide/guide_toc.cs +++ b/docs/html/guide/guide_toc.cs @@ -119,7 +119,7 @@ </a></li> <li><a href="<?cs var:toroot ?>guide/topics/ui/actionbar.html"> <span class="en">Action Bar</span> - </a></li> + </a> <span class="new">updated</span></li> <li><a href="<?cs var:toroot ?>guide/topics/ui/dialogs.html"> <span class="en">Dialogs</span> </a></li> @@ -739,21 +739,21 @@ applications</span> <ul> <li class="toggle-list"> <div><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design.html"> - <span class="en">Icon Design <span class="new">updated</span></span> - </a></div> + <span class="en">Icon Design</span> + </a> <span class="new">updated</span></div> <ul> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design_launcher.html"> - <span class="en">Launcher Icons <span class="new">updated</span></span> - </a></li> + <span class="en">Launcher Icons</span> + </a> <span class="new">updated</span></li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design_menu.html"> <span class="en">Menu Icons</span> </a></li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design_action_bar.html"> - <span class="en">Action Bar Icons <span class="new">new!</span></span> - </a></li> + <span class="en">Action Bar Icons</span> + </a> <span class="new">new!</span></li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design_status_bar.html"> - <span class="en">Status Bar Icons <span class="new">updated</span></span> - </a></li> + <span class="en">Status Bar Icons</span> + </a> <span class="new">updated</span></li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/icon_design_tab.html"> <span class="en">Tab Icons</span> </a></li> @@ -766,8 +766,8 @@ applications</span> </ul> </li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/widget_design.html"> - <span class="en">App Widget Design <span class="new">updated</span></span> - </a></li> + <span class="en">App Widget Design</span> + </a> <span class="new">updated</span></li> <li><a href="<?cs var:toroot ?>guide/practices/ui_guidelines/activity_task_design.html"> <span class="en">Activity and Task Design</span> </a></li> @@ -797,6 +797,10 @@ applications</span> <li><a href="<?cs var:toroot ?>guide/practices/design/seamlessness.html"> <span class="en">Designing for Seamlessness</span> </a></li> + <li><a href="<?cs var:toroot ?>guide/practices/security.html"> + <span class="en">Designing for Security</span></a> + <span class="new">new!</span><!-- 11/7/10 --> + </li> </ul> </li> @@ -840,8 +844,8 @@ applications</span> <span class="en">App Install Location</span> </a></li> <li><a href="<?cs var:toroot ?>guide/appendix/media-formats.html"> - <span class="en">Supported Media Formats <span class="new">updated</span></span> - </a></li> + <span class="en">Supported Media Formats</span> + </a> <span class="new">updated</span></li> <li><a href="<?cs var:toroot ?>guide/appendix/g-app-intents.html"> <span class="en">Intents List: Google Apps</span> </a></li> diff --git a/docs/html/guide/market/billing/billing_integrate.jd b/docs/html/guide/market/billing/billing_integrate.jd index 3eebd595a492..6017583d8b15 100755 --- a/docs/html/guide/market/billing/billing_integrate.jd +++ b/docs/html/guide/market/billing/billing_integrate.jd @@ -476,7 +476,7 @@ key can have the following five values:</p> <ul> <li><code>CHECK_BILLING_SUPPORTED</code>—verifies that the Android Market application supports in-app billing.</li> - <li><code>REQUEST_PURCHASE</code>—sends a purchase request for an in-app item.</li> + <li><code>REQUEST_PURCHASE</code>—sends a purchase request for an in-app item.</li> <li><code>GET_PURCHASE_INFORMATION</code>—retrieves transaction information for a purchase or refund.</li> <li><code>CONFIRM_NOTIFICATIONS</code>—acknowledges that you received the transaction @@ -584,9 +584,9 @@ interface.</p> // Note that the developer payload is optional. if (mDeveloperPayload != null) { request.putString(DEVELOPER_PAYLOAD, mDeveloperPayload); + } Bundle response = mService.sendBillingRequest(request); // Do something with this response. - } </pre> <p>The <code>makeRequestBundle()</code> method constructs an initial Bundle, which contains the three keys that are required for all requests: <code>BILLING_REQUEST</code>, @@ -930,9 +930,9 @@ sent in response to billing requests.</p> <pre> public class BillingReceiver extends BroadcastReceiver { - + private static final String TAG = "BillingReceiver"; - + // Intent actions that we receive in the BillingReceiver from Android Market. // These are defined by Android Market and cannot be changed. // The sample application defines these in the Consts.java file. @@ -940,7 +940,7 @@ public class BillingReceiver extends BroadcastReceiver { public static final String ACTION_RESPONSE_CODE = "com.android.vending.billing.RESPONSE_CODE"; public static final String ACTION_PURCHASE_STATE_CHANGED = "com.android.vending.billing.PURCHASE_STATE_CHANGED"; - + // The intent extras that are passed in an intent from Android Market. // These are defined by Android Market and cannot be changed. // The sample application defines these in the Consts.java file. diff --git a/docs/html/guide/practices/security.jd b/docs/html/guide/practices/security.jd new file mode 100644 index 000000000000..5da7e98c6a4c --- /dev/null +++ b/docs/html/guide/practices/security.jd @@ -0,0 +1,772 @@ +page.title=Designing for Security +@jd:body + +<div id="qv-wrapper"> +<div id="qv"> +<h2>In this document</h2> +<ol> +<li><a href="#Dalvik">Using Davlik Code</a></li> +<li><a href="#Native">Using Native Code</a></li> +<li><a href="#Data">Storing Data</a></li> +<li><a href="#IPC">Using IPC</a></li> +<li><a href="#Permissions">Using Permissions</a></li> +<li><a href="#Networking">Using Networking</a></li> +<li><a href="#DynamicCode">Dynamically Loading Code</a></li> +<li><a href="#Input">Performing Input Validation</a></li> +<li><a href="#UserData">Handling User Data</a></li> +<li><a href="#Crypto">Using Cryptography</a></li> +</ol> +<h2>See also</h2> +<ol> +<li><a href="http://source.android.com/tech/security/index.html">Android +Security Overview</a></li> +<li><a href="{@docRoot}guide/topics/security/security.html">Android Security +And Permissions</a></li> +</ol> +</div></div> +<p>Android was designed so that most developers will be able to build +applications using the default settings and not be confronted with difficult +decisions about security. Android also has a number of security features built +into the operating system that significantly reduce the frequency and impact of +application security issues.</p> + +<p>Some of the security features that help developers build secure applications +include: +<ul> +<li>The Android Application Sandbox that isolates data and code execution on a +per-application basis.</li> +<li>Android application framework with robust implementations of common +security functionality such as cryptography, permissions, and secure IPC.</li> +<li>Technologies like ASLR, NX, ProPolice, safe_iop, OpenBSD dlmalloc, OpenBSD +calloc, and Linux mmap_min_addr to mitigate risks associated with common memory +management errors</li> +<li>An encrypted filesystem that can be enabled to protect data on lost or +stolen devices.</li> +</ul></p> + +<p>Nevertheless, it is important for developers to be familiar with Android +security best practices to make sure they take advantage of these capabilities +and to reduce the likelihood of inadvertently introducing security issues that +can affect their applications.</p> + +<p>This document is organized around common APIs and development techniques +that can have security implications for your application and its users. As +these best practices are constantly evolving, we recommend you check back +occasionally throughout your application development process.</p> + +<a name="Dalvik"></a> +<h2>Using Dalvik Code</h2> +<p>Writing secure code that runs in virtual machines is a well-studied topic +and many of the issues are not specific to Android. Rather than attempting to +rehash these topics, we’d recommend that you familiarize yourself with the +existing literature. Two of the more popular resources are: +<ul> +<li><a href="http://www.securingjava.com/toc.html"> +http://www.securingjava.com/toc.html</a></li> +<li><a +href="https://www.owasp.org/index.php/Java_Security_Resources"> +https://www.owasp.org/index.php/Java_Security_Resources</a></li> +</ul></p> + +<p>This document is focused on the areas which are Android specific and/or +different from other environments. For developers experienced with VM +programming in other environments, there are two broad issues that may be +different about writing apps for Android: +<ul> +<li>Some virtual machines, such as the JVM or .net runtime, act as a security +boundary, isolating code from the underlying operating system capabilities. On +Android, the Dalvik VM is not a security boundary -- the application sandbox is +implemented at the OS level, so Dalvik can interoperate with native code in the +same application without any security constraints.</li> +<li>Given the limited storage on mobile devices, it’s common for developers +to want to build modular applications and use dynamic class loading. When +doing this consider both the source where you retrieve your application logic +and where you store it locally. Do not use dynamic class loading from sources +that are not verified, such as unsecured network sources or external storage, +since that code can be modified to include malicious behavior.</li> +</ul></p> + +<a name="Native"></a> +<h2>Using Native Code</h2> + +<p>In general, we encourage developers to use the Android SDK for most +application development, rather than using native code. Applications built +with native code are more complex, less portable, and more like to include +common memory corruption errors such as buffer overflows.</p> + +<p>Android is built using the Linux kernel and being familiar with Linux +development security best practices is especially useful if you are going to +use native code. This document is too short to discuss all of those best +practices, but one of the most popular resources is “Secure Programming for +Linux and Unix HOWTO”, available at <a +href="http://www.dwheeler.com/secure-programs"> +http://www.dwheeler.com/secure-programs</a>.</p> + +<p>An important difference between Android and most Linux environments is the +Application Sandbox. On Android, all applications run in the Application +Sandbox, including those written with native code. At the most basic level, a +good way to think about it for developers familiar with Linux is to know that +every application is given a unique UID with very limited permissions. This is +discussed in more detail in the <a +href="http://source.android.com/tech/security/index.html">Android Security +Overview</a> and you should be familiar with application permissions even if +you are using native code.</p> + +<a name="Data"></a> +<h2>Storing Data</h2> + +<h3>Using internal files</h3> + +<p>By default, files created on <a +href="{@docRoot}guide/topics/data/data-storage.html#filesInternal">internal +storage</a> are only accessible to the application that created the file. This +protection is implemented by Android and is sufficient for most +applications.</p> + +<p>Use of <a +href="{@docRoot}reference/android/content/Context.html#MODE_WORLD_WRITEABLE"> +world writable</a> or <a +href="{@docRoot}reference/android/content/Context.html#MODE_WORLD_READABLE +">world readable</a> files for IPC is discouraged because it does not provide +the ability to limit data access to particular applications, nor does it +provide any control on data format. As an alternative, you might consider using +a ContentProvider which provides read and write permissions, and can make +dynamic permission grants on a case-by-case basis.</p> + +<p>To provide additional protection for sensitive data, some applications +choose to encrypt local files using a key that is not accessible to the +application. (For example, a key can be placed in a <code><a +href={@docRoot}reference/java/security/KeyStore.html">KeyStore</a></code> and +protected with a user password that is not stored on the device). While this +does not protect data from a root compromise that can monitor the user +inputting the password, it can provide protection for a lost device without <a +href="http://source.android.com/tech/encryption/index.html">file system +encryption</a>.</p> + +<h3>Using external storage</h3> + +<p>Files created on <a +href="{@docRoot}guide/topics/data/data-storage.html#filesExternal">external +storage</a>, such as SD Cards, are globally readable and writable. Since +external storage can be removed by the user and also modified by any +application, applications should not store sensitive information using +external storage.</p> + +<p>As with data from any untrusted source, applications should perform input +validation when handling data from external storage (see Input Validation +section). We strongly recommend that applications not store executables or +class files on external storage prior to dynamic loading. If an application +does retrieve executable files from external storage they should be signed and +cryptographically verified prior to dynamic loading.</p> + +<h3>Using content providers</h3> + +<p>ContentProviders provide a structured storage mechanism that can be limited +to your own application, or exported to allow access by other applications. By +default, a <code> +<a href="{@docRoot}reference/android/content/ContentProvider.html"> +ContentProvider</a></code> is +<a href="{@docRoot}guide/topics/manifest/provider-element.html#exported">exported +</a> for use by other applications. If you do not intend to provide other +applications with access to your<code> +<a href="{@docRoot}reference/android/content/ContentProvider.html"> +ContentProvider</a></code>, mark them as <code><a +href="{@docRoot}guide/topics/manifest/provider-element.html#exported"> +android:exported=false</a></code> in the application manifest.</p> + +<p>When creating a <code> +<a href="{@docRoot}reference/android/content/ContentProvider.html">ContentProvider +</a></code> that will be exported for use by other applications, you can specify +a single +<a href="{@docRoot}guide/topics/manifest/provider-element.html#prmsn">permission +</a> for reading and writing, or distinct permissions for reading and writing +within the manifest. We recommend that you limit your permissions to those +required to accomplish the task at hand. Keep in mind that it’s usually +easier to add permissions later to expose new functionality than it is to take +them away and break existing users.</p> + +<p>If you are using a <code> +<a href="{@docRoot}reference/android/content/ContentProvider.html"> +ContentProvider</a></code> for sharing data between applications built by the +same developer, it is preferable to use +<a href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature +level permissions</a>. Signature permissions do not require user confirmation, +so they provide a better user experience and more controlled access to the +<code> +<a href="{@docRoot}reference/android/content/ContentProvider.html"> +ContentProvider</a></code>.</p> + +<p>ContentProviders can also provide more granular access by declaring the <a +href="{@docRoot}guide/topics/manifest/provider-element.html#gprmsn"> +grantUriPermissions</a> element and using the <code><a +href="{@docRoot}reference/android/content/Intent.html#FLAG_GRANT_READ_URI_PERMIS +SION">FLAG_GRANT_READ_URI_PERMISSION</a></code> and <code><a +href="{@docRoot}reference/android/content/Intent.html#FLAG_GRANT_WRITE_URI_PERMI +SSION">FLAG_GRANT_WRITE_URI_PERMISSION</a></code> flags in the Intent object +that activates the component. The scope of these permissions can be further +limited by the <code><a +href="{@docRoot}guide/topics/manifest/grant-uri-permission-element.html"> +grant-uri-permission element</a></code>.</p> + +<p>When accessing a <code> +<a href="{@docRoot}reference/android/content/ContentProvider.html"> +ContentProvider</a></code>, use parameterized query methods such as <code> +<a href="{@docRoot}reference/android/content/ContentProvider.html#query(android.net +.Uri,%20java.lang.String[],%20java.lang.String,%20java.lang.String[],%20java.lan +g.String)">query()</a></code>, <code><a +href="{@docRoot}reference/android/content/ContentProvider.html#update(android.ne +t.Uri,%20android.content.ContentValues,%20java.lang.String,%20java.lang.String[] +)">update()</a></code>, and <code><a +href="{@docRoot}reference/android/content/ContentProvider.html#delete(android.ne +t.Uri,%20java.lang.String,%20java.lang.String[])">delete()</a></code> to avoid +potential <a href="http://en.wikipedia.org/wiki/SQL_injection">SQL +Injection</a> from untrusted data. Note that using parameterized methods is not +sufficient if the <code>selection</code> is built by concatenating user data +prior to submitting it to the method.</p> + +<p>Do not have a false sense of security about the write permission. Consider +that the write permission allows SQL statements which make it possible for some +data to be confirmed using creative <code>WHERE</code> clauses and parsing the +results. For example, an attacker might probe for presence of a specific phone +number in a call-log by modifying a row only if that phone number already +exists. If the content provider data has predictable structure, the write +permission may be equivalent to providing both reading and writing.</p> + +<a name="IPC"></a> +<h2>Using Interprocess Communication (IPC)</h2> + +<p>Some Android applications attempt to implement IPC using traditional Linux +techniques such as network sockets and shared files. We strongly encourage the +use of Android system functionality for IPC such as Intents, Binders, Services, +and Receivers. The Android IPC mechanisms allow you to verify the identity of +the application connecting to your IPC and set security policy for each IPC +mechanism.</p> + +<p>Many of the security elements are shared across IPC mechanisms. <a +href="{@docRoot}reference/android/content/BroadcastReceiver.html"> +Broadcast Receivers</a>, <a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestActivity"> +Activities</a>, and <a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestService"> +Services</a> are all declared in the application manifest. If your IPC mechanism is +not intended for use by other applications, set the android:exported property +to false. This is useful for applications that consist of multiple processes +within the same UID, or if you decide late in development that you do not +actually want to expose functionality as IPC but you don’t want to rewrite +the code.</p> + +<p>If your IPC is intended to be accessible to other applications, you can +apply a security policy by using the <a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestPermission"> +Permission</a> tag. If IPC is between applications built by the same developer, +it is preferable to use <a +href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature +level permissions</a>. Signature permissions do not require user confirmation, +so they provide a better user experience and more controlled access to the IPC +mechanism.</p> + +<p>One area that can introduce confusion is the use of intent filters. Note +that Intent filters should not be considered a security feature -- components +can be invoked directly and may not have data that would conform to the intent +filter. You should perform input validation within your intent receiver to +confirm that it is properly formatted for the invoked receiver, service, or +activity.</p> + +<h3>Using intents</h3> + +<p>Intents are the preferred mechanism for asynchronous IPC in Android. +Depending on your application requirements, you might use <code><a +href="{@docRoot}reference/android/content/Context.html#sendBroadcast(android.con +tent.Intent)">sendBroadcast()</a></code>, <code><a +href="{@docRoot}reference/android/content/Context.html#sendOrderedBroadcast(andr +oid.content.Intent,%20java.lang.String)">sendOrderedBroadcast()</a></code>, or +direct an intent to a specific application component.</p> + +<p>Note that ordered broadcasts can be “consumed” by a recipient, so they +may not be delivered to all applications. If you are sending an Intent where +delivery to a specific receiver is required, the intent must be delivered +directly to the receiver.</p> + +<p>Senders of an intent can verify that the recipient has a permission +specifying a non-Null Permission upon sending. Only applications with that +Permission will receive the intent. If data within a broadcast intent may be +sensitive, you should consider applying a permission to make sure that +malicious applications cannot register to receive those messages without +appropriate permissions. In those circumstances, you may also consider +invoking the receiver directly, rather than raising a broadcast.</p> + +<h3>Using binder and AIDL interfaces</h3> + +<p><a href="{@docRoot}reference/android/os/Binder.html">Binders</a> are the +preferred mechanism for RPC-style IPC in Android. They provide a well-defined +interface that enables mutual authentication of the endpoints, if required.</p> + +<p>We strongly encourage designing interfaces in a manner that does not require +interface specific permission checks. Binders are not declared within the +application manifest, and therefore you cannot apply declarative permissions +directly to a Binder. Binders generally inherit permissions declared in the +application manifest for the Service or Activity within which they are +implemented. If you are creating an interface that requires authentication +and/or access controls on a specific binder interface, those controls must be +explicitly added as code in the interface.</p> + +<p>If providing an interface that does require access controls, use <code><a +href="{@docRoot}reference/android/content/Context.html#checkCallingPermission(ja +va.lang.String)">checkCallingPermission()</a></code> to verify whether the +caller of the Binder has a required permission. This is especially important +before accessing a Service on behalf of the caller, as the identify of your +application is passed to other interfaces. If invoking an interface provided +by a Service, the <code><a +href="{@docRoot}reference/android/content/Context.html#bindService(android.conte +nt.Intent,%20android.content.ServiceConnection,%20int)">bindService()</a></code> + invocation may fail if you do not have permission to access the given Service. + If calling an interface provided locally by your own application, it may be +useful to use the <code><a +href="{@docRoot}reference/android/os/Binder.html#clearCallingIdentity()"> +clearCallingIdentity()</a></code> to satisfy internal security checks.</p> + +<h3>Using broadcast receivers</h3> + +<p>Broadcast receivers are used to handle asynchronous requests initiated via +an intent.</p> + +<p>By default, receivers are exported and can be invoked by any other +application. If your <code><a +href={@docRoot}reference/android/content/BroadcastReceiver.html"> +BroadcastReceivers</a></code> is intended for use by other applications, you +may want to apply security permissions to receivers using the <code><a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestReceiver"> +<receiver></a></code> element within the application manifest. This will +prevent applications without appropriate permissions from sending an intent to +the <code><a +href={@docRoot}reference/android/content/BroadcastReceiver.html"> +BroadcastReceivers</a></code>.</p> + +<h3>Using Services</h3> + +<p>Services are often used to supply functionality for other applications to +use. Each service class must have a corresponding <service> declaration in its +package's AndroidManifest.xml.</p> + +<p>By default, Services are exported and can be invoked by any other +application. Services can be protected using the android:permission attribute +within the manifest’s <code><a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestService"> +<service></a></code> tag. By doing so, other applications will need to declare +a corresponding <code><a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestService_permis +sion"><uses-permission></a></code> element in their own manifest to be +able to start, stop, or bind to the service.</p> + +<p>A Service can protect individual IPC calls into it with permissions, by +calling <code><a +href="{@docRoot}reference/android/content/Context.html#checkCallingPermission(ja +va.lang.String)">checkCallingPermission()</a></code>before executing +the implementation of that call. We generally recommend using the +declarative permissions in the manifest, since those are less prone to +oversight.</p> + +<h3>Using Activities</h3> + +<p>Activities are most often used for providing the core user-facing +functionality of an application. By default, Activities are exported and +invokable by other applications only if they have an intent filter or binder +declared. In general, we recommend that you specifically declare a Receiver or +Service to handle IPC, since this modular approach reduces the risk of exposing +functionality that is not intended for use by other applications.</p> + +<p>If you do expose an Activity for purposes of IPC, the <code><a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestActivity_permi +ssion">android:permission</a></code> attribute in the <code><a +href="{@docRoot}reference/android/R.styleable.html#AndroidManifestActivity"> +<activity></a></code> declaration in the application manifest can be used to +restrict access to only those applications which have the stated +permissions.</p> + +<a name="Permissions"></a> +<h2>Using Permissions</h2> + +<h3>Requesting Permissions</h3> + +<p>We recommend minimizing the number of permissions requested by an +application. Not having access to sensitive permissions reduces the risk of +inadvertently misusing those permissions, can improve user adoption, and makes +applications less attractive targets for attackers.</p> + +<p>If it is possible to design your application in a way that does not require +a permission, that is preferable. For example, rather than requesting access +to device information to create an identifier, create a <a +href="{@docRoot}reference/java/util/UUID.html">GUID</a> for your application. +(This specific example is also discussed in Handling User Data) Or, rather than +using external storage, store data in your application directory.</p> + +<p>If a permission is not required, do not request it. This sounds simple, but +there has been quite a bit of research into the frequency of over-requesting +permissions. If you’re interested in the subject you might start with this +research paper published by U.C. Berkeley: <a +href="http://www.eecs.berkeley.edu/Pubs/TechRpts/2011/EECS-2011-48.pdf"> +http://www.eecs.berkeley.edu/Pubs/TechRpts/2011/EECS-2011-48.pdf</a></p> + +<p>In addition to requesting permissions, your application can use <a +href="{@docRoot}guide/topics/manifest/permission-element.html">permissions</a> +to protect IPC that is security sensitive and will be exposed to other +applications -- such as a <code><a +href="{@docRoot}reference/android/content/ContentProvider.html"> +ContentProvider</a></code>. In general, we recommend using access controls +other than user confirmed permissions where possible since permissions can +be confusing for users. For example, consider using the <a +href="{@docRoot}guide/topics/manifest/permission-element.html#plevel">signature +protection level</a> on permissions for IPC communication between applications +provided by a single developer.</p> + +<p>Do not cause permission re-delegation. This occurs when an app exposes data +over IPC that is only available because it has a specific permission, but does +not require that permission of any clients of it’s IPC interface. More +details on the potential impacts, and frequency of this type of problem is +provided in this research paper published at USENIX: <a +href="http://www.cs.berkeley.edu/~afelt/felt_usenixsec2011.pdf">http://www.cs.be +rkeley.edu/~afelt/felt_usenixsec2011.pdf</a></p> + +<h3>Creating Permissions</h3> + +<p>Generally, you should strive to create as few permissions as possible while +satisfying your security requirements. Creating a new permission is relatively +uncommon for most applications, since <a +href="{@docRoot}reference/android/Manifest.permission.html"> +system-defined permissions</a> cover many situations. Where appropriate, +perform access checks using existing permissions.</p> + +<p>If you must create a new permission, consider whether you can accomplish +your task with a Signature permission. Signature permissions are transparent +to the user and only allow access by applications signed by the same developer +as application performing the permission check. If you create a Dangerous +permission, then the user needs to decide whether to install the application. +This can be confusing for other developers, as well as for users.</p> + +<p>If you create a Dangerous permission, there are a number of complexities +that you need to consider. +<ul> +<li>The permission must have a string that concisely expresses to a user the +security decision they will be required to make.</li> +<li>The permission string must be localized to many different languages.</li> +<li>Uses may choose not to install an application because a permission is +confusing or perceived as risky.</li> +<li>Applications may request the permission when the creator of the permission +has not been installed.</li> +</ul></p> + +<p>Each of these poses a significant non-technical challenge for an application +developer, which is why we discourage the use of Dangerous permission.</p> + +<a name="Networking"></a> +<h2>Using Networking</h2> + +<h3>Using IP Networking</h3> + +<p>Networking on Android is not significantly different from Linux +environments. The key consideration is making sure that appropriate protocols +are used for sensitive data, such as <a +href="{@docRoot}reference/javax/net/ssl/HttpsURLConnection.html">HTTPS</a> for +web traffic. We prefer use of HTTPS over HTTP anywhere that HTTPS is +supported on the server, since mobile devices frequently connect on networks +that are not secured, such as public WiFi hotspots.</p> + +<p>Authenticated, encrypted socket-level communication can be easily +implemented using the <code><a +href="{@docRoot}reference/javax/net/ssl/SSLSocket.html">SSLSocket</a></code> +class. Given the frequency with which Android devices connect to unsecured +wireless networks using WiFi, the use of secure networking is strongly +encouraged for all applications.</p> + +<p>We have seen some applications use <a +href="http://en.wikipedia.org/wiki/Localhost">localhost</a> network ports for +handling sensitive IPC. We discourage this approach since these interfaces are +accessible by other applications on the device. Instead, use an Android IPC +mechanism where authentication is possible such as a Service and Binder. (Even +worse than using loopback is to bind to INADDR_ANY since then your application +may receive requests from anywhere. We’ve seen that, too.)</p> + +<p>Also, one common issue that warrants repeating is to make sure that you do +not trust data downloaded from HTTP or other insecure protocols. This includes +validation of input in <code><a +href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code> and +any responses to intents issued against HTTP.</p> + +<h3>Using Telephony Networking</h3> + +<p>SMS is the telephony protocol most frequently used by Android developers. +Developers should keep in mind that this protocol was primarily designed for +user-to-user communication and is not well-suited for some application +purposes. Due to the limitations of SMS, we strongly recommend the use of <a +href="http://code.google.com/android/c2dm/">C2DM</a> and IP networking for +sending data messages to devices.</p> + +<p>Many developers do not realize that SMS is not encrypted or strongly +authenticated on the network or on the device. In particular, any SMS receiver +should expect that a malicious user may have sent the SMS to your application +-- do not rely on unauthenticated SMS data to perform sensitive commands. +Also, you should be aware that SMS may be subject to spoofing and/or +interception on the network. On the Android-powered device itself, SMS +messages are transmitted as Broadcast intents, so they may be read or captured +by other applications that have the READ_SMS permission.</p> + +<a name="DynamicCode"></a> +<h2>Dynamically Loading Code</h2> + +<p>We strongly discourage loading code from outside of the application APK. +Doing so significantly increases the likelihood of application compromise due +to code injection or code tampering. It also adds complexity around version +management and application testing. Finally, it can make it impossible to +verify the behavior of an application, so it may be prohibited in some +environments.</p> + +<p>If your application does dynamically load code, the most important thing to +keep in mind about dynamically loaded code is that it runs with the same +security permissions as the application APK. The user made a decision to +install your application based on your identity, and they are expecting that +you provide any code run within the application, including code that is +dynamically loaded.</p> + +<p>The major security risk associated with dynamically loading code is that the +code needs to come from a verifiable source. If the modules are included +directly within your APK, then they cannot be modified by other applications. +This is true whether the code is a native library or a class being loaded using +<a href="{@docRoot}reference/dalvik/system/DexClassLoader.html"> +<code>DexClassLoader</code></a>. We have seen many instances of applications +attempting to load code from insecure locations, such as downloaded from the +network over unencrypted protocols or from world writable locations such as +external storage. These locations could allow someone on the network to modify +the content in transit, or another application on a users device to modify the +content, respectively.</p> + + +<h3>Using WebView</h3> + +<p>Since WebView consumes web content that can include HTML and JavaScript, +improper use can introduce common web security issues such as <a +href="http://en.wikipedia.org/wiki/Cross_site_scripting">cross-site-scripting</a +> (JavaScript injection). Android includes a number of mechanisms to reduce +the scope of these potential issues by limiting the capability of WebView to +the minimum functionality required by your application.</p> + +<p>If your application does not directly use JavaScript within a <code><a +href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, do +not call +<a href="{@docRoot}reference/android/webkit/WebSettings.html#setJavaScriptEnabled(boolean) +<code>setJavaScriptEnabled()</code></a>. We have seen this method invoked +in sample code that might be repurposed in production application -- so +remove it if necessary. By default, <code><a +href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code> does +not execute JavaScript so cross-site-scripting is not possible.</p> + +<p>Use <code><a +href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(jav +a.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> with +particular care because it allows JavaScript to invoke operations that are +normally reserved for Android applications. Only expose <code><a +href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(jav +a.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> to +sources from which all input is trustworthy. If untrusted input is allowed, +untrusted JavaScript may be able to invoke Android methods. In general, we +recommend only exposing <code><a +href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(jav +a.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> to +JavaScript that is contained within your application APK.</p> + +<p>Do not trust information downloaded over HTTP, use HTTPS instead. Even if +you are connecting only to a single website that you trust or control, HTTP is +subject to <a +href="http://en.wikipedia.org/wiki/Man-in-the-middle_attack">MiTM</a> attacks +and interception of data. Sensitive capabilities using <code><a +href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(jav +a.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> should +not ever be exposed to unverified script downloaded over HTTP. Note that even +with the use of HTTPS, +<code><a +href="{@docRoot}reference/android/webkit/WebView.html#addJavascriptInterface(jav +a.lang.Object,%20java.lang.String)">addJavaScriptInterface()</a></code> +increases the attack surface of your application to include the server +infrastructure and all CAs trusted by the Android-powered device.</p> + +<p>If your application accesses sensitive data with a <code><a +href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, you +may want to use the <code><a +href="{@docRoot}reference/android/webkit/WebView.html#clearCache(boolean)"> +clearCache()</a></code> method to delete any files stored locally. Server side +headers like no-cache can also be used to indicate that an application should +not cache particular content.</p> + +<a name="Input"></a> +<h2>Performing Input Validation</h2> + +<p>Insufficient input validation is one of the most common security problems +affecting applications, regardless of what platform they run on. Android does +have platform-level countermeasures that reduce the exposure of applications to +input validation issues, you should use those features where possible. Also +note that selection of type-safe languages tends to reduce the likelihood of +input validation issues. We strongly recommend building your applications with +the Android SDK.</p> + +<p>If you are using native code, then any data read from files, received over +the network, or received from an IPC has the potential to introduce a security +issue. The most common problems are <a +href="http://en.wikipedia.org/wiki/Buffer_overflow">buffer overflows</a>, <a +href="http://en.wikipedia.org/wiki/Double_free#Use_after_free">use after +free</a>, and <a +href="http://en.wikipedia.org/wiki/Off-by-one_error">off-by-one errors</a>. +Android provides a number of technologies like ASLR and DEP that reduce the +exploitability of these errors, but they do not solve the underlying problem. +These can be prevented by careful handling of pointers and managing of +buffers.</p> + +<p>Dynamic, string based languages such as JavaScript and SQL are also subject +to input validation problems due to escape characters and <a +href="http://en.wikipedia.org/wiki/Code_injection">script injection</a>.</p> + +<p>If you are using data within queries that are submitted to SQL Database or a +Content Provider, SQL Injection may be an issue. The best defense is to use +parameterized queries, as is discussed in the ContentProviders section. +Limiting permissions to read-only or write-only can also reduce the potential +for harm related to SQL Injection.</p> + +<p>If you are using <code><a +href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, then +you must consider the possibility of XSS. If your application does not +directly use JavaScript within a <code><a +href="{@docRoot}reference/android/webkit/WebView.html">WebView</a></code>, do +not call setJavaScriptEnabled() and XSS is no longer possible. If you must +enable JavaScript then the WebView section provides other security best +practices.</p> + +<p>If you cannot use the security features above, we strongly recommend the use +of well-structured data formats and verifying that the data conforms to the +expected format. While blacklisting of characters or character-replacement can +be an effective strategy, these techniques are error-prone in practice and +should be avoided when possible.</p> + +<a name="UserData"></a> +<h2>Handling User Data</h2> + +<p>In general, the best approach is to minimize use of APIs that access +sensitive or personal user data. If you have access to data and can avoid +storing or transmitting the information, do not store or transmit the data. +Finally, consider if there is a way that your application logic can be +implemented using a hash or non-reversible form of the data. For example, your +application might use the hash of an an email address as a primary key, to +avoid transmitting or storing the email address. This reduces the chances of +inadvertently exposing data, and it also reduces the chance of attackers +attempting to exploit your application.</p> + +<p>If your application accesses personal information such as passwords or +usernames, keep in mind that some jurisdictions may require you to provide a +privacy policy explaining your use and storage of that data. So following the +security best practice of minimizing access to user data may also simplify +compliance.</p> + +<p>You should also consider whether your application might be inadvertently +exposing personal information to other parties such as third-party components +for advertising or third-party services used by your application. If you don't +know why a component or service requires a personal information, don’t +provide it. In general, reducing the access to personal information by your +application will reduce the potential for problems in this area.</p> + +<p>If access to sensitive data is required, evaluate whether that information +must be transmitted to a server, or whether the operation can be performed on +the client. Consider running any code using sensitive data on the client to +avoid transmitting user data.</p> + +<p>Also, make sure that you do not inadvertently expose user data to other +application on the device through overly permissive IPC, world writable files, +or network sockets. This is a special case of permission redelegation, +discussed in the Requesting Permissions section.</p> + +<p>If a GUID is required, create a large, unique number and store it. Do not +use phone identifiers such as the phone number or IMEI which may be associated +with personal information. This topic is discussed in more detail in the <a +href="http://android-developers.blogspot.com/2011/03/identifying-app-installatio +ns.html">Android Developer Blog</a>.</p> + +<h3>Handling Credentials</h3> + +<p>In general, we recommend minimizing the frequency of asking for user +credentials -- to make phishing attacks more conspicuous, and less likely to be +successful. Instead use an authorization token and refresh it.</p> + +<p>Where possible, username and password should not be stored on the device. +Instead, perform initial authentication using the username and password +supplied by the user, and then use a short-lived, service-specific +authorization token.</p> + +<p>Services that will be accessible to multiple applications should be accessed +using <code> +<a href="{@docRoot}reference/android/accounts/AccountManager.html"> +AccountManager</a></code>. If possible, use the <code><a +href="{@docRoot}reference/android/accounts/AccountManager.html"> +AccountManager</a></code> class to invoke a cloud-based service and do not store +passwords on the device.</p> + +<p>After using <code><a +href="{@docRoot}reference/android/accounts/AccountManager.html"> +AccountManager</a></code> to retrieve an Account, check the <code><a +href="{@docRoot}reference/android/accounts/Account.html#CREATOR">CREATOR</a> +</code> before passing in any credentials, so that you do not inadvertently pass +credentials to the wrong application.</p> + +<p>If credentials are to be used only by applications that you create, then you +can verify the application which accesses the <code><a +href="{@docRoot}reference/android/accounts/AccountManager.html"> +AccountManager</a></code> using <code><a href="<code><a +href="{@docRoot}h/reference/android/content/pm/PackageManager.html#checkSignatur +es(java.lang.String,%20java.lang.String)">checkSignature()</a></code>. +Alternatively, if only one application will use the credential, you might use a +<code><a +href={@docRoot}reference/java/security/KeyStore.html">KeyStore</a></code> for +storage.</p> + +<a name="Crypto"></a> +<h2>Using Cryptography</h2> + +<p>In addition to providing data isolation, supporting full-filesystem +encryption, and providing secure communications channels Android provides a +wide array of algorithms for protecting data using cryptography.</p> + +<p>In general, try to use the highest level of pre-existing framework +implementation that can support your use case. If you need to securely +retrieve a file from a known location, a simple HTTPS URI may be adequate and +require no knowledge of cryptography on your part. If you need a secure +tunnel, consider using +<a href="{@docRoot}reference/javax/net/ssl/HttpsURLConnection.html"> +<code>HttpsURLConnection</code></a> or <code><a +href="{@docRoot}reference/javax/net/ssl/SSLSocket.html">SSLSocket</a></code>, +rather than writing your own protocol.</p> + +<p>If you do find yourself needing to implement your own protocol, we strongly +recommend that you not implement your own cryptographic algorithms. Use +existing cryptographic algorithms such as those in the implementation of AES or +RSA provided in the <code><a +href="{@docRoot}reference/javax/crypto/Cipher.html">Cipher</a></code> class.</p> + +<p>Use a secure random number generator ( +<a href="http://developer.android.com/reference/java/security/SecureRandom.html"> +<code>SecureRandom</code></a>) to initialize any cryptographic keys (<a +href="http://developer.android.com/reference/javax/crypto/KeyGenerator.html"> +<code>KeyGenerator</code></a>). Use of a key that is not generated with a secure random +number generator significantly weakens the strength of the algorithm, and may +allow offline attacks.</p> + +<p>If you need to store a key for repeated use, use a mechanism like <code><a +href={@docRoot}reference/java/security/KeyStore.html">KeyStore</a></code> that +provides a mechanism for long term storage and retrieval of cryptographic +keys.</p> + +<h2>Conclusion</h2> + +<p>Android provides developers with the ability to design applications with a +broad range of security requirements. These best practices will help you make +sure that your application takes advantage of the security benefits provided by +the platform.</p> + +<p>You can receive more information on these topics and discuss security best +practices with other developers in the <a +href="http://groups.google.com/group/android-security-discuss">Android Security +Discuss</a> Google Group</p> diff --git a/docs/html/guide/practices/tablets-and-handsets.jd b/docs/html/guide/practices/tablets-and-handsets.jd index 7bc1ad7c84b9..dc35801ea78a 100644 --- a/docs/html/guide/practices/tablets-and-handsets.jd +++ b/docs/html/guide/practices/tablets-and-handsets.jd @@ -372,9 +372,8 @@ tips to follow when creating your action bar:</p> value</strong>. In your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, use {@code "ifRoom"} for the {@code android:showAsAction} attribute if you'd like the menu item to appear in the action -bar. However, you might need {@code "always"} when an action view does not provide an alternative -action for the overflow menu (that is, it must appear as an action view) or when a menu item added -by a fragment is low in the menu order and it must jump into the action bar at all times. However, +bar. However, you might need {@code "always"} when an action view does not provide a default +action for the overflow menu (that is, it must appear as an action view). However, you should not use {@code "always"} more than once or twice. In almost all other cases, use {@code "ifRoom"} as the value for {@code "android:showAsAction"} when you want the item to appear as an action item. Forcing too many action items into the action bar can create a cluttered UI and diff --git a/docs/html/guide/publishing/licensing.jd b/docs/html/guide/publishing/licensing.jd index fc0de9d2ce6f..609241b7d0a2 100644 --- a/docs/html/guide/publishing/licensing.jd +++ b/docs/html/guide/publishing/licensing.jd @@ -100,9 +100,14 @@ application. </p> <h4>Application, Android Market client, and server</h4> <p>The licensing service is based on the capability of the Android Market server -to determine whether a given user is licensed to use a given application. The -server considers a user licensed if the user is recorded to have purchased the -application, or if the application is available for free. To properly identify +to determine whether a given user is licensed to use a given application. The licensing server +considers a user to be licensed if the user is a recorded purchaser of an application. If a paid +application has been uploaded to Android Market but saved only as a draft application (in +other words, the app is unpublished), the licensing server considers all users to be licensed users +of the application. Keep in mind, you cannot implement Android Market Licensing in a free +application.</p> + +<p>To properly identify the user and determine the license status, the server requires information about the application and user — the application and the Android Market client work together to assemble the information and pass it to the server. </p> @@ -243,7 +248,7 @@ application: </p> <ul> <li>Only paid applications published through Market can use the -service. </li> +service.</li> <li>An application can use the service only if the Android Market client is installed on its host device and the device is running Android 1.5 (API level 3) or higher.</li> @@ -258,8 +263,8 @@ practices in this document, you can help ensure that your implementation will be secure.</li> <li>Adding licensing to an application does not affect the way the application functions when run on a device that does not offer Android Market.</li> -<li>Licensing is currently for paid apps only, since free apps are considered -licensed for all users. If your application is already published as free, +<li>Licensing is currently for paid apps only, since draft apps are +licensed for all users. If your application is already published as a free app, you won't be able to upload a new version that uses licensing.</li> </ul> @@ -698,7 +703,7 @@ haven't done that already, do it now before continuing. </p> <p>Next, open the application's project properties window, as shown below. Select the "Android" properties group and click <strong>Add</strong>, then choose the LVL library project (com_android_vending_licensing) and click -<strong>OK</strong>. For more information, see +<strong>OK</strong>. For more information, see <a href="{@docRoot}guide/developing/projects/projects-eclipse.html#SettingUpLibraryProject"> Managing Projects from Eclipse with ADT</a></p>. @@ -727,7 +732,7 @@ properties, including the reference to the library project:</p> --library <em>path/to/my/library_project</em> </pre> -<p>For more information about working with library projects, +<p>For more information about working with library projects, see <a href="{@docRoot}guide/developing/projects/projects-cmdline.html#SettingUpLibraryProject"> Managing Projects from the Command Line</a></p>. @@ -1226,6 +1231,8 @@ whether there is a valid license response cached locally, in <li>Otherwise, the LicenseChecker initiates a license check request that is sent to the licensing server.</li> </ul> +<p class="note"><strong>Note:</strong> The licensing server always returns +<code>LICENSED</code> when you perform a license check of a draft application.</p> </li> <li>When a response is received, LicenseChecker creates a LicenseValidator that verifies the signed license data and extracts the fields of the response, then @@ -1384,7 +1391,7 @@ methods post to the Handler.</li> </ol> <p>If you want your LicenseCheckerCallback methods to update the UI thread, -instantiate a {@link android.os.Handler} in the main Activity's +instantiate a {@link android.os.Handler} in the main Activity's {@link android.app.Activity#onCreate(android.os.Bundle) onCreate()} method, as shown below. In this example, the LVL sample application's LicenseCheckerCallback methods (see above) call <code>displayResult()</code> to @@ -2338,7 +2345,7 @@ Policy <code>processServerResonse()</code> method with a "RETRY" response code. </p> <p>In general, the RETRY response code is a signal to the application that an -error has occurred that has prevented a license check from completing. +error has occurred that has prevented a license check from completing. <p>The Android Market server helps an application to manage licensing under error conditions by setting a retry "grace period" and a recommended maximum diff --git a/docs/html/guide/topics/data/backup.jd b/docs/html/guide/topics/data/backup.jd index dec21467d71b..79dfd883e39d 100644 --- a/docs/html/guide/topics/data/backup.jd +++ b/docs/html/guide/topics/data/backup.jd @@ -708,17 +708,9 @@ you must use synchronized statements each time you perform a read or write. For in any Activity where you read and write the file, you need an object to use as the intrinsic lock for the synchronized statements:</p> -<div class="sidebox-wrapper"> -<div class="sidebox"> -<p><strong>Interesting Fact:</strong></p> -<p>A zero-length array is lighter-weight than a normal Object, so it's great for an -intrinsic lock.</p> -</div> -</div> - <pre> // Object for intrinsic lock -static final Object[] sDataLock = new Object[0]; +static final Object sDataLock = new Object(); </pre> <p>Then create a synchronized statement with this lock each time you read or write the files. For diff --git a/docs/html/guide/topics/media/camera.jd b/docs/html/guide/topics/media/camera.jd index 877bded96393..b962f9686e89 100644 --- a/docs/html/guide/topics/media/camera.jd +++ b/docs/html/guide/topics/media/camera.jd @@ -29,6 +29,15 @@ parent.link=index.html </ol> </li> <li><a href="#saving-media">Saving Media Files</a></li> + <li><a href="#camera-features">Camera Features</a> + <ol> + <li><a href="#check-feature">Checking feature availability</a></li> + <li><a href="#using-features">Using camera features</a></li> + <li><a href="#metering-focus-areas">Metering and focus areas</a></li> + <li><a href="#face-detection">Face detection</a></li> + <li><a href="#time-lapse-video">Time lapse video</a></li> + </ol> + </li> </ol> <h2>Key Classes</h2> <ol> @@ -39,8 +48,7 @@ parent.link=index.html </ol> <h2>See also</h2> <ol> - <li><a href="{@docRoot}reference/android/hardware/Camera.html">Camera</a></li> - <li><a href="{@docRoot}reference/android/media/MediaRecorder.html">MediaRecorder</a></li> + <li><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></li> <li><a href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a></li> </ol> </div> @@ -64,7 +72,7 @@ manifest</a>.</li> <li><strong>Quick Picture or Customized Camera</strong> - How will your application use the camera? Are you just interested in snapping a quick picture or video clip, or will your application -provide a new way to use cameras? For a getting a quick snap or clip, consider +provide a new way to use cameras? For a getting a quick snap or clip, consider <a href="#intents">Using Existing Camera Apps</a>. For developing a customized camera feature, check out the <a href="#custom-camera">Building a Camera App</a> section.</li> @@ -85,7 +93,7 @@ classes:</p> <dl> <dt>{@link android.hardware.Camera}</dt> <dd>This class is the primary API for controlling device cameras. This class is used to take -pictures or videos when you are building a camera application.</a>.</dd> +pictures or videos when you are building a camera application.</dd> <dt>{@link android.view.SurfaceView}</dt> <dd>This class is used to present a live camera preview to the user.</dd> @@ -120,8 +128,8 @@ for example: <pre> <uses-feature android:name="android.hardware.camera" /> </pre> - <p>For a list of camera features, see the manifest <a -href="{@docRoot}guide/topics/manifest/uses-feature-element.html#features-reference">Features + <p>For a list of camera features, see the manifest +<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#hw-features">Features Reference</a>.</p> <p>Adding camera features to your manifest causes Android Market to prevent your application from being installed to devices that do not include a camera or do not support the camera features you @@ -148,6 +156,15 @@ application must request the audio capture permission. <uses-permission android:name="android.permission.RECORD_AUDIO" /> </pre> </li> + <li><strong>Location Permission</strong> - If your application tags images with GPS location +information, you must request location permission: +<pre> +<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> +</pre> +<p>For more information about getting user location, see +<a href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User +Location</a>.</p> + </li> </ul> @@ -224,8 +241,8 @@ After the user finishes taking a picture (or cancels the operation), the user in your application, and you must intercept the {@link android.app.Activity#onActivityResult(int, int, android.content.Intent) onActivityResult()} method to receive the result of the intent and continue your application execution. For information -on how to receive the completed intent, see <a href="#intent-receive">Receiving Camera Intent -Result</a>.</p> +on how to receive the completed intent, see <a href="#intent-receive">Receiving camera intent +result</a>.</p> <h3 id="intent-video">Video capture intent</h3> @@ -360,8 +377,8 @@ properly release it for use by other applications.</li> <p>Camera hardware is a shared resource that must be carefully managed so your application does not collide with other applications that may also want to use it. The following sections discusses -how to detect camera hardware, how to request access to a camera and how to release it when your -application is done using it.</p> +how to detect camera hardware, how to request access to a camera, how to capture pictures or video +and how to release the camera when your application is done using it.</p> <p class="caution"><strong>Caution:</strong> Remember to release the {@link android.hardware.Camera} object by calling the {@link android.hardware.Camera#release() Camera.release()} when your @@ -492,7 +509,8 @@ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback // ignore: tried to stop a non-existent preview } - // make any resize, rotate or reformatting changes here + // set preview size and make any resize, rotate or + // reformatting changes here // start preview with new settings try { @@ -506,6 +524,12 @@ public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback } </pre> +<p>If you want to set a specific size for your camera preview, set this in the {@code +surfaceChanged()} method as noted in the comments above. When setting preview size, you +<em>must use</em> values from {@link android.hardware.Camera.Parameters#getSupportedPreviewSizes}. +<em>Do not</em> set arbitrary values in the {@link +android.hardware.Camera.Parameters#setPreviewSize setPreviewSize()} method.</p> + <h3 id="preview-layout">Placing preview in a layout</h3> <p>A camera preview class, such as the example shown in the previous section, must be placed in the @@ -780,6 +804,10 @@ without creating a camera preview first and skip the first few steps of this pro since users typically prefer to see a preview before starting a recording, that process is not discussed here.</p> +<p class="note"><strong>Tip:</strong> If your application is typically used for recording video, set +{@link android.hardware.Camera.Parameters#setRecordingHint} to {@code true} prior to starting your +preview. This setting can help reduce the time it takes to start recording.</p> + <h4 id="configuring-mediarecorder">Configuring MediaRecorder</h4> <p>When using the {@link android.media.MediaRecorder} class to record video, you must perform configuration steps in a <em>specific order</em> and then call the {@link @@ -851,7 +879,7 @@ setAudioChannels()}</li> <li>{@link android.media.MediaRecorder#setAudioSamplingRate(int) setAudioSamplingRate()}</li> </ul> -<h4 id="start-stop-mediarecorder">Starting and Stopping MediaRecorder</h4> +<h4 id="start-stop-mediarecorder">Starting and stopping MediaRecorder</h4> <p>When starting and stopping video recording using the {@link android.media.MediaRecorder} class, you must follow a specific order, as listed below.</p> @@ -938,7 +966,7 @@ public class CameraActivity extends Activity { private MediaRecorder mMediaRecorder; ... - + @Override protected void onPause() { super.onPause(); @@ -1052,4 +1080,425 @@ instead. For more information, see <a href="{@docRoot}guide/topics/data/data-storage.html#SavingSharedFiles">Saving Shared Files</a>.</p> <p>For more information about saving files on an Android device, see <a -href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.</p>
\ No newline at end of file +href="{@docRoot}guide/topics/data/data-storage.html">Data Storage</a>.</p> + + +<h2 id="camera-features">Camera Features</h2> +<p>Android supports a wide array of camera features you can control with your camera application, +such as picture format, flash mode, focus settings, and many more. This section lists the common +camera features, and briefly discusses how to use them. Most camera features can be accessed and set +using the through {@link android.hardware.Camera.Parameters} object. However, there are several +important features that require more than simple settings in {@link +android.hardware.Camera.Parameters}. These features are covered in the following sections:<p> + +<ul> + <li><a href="#metering-focus-areas">Metering and focus areas</a></li> + <li><a href="#face-detection">Face detection</a></li> + <li><a href="#time-lapse-video">Time lapse video</a></li> +</ul> + +<p>For general information about how to use features that are controlled through {@link +android.hardware.Camera.Parameters}, review the <a href="#using-features">Using camera +features</a> section. For more detailed information about how to use features controlled through the +camera parameters object, follow the links in the feature list below to the API reference +documentation.</p> + +<p class="table-caption" id="table1"> + <strong>Table 1.</strong> Common camera features sorted by the Android API Level in which they +were introduced.</p> +<table> + <tr> + <th>Feature</th> <th>API Level</th> <th>Description</th> + </tr> + <tr> + <td><a href="#face-detection">Face Detection</a></td> + <td>14</td> + <td>Identify human faces within a picture and use them for focus, metering and white +balance</td> + </tr> + <tr> + <td><a href="#metering-focus-areas">Metering Areas</a></td> + <td>14</td> + <td>Specify one or more areas within an image for calculating white balance</td> + </tr> + <tr> + <td><a href="#metering-focus-areas">Focus Areas</a></td> + <td>14</td> + <td>Set one or more areas within an image to use for focus</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setAutoWhiteBalanceLock White Balance Lock}</td> + <td>14</td> + <td>Stop or start automatic white balance adjustments</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setAutoExposureLock Exposure Lock}</td> + <td>14</td> + <td>Stop or start automatic exposure adjustments</td> + </tr> + <tr> + <td>{@link android.hardware.Camera#takePicture Video Snapshot}</td> + <td>14</td> + <td>Take a picture while shooting video (frame grab)</td> + </tr> + <tr> + <td><a href="#time-lapse-video">Time Lapse Video</a></td> + <td>11</td> + <td>Record frames with set delays to record a time lapse video</td> + </tr> + <tr> + <td>{@link android.hardware.Camera#open(int) Multiple Cameras}</td> + <td>9</td> + <td>Support for more than one camera on a device, including front-facing and back-facing +cameras</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#getFocusDistances Focus Distance}</td> + <td>9</td> + <td>Reports distances between the camera and objects that appear to be in focus</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setZoom Zoom}</td> + <td>8</td> + <td>Set image magnification</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setExposureCompensation Exposure +Compensation}</td> + <td>8</td> + <td>Increase or decrease the light exposure level</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setGpsLatitude GPS Data}</td> + <td>5</td> + <td>Include or omit geographic location data with the image</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setWhiteBalance White Balance}</td> + <td>5</td> + <td>Set the white balance mode, which affects color values in the captured image</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setFocusMode Focus Mode}</td> + <td>5</td> + <td>Set how the camera focuses on a subject such as automatic, fixed, macro or infinity</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setSceneMode Scene Mode}</td> + <td>5</td> + <td>Apply a preset mode for specific types of photography situations such as night, beach, snow +or candlelight scenes</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setJpegQuality JPEG Quality}</td> + <td>5</td> + <td>Set the compression level for a JPEG image, which increases or decreases image output file +quality and size</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setFlashMode Flash Mode}</td> + <td>5</td> + <td>Turn flash on, off, or use automatic setting</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setColorEffect Color Effects}</td> + <td>5</td> + <td>Apply a color effect to the captured image such as black and white, sepia tone or negative. +</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setAntibanding Anti-Banding}</td> + <td>5</td> + <td>Reduces the effect of banding in color gradients due to JPEG compression</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setPictureFormat Picture Format}</td> + <td>1</td> + <td>Specify the file format for the picture</td> + </tr> + <tr> + <td>{@link android.hardware.Camera.Parameters#setPictureSize Picture Size}</td> + <td>1</td> + <td>Specify the pixel dimensions of the saved picture</td> + </tr> +</table> + +<p class="note"><strong>Note:</strong> These features are not supported on all devices due to +hardware differences and software implementation. For information on checking the availability +of features on the device where your application is running, see <a href="#check-feature">Checking +feature availability</a>.</p> + + +<h3 id="check-feature">Checking feature availability</h3> +<p>The first thing to understand when setting out to use camera features on Android devices is that +not all camera features are supported on all devices. In addition, devices that support a particular +feature may support them to different levels or with different options. Therefore, part of your +decision process as you develop a camera application is to decide what camera features you want to +support and to what level. After making that decision, you should plan on including code in your +camera application that checks to see if device hardware supports those features and fails +gracefully if a feature is not available.</p> + +<p>You can check the availabilty of camera features by getting an instance of a camera’s parameters +object, and checking the relevant methods. The following code sample shows you how to obtain a +{@link android.hardware.Camera.Parameters} object and check if the camera supports the autofocus +feature:</p> + +<pre> +// get Camera parameters +Camera.Parameters params = mCamera.getParameters(); + +List<String> focusModes = params.getSupportedFocusModes(); +if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) { + // Autofocus mode is supported +} +</pre> + +<p>You can use the technique shown above for most camera features. The +{@link android.hardware.Camera.Parameters} object provides a {@code getSupported...()}, {@code +is...Supported()} or {@code getMax...()} method to determine if (and to what extent) a feature is +supported.</p> + +<p>If your application requires certain camera features in order to function properly, you can +require them through additions to your application manifest. When you declare the use of specific +camera features, such as flash and auto-focus, the Android Market restricts your application from +being installed on devices which do not support these features. For a list of camera features that +can be declared in your app manifest, see the manifest +<a href="{@docRoot}guide/topics/manifest/uses-feature-element.html#hw-features"> Features +Reference</a>.</p> + +<h3 id="using-features">Using camera features</h3> +<p>Most camera features are activated and controlled using a {@link +android.hardware.Camera.Parameters} object. You obtain this object by first getting an instance of +the {@link android.hardware.Camera} object, calling the {@link +android.hardware.Camera#getParameters getParameters()} method, changing the returned parameter +object and then setting it back into the camera object, as demonstrated in the following example +code:</p> + +<pre> +// get Camera parameters +Camera.Parameters params = mCamera.getParameters(); +// set the focus mode +params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO); +// set Camera parameters +mCamera.setParameters(params); +</pre> + +<p>This technique works for nearly all camera features, and most parameters can be changed at any +time after you have obtained an instance of the {@link android.hardware.Camera} object. Changes to +parameters are typically visible to the user immediately in the application’s camera preview. +On the software side, parameter changes may take several frames to actually take effect as the +camera hardware processes the new instructions and then sends updated image data.</p> + +<p class="caution"><strong>Important:</strong> Some camera features cannot be changed at will. In +particular, changing the size or orientation of the camera preview requires that you first stop the +preview, change the preview size, and then restart the preview. Starting with Android 4.0 (API +Level 14) preview orientation can be changed without restarting the preview.</p> + +<p>Other camera features require more code in order to implement, including:</p> +<ul> + <li>Metering and focus areas</li> + <li>Face detection</li> + <li>Time lapse video</li> +</ul> +<p>A quick outline of how to implement these features is provided in the following sections.</p> + + +<h3 id="metering-focus-areas">Metering and focus areas</h3> +<p>In some photographic scenarios, automatic focusing and light metering may not produce the +desired results. Starting with Android 4.0 (API Level 14), your camera application can provide +additional controls to allow your app or users to specify areas in an image to use for determining +focus or light level settings and pass these values to the camera hardware for use in capturing +images or video.</p> + +<p>Areas for metering and focus work very similarly to other camera features, in that you control +them through methods in the {@link android.hardware.Camera.Parameters} object. The following code +demonstrates setting two light metering areas for an instance of +{@link android.hardware.Camera}:</p> + +<pre> +// Create an instance of Camera +mCamera = getCameraInstance(); + +// set Camera parameters +Camera.Parameters params = mCamera.getParameters(); + +if (params.getMaxNumMeteringAreas() > 0){ // check that metering areas are supported + List<Camera.Area> meteringAreas = new ArrayList<Camera.Area>(); + + Rect areaRect1 = new Rect(-100, -100, 100, 100); // specify an area in center of image + meteringAreas.add(new Camera.Area(areaRect1, 600)); // set weight to 60% + Rect areaRect2 = new Rect(800, -1000, 1000, -800); // specify an area in upper right of image + meteringAreas.add(new Camera.Area(areaRect2, 400)); // set weight to 40% + params.setMeteringAreas(meteringAreas); +} + +mCamera.setParameters(params); +</pre> + +<p>The {@link android.hardware.Camera.Area} object contains two data parameters: A {@link +android.graphics.Rect} object for specifying an area within the camera’s field of view and a weight +value, which tells the camera what level of importance this area should be given in light metering +or focus calculations.</p> + +<p>The {@link android.graphics.Rect} field in a {@link android.hardware.Camera.Area} object +describes a rectangular shape mapped on a 2000 x 2000 unit grid. The coordinates -1000, -1000 +represent the top, left corner of the camera image, and coordinates 1000, 1000 represent the +bottom, right corner of the camera image, as shown in the illustration below.</p> + +<img src='images/camera-area-coordinates.png' /> +<p class="img-caption"> + <strong>Figure 1.</strong> The red lines illustrate the coordinate system for specifying a +{@link android.hardware.Camera.Area} within a camera preview. The blue box shows the location and +shape of an camera area with the {@link android.graphics.Rect} values 333,333,667,667. +</p> + +<p>The bounds of this coordinate system always correspond to the outer edge of the image visible in +the camera preview and do not shrink or expand with the zoom level. Similarly, rotation of the image +preview using {@link android.hardware.Camera#setDisplayOrientation Camera.setDisplayOrientation()} +does not remap the coordinate system.</p> + + +<h3 id="face-detection">Face detection</h3> +<p>For pictures that include people, faces are usually the most important part of the picture, and +should be used for determining both focus and white balance when capturing an image. The Android 4.0 +(API Level 14) framework provides APIs for identifying faces and calculating picture settings using +face recognition technology.</p> + +<p class="note"><strong>Note:</strong> While the face detection feature is running, +{@link android.hardware.Camera.Parameters#setWhiteBalance}, +{@link android.hardware.Camera.Parameters#setFocusAreas} and +{@link android.hardware.Camera.Parameters#setMeteringAreas} have no effect.</p> + +<p>Using the face detection feature in your camera application requires a few general steps:</p> +<ul> + <li>Check that face detection is supported on the device</li> + <li>Create a face detection listener</li> + <li>Add the face detection listener to your camera object</li> + <li>Start face detection after preview (and after <em>every</em> preview restart)</li> +</ul> + +<p>The face detection feature is not supported on all devices. You can check that this feature is +supported by calling {@link android.hardware.Camera.Parameters#getMaxNumDetectedFaces}. An +example of this check is shown in the {@code startFaceDetection()} sample method below.</p> + +<p>In order to be notified and respond to the detection of a face, your camera application must set +a listener for face detection events. In order to do this, you must create a listener class that +implements the {@link android.hardware.Camera.FaceDetectionListener} interface as shown in the +example code below.</p> + +<pre> +class MyFaceDetectionListener implements Camera.FaceDetectionListener { + + @Override + public void onFaceDetection(Face[] faces, Camera camera) { + if (faces.length > 0){ + Log.d("FaceDetection", "face detected: "+ faces.length + + " Face 1 Location X: " + faces[0].rect.centerX() + + "Y: " + faces[0].rect.centerY() ); + } + } +} +</pre> + +<p>After creating this class, you then set it into your application’s +{@link android.hardware.Camera} object, as shown in the example code below:</p> + +<pre> +mCamera.setFaceDetectionListener(new MyFaceDetectionListener()); +</pre> + +<p>Your application must start the face detection function each time you start (or restart) the +camera preview. Create a method for starting face detection so you can call it as needed, as shown +in the example code below.</p> + +<pre> +public void startFaceDetection(){ + // Try starting Face Detection + Camera.Parameters params = mCamera.getParameters(); + + // start face detection only *after* preview has started + if (params.getMaxNumDetectedFaces() > 0){ + // camera supports face detection, so can start it: + mCamera.startFaceDetection(); + } +} +</pre> + +<p>You must start face detection <em>each time</em> you start (or restart) the camera preview. If +you use the preview class shown in <a href="#camera-preview">Creating a preview class</a>, add your +{@link android.hardware.Camera#startFaceDetection startFaceDetection()} method to both the +{@link android.view.SurfaceHolder.Callback#surfaceCreated surfaceCreated()} and {@link +android.view.SurfaceHolder.Callback#surfaceChanged surfaceChanged()} methods in your preview class, +as shown in the sample code below.</p> + +<pre> +public void surfaceCreated(SurfaceHolder holder) { + try { + mCamera.setPreviewDisplay(holder); + mCamera.startPreview(); + + startFaceDetection(); // start face detection feature + + } catch (IOException e) { + Log.d(TAG, "Error setting camera preview: " + e.getMessage()); + } +} + +public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + + if (mHolder.getSurface() == null){ + // preview surface does not exist + Log.d(TAG, "mHolder.getSurface() == null"); + return; + } + + try { + mCamera.stopPreview(); + + } catch (Exception e){ + // ignore: tried to stop a non-existent preview + Log.d(TAG, "Error stopping camera preview: " + e.getMessage()); + } + + try { + mCamera.setPreviewDisplay(mHolder); + mCamera.startPreview(); + + startFaceDetection(); // re-start face detection feature + + } catch (Exception e){ + // ignore: tried to stop a non-existent preview + Log.d(TAG, "Error starting camera preview: " + e.getMessage()); + } +} +</pre> + +<p class="note"><strong>Note:</strong> Remember to call this method <em>after</em> calling +{@link android.hardware.Camera#startPreview startPreview()}. Do not attempt to start face detection +in the {@link android.app.Activity#onCreate onCreate()} method of your camera app’s main activity, +as the preview is not available by this point in your application's the execution.</p> + + +<h3 id="time-lapse-video">Time lapse video</h3> +<p>Time lapse video allows users to create video clips that combine pictures taken a few seconds or +minutes apart. This feature uses {@link android.media.MediaRecorder} to record the images for a time +lapse sequence. </p> + +<p>To record a time lapse video with {@link android.media.MediaRecorder}, you must configure the +recorder object as if you are recording a normal video, setting the captured frames per second to a +low number and using one of the time lapse quality settings, as shown in the code example below.</p> + +<pre> +// Step 3: Set a CamcorderProfile (requires API Level 8 or higher) +mMediaRecorder.setProfile(CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH)); +... +// Step 5.5: Set the video capture rate to a low number +mMediaRecorder.setCaptureRate(0.1); // capture a frame every 10 seconds +</pre> + +<p>These settings must be done as part of a larger configuration procedure for {@link +android.media.MediaRecorder}. For a full configuration code example, see <a +href="#configuring-mediarecorder">Configuring MediaRecorder</a>. Once the configuration is complete, +you start the video recording as if you were recording a normal video clip. For more information +about configuring and running {@link android.media.MediaRecorder}, see <a +href="#capture-video">Capturing videos</a>.</p> diff --git a/docs/html/guide/topics/media/images/camera-area-coordinates.png b/docs/html/guide/topics/media/images/camera-area-coordinates.png Binary files differnew file mode 100644 index 000000000000..9876453921c8 --- /dev/null +++ b/docs/html/guide/topics/media/images/camera-area-coordinates.png diff --git a/docs/html/guide/topics/media/index.jd b/docs/html/guide/topics/media/index.jd index 7c1754feb94f..0e0412a10d52 100644 --- a/docs/html/guide/topics/media/index.jd +++ b/docs/html/guide/topics/media/index.jd @@ -6,7 +6,7 @@ page.title=Multimedia and Camera <h2>Topics</h2> <ol> -<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">MediaPlayer</a></li> +<li><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></li> <li><a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a></li> <li><a href="{@docRoot}guide/topics/media/camera.html">Camera</a></li> <li><a href="{@docRoot}guide/topics/media/audio-capture.html">Audio Capture</a></li> @@ -46,7 +46,8 @@ hardware.</p> and playback.</p> <dl> - <dt><strong><a href="{@docRoot}guide/topics/media/mediaplayer.html">MediaPlayer</a></strong></dt> + <dt><strong><a href="{@docRoot}guide/topics/media/mediaplayer.html">Media Playback</a></strong> + </dt> <dd>How to play audio and video in your application.</dd> <dt><strong><a href="{@docRoot}guide/topics/media/jetplayer.html">JetPlayer</a></strong></dt> diff --git a/docs/html/guide/topics/providers/calendar-provider.jd b/docs/html/guide/topics/providers/calendar-provider.jd index 3ab51259a518..d30dda423ec5 100644 --- a/docs/html/guide/topics/providers/calendar-provider.jd +++ b/docs/html/guide/topics/providers/calendar-provider.jd @@ -280,11 +280,9 @@ Calendars.ACCOUNT_NAME}, you must also include {@link android.provider.CalendarContract.Calendars#ACCOUNT_TYPE Calendars.ACCOUNT_TYPE} in the selection. That is because a given account is only considered unique given both its <code>ACCOUNT_NAME</code> and its -<code>ACCOUNT_TYPE</code>. The <code>ACCOUNT_TYPE</code> refers to the way that -the account is being synced. It is often but not always the domain. For -example, an account could be synced through a corporate pop3 sync adapter, in which -case the <code>ACCOUNT_TYPE</code> would not be a domain. There is also a -special type of account called {@link +<code>ACCOUNT_TYPE</code>. The <code>ACCOUNT_TYPE</code> is the string corresponding to the +account authenticator that was used when the account was registered with the +{@link android.accounts.AccountManager}. There is also a special type of account called {@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} for calendars not associated with a device account. {@link android.provider.CalendarContract#ACCOUNT_TYPE_LOCAL} accounts do not get diff --git a/docs/html/guide/topics/ui/actionbar.jd b/docs/html/guide/topics/ui/actionbar.jd index 4742923891c8..3c0ef26ef5fb 100644 --- a/docs/html/guide/topics/ui/actionbar.jd +++ b/docs/html/guide/topics/ui/actionbar.jd @@ -8,179 +8,249 @@ parent.link=index.html <h2>Quickview</h2> <ul> - <li>A replacement for the title bar that includes the application icon and activity title</li> - <li>Provides action items from the Options Menu and modes of navigating around the -application</li> - <li>Supports custom views, including an embedded search box</li> - <li>Requires API Level 11</li> + <li>A title bar that includes the application icon and activity title</li> + <li>Provides access to menu items and navigation modes such as tabs</li> + <li>Requires API level 11 or greater</li> </ul> <h2>In this document</h2> - <ol> - <li><a href="#Adding">Adding the Action Bar</a> - <ol> - <li><a href="#Removing">Removing the Action Bar</a></li> - </ol> - </li> - <li><a href="#ActionItems">Adding Action Items</a> - <ol> - <li><a href="#Home">Using the app icon as an action item</a></li> - </ol> - </li> - <li><a href="#ActionView">Adding an Action View</a></li> - <li><a href="#Tabs">Adding Tabs</a></li> - <li><a href="#Dropdown">Adding Drop-down Navigation</a></li> - <li><a href="#Style">Styling the Action Bar</a></li> - </ol> +<ol> + <li><a href="#Adding">Adding the Action Bar</a> + <ol> + <li><a href="#Removing">Removing the action bar</a></li> + </ol> + </li> + <li><a href="#ActionItems">Adding Action Items</a> + <ol> + <li><a href="#ChoosingActionItems">Choosing your action items</a></li> + <li><a href="#SplitBar">Using split action bar</a></li> + </ol> + </li> + <li><a href="#Home">Using the App Icon for Navigation</a> + <ol> + <li><a href="#Up">Navigating up</a></li> + </ol> + </li> + <li><a href="#ActionView">Adding an Action View</a> + <ol> + <li><a href="#ActionViewCollapsing">Handling collapsible action views</a></li> + </ol> + </li> + <li><a href="#ActionProvider">Adding an Action Provider</a> + <ol> + <li><a href="#ShareActionProvider">Using the ShareActionProvider</a></li> + <li><a href="#CreatingActionProvider">Creating a custom action provider</a></li> + </ol> + </li> + <li><a href="#Tabs">Adding Navigation Tabs</a></li> + <li><a href="#Dropdown">Adding Drop-down Navigation</a></li> + <li><a href="#Style">Styling the Action Bar</a> + <ol> + <li><a href="#GeneralStyles">General appearance</a></li> + <li><a href="#ActionItemStyles">Action items</a></li> + <li><a href="#NavigationStyles">Navigation tabs</a></li> + <li><a href="#DropDownStyles">Drop-down lists</a></li> + <li><a href="#AdvancedStyles">Advanced styling</a></li> + </ol> + </li> +</ol> <h2>Key classes</h2> <ol> <li>{@link android.app.ActionBar}</li> <li>{@link android.view.Menu}</li> + <li>{@link android.view.ActionProvider}</li> </ol> - + <h2>Related samples</h2> <ol> - <li><a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#ActionBar">API - Demos</a></li> <li><a href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a></li> + <li><a +href="{@docRoot}resources/samples/ActionBarCompat/index.html">Action Bar Compatibility</a></li> + <li><a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/index.html#ActionBar"> +API Demos</a></li> </ol> - - <h2>See also</h2> + + <h2>See also</h2>item <ol> <li><a href="{@docRoot}guide/topics/ui/menus.html">Menus</a></li> + <li><a href="{@docRoot}guide/practices/tablets-and-handsets.html">Supporting Tablets +and Handsets</a></li> </ol> </div> </div> -<p>The Action Bar is a widget for activities that replaces the traditional title bar at -the top of the screen. By default, the Action Bar includes the application logo on the left side, -followed by the activity title, and any available items from the Options Menu on the right side. The -Action Bar offers several useful features, including the ability to:</p> +<p>The action bar is a window feature that identifies the application and user location, and +provides user actions and navigation modes. You should use the action bar in most activities that +need to prominently present user actions or global navigation, because the action bar offers users a +consistent interface across applications and the system gracefully adapts the action bar's +appearance for different screen configurations. You can control the behaviors and visibility of the +action bar with the {@link android.app.ActionBar} APIs, which were added in Android 3.0 (API level +11).</p> + +<p>The primary goals of the action bar are to:</p> <ul> - <li>Display items from the <a -href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">Options Menu</a> directly in the Action -Bar, as "action -items"—providing instant access to key user actions. - <p>Menu items that do not appear as action items are placed in the overflow menu, revealed -by a drop-down list in the Action Bar.</p></li> - <li>Provide tabs for navigating between <a -href="{@docRoot}guide/topics/fundamentals/fragments.html">fragments</a>.</li> - <li>Provide a drop-down list for navigation.</li> - <li>Provide interactive "action views" in place of action items (such as a search box).</li> -</ul> + <li>Provide a dedicated space for identifying the application brand and user location. + <p>This is accomplished with the app icon or logo on the left side and the activity title. +You might choose to remove the activity title, however, if the current view is identified by a +navigation label, such as the currently selected tab.</p></li> + + <li>Provide consistent navigation and view refinement across different applications. + <p>The action bar provides built-in tab navigation for switching between <a +href="{@docRoot}guide/topics/fundamentals/fragments.html">fragments</a>. It also offers a drop-down +list you can use as an alternative navigation mode or to refine the current view (such as to sort +a list by different criteria).</p> + </li> -<img src="{@docRoot}images/ui/actionbar.png" height="36" alt="" /> + <li>Make key actions for the activity (such as "search", "create", "share", etc.) prominent and +accessible to the user in a predictable way. + <p>You can provide instant access to key user actions by placing items from the <a +href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a> directly in the action bar, +as "action items." Action items can also provide an "action view," which provides an embedded +widget for even more immediate action behaviors. Menu items that are not promoted +to an action item are available in the overflow menu, revealed by either the device MENU button +(when available) or by an "overflow menu" button in the action bar (when the device does not +include a MENU button).</p> +</li> +</ul> -<p class="img-caption"><strong>Figure 1.</strong> A screenshot of the Action Bar in the Email -application, containing action items to compose new email and refresh the inbox.</p> +<img src="{@docRoot}images/ui/actionbar.png" alt="" /> +<p class="img-caption"><strong>Figure 1.</strong> Action bar from the <a +href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> app (on a +landscape handset), showing the logo on the left, navigation tabs, and an action item on the +right (plus the overflow menu button).</p> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h2>Remaining backward-compatible</h2> +<p>If you want to provide an action bar in your application <em>and</em> remain compatible with +versions of Android older than 3.0, you need to create the action bar in your +activity's layout (because the {@link android.app.ActionBar} class is not available on older +versions).</p> +<p>To help you, the <a +href="{@docRoot}resources/samples/ActionBarCompat/index.html">Action Bar Compatibility</a> sample +app provides an API layer and action bar layout that allows your app to use some of the {@link +android.app.ActionBar} APIs and also support older versions of Android by replacing the traditional +title bar with a custom action bar layout.</p> +</div> +</div> <h2 id="Adding">Adding the Action Bar</h2> -<p>The Action Bar is included by default in all activities that target Android 3.0 or greater. More -specifically, all activities that use the new "holographic" theme include the Action Bar, and any -application that targets Android 3.0 automatically receives this theme. An application is considered -to "target" Android 3.0 when it has set either the {@code android:minSdkVersion} or {@code -android:targetSdkVersion} attribute in the <a -href="{@docRoot}guide/topics/manifest/uses-sdk-element.html">{@code <uses-sdk>}</a> element to -{@code "11"} or greater. For example:</p> +<p>Beginning with Android 3.0 (API level 11), the action bar is included in all +activities that use the {@link android.R.style#Theme_Holo Theme.Holo} theme (or one of its +descendants), which is the default theme when either the <a +href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#target">{@code targetSdkVersion}</a> or +<a href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> +attribute is set to {@code "11"} or greater. For example:</p> <pre> -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.example.helloworld" - android:versionCode="1" - android:versionName="1.0"> +<manifest ... > <uses-sdk android:minSdkVersion="4" <b>android:targetSdkVersion="11"</b> /> - <application ... > - ... - </application> + ... </manifest> </pre> -<p>In this example, the application requires a minimum version of API -Level 4 (Android 1.6), but it also targets API Level 11 (Android 3.0). This way, when -the application is installed on a device running Android 3.0 or greater, the system applies the -holographic theme to each activity, and thus, each activity includes the Action Bar.</p> +<p>In this example, the application requires a minimum version of API Level 4 (Android 1.6), but it +also targets API level 11 (Android 3.0). This way, when the application runs on Android 3.0 or +greater, the system applies the holographic theme to each activity, and thus, each activity includes +the action bar.</p> -<p>However, if you want to use Action Bar APIs, such as to add tabs or modify Action Bar styles, -you need to set the {@code android:minSdkVersion} to {@code "11"}, so you can access the -{@link android.app.ActionBar} class.</p> +<p>If you want to use {@link android.app.ActionBar} APIs, such as to add navigation modes and modify +action bar styles, you should set the <a +href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> to {@code +"11"} or greater. If you want your app +to support older versions of Android, there are ways to use a limited set of {@link +android.app.ActionBar} APIs on devices that support API level 11 or higher, while still running +on older versions. See the sidebox for information about remaining backward-compatible.</p> -<h3 id="Removing">Removing the Action Bar</h3> +<h3 id="Removing">Removing the action bar</h3> -<p>If you want to remove the Action Bar for a particular activity, set the activity theme to +<p>If you don't want the action bar for a particular activity, set the activity theme to {@link android.R.style#Theme_Holo_NoActionBar Theme.Holo.NoActionBar}. For example:</p> <pre> <activity android:theme="@android:style/Theme.Holo.NoActionBar"> </pre> -<p class="note"><strong>Tip:</strong> If you have a custom activity theme in which you'd like to -remove the Action Bar, set the {@link android.R.styleable#Theme_windowActionBar -android:windowActionBar} style property {@code false}. See <a href="#Style">Styling the Action -Bar</a> for more about Action Bar styles.</p> - -<p>You can also hide the Action Bar at runtime by calling {@link android.app.ActionBar#hide}, -then show it again by calling {@link android.app.ActionBar#show}. For example:</p> +<p>You can also hide the action bar at runtime by calling {@link android.app.ActionBar#hide}. For +example:</p> <pre> -ActionBar actionBar = getActionBar(); +ActionBar actionBar = {@link android.app.Activity#getActionBar()}; actionBar.hide(); </pre> -<p>When the Action Bar hides, the system adjusts your activity content to fill all the -available screen space.</p> +<p>When the action bar hides, the system adjusts your activity layout to fill all the +screen space now available. You can bring the action bar back with {@link +android.app.ActionBar#show()}.</p> + +<p>Beware that hiding and removing the action bar causes your activity to re-layout in order to +account for the space consumed by the action bar. If your activity regularly hides and shows the +action bar (such as in the Android Gallery app), you might want to use overlay mode. Overlay mode +draws the action bar on top of your activity layout rather than in its own area of the screen. This +way, your layout remains fixed when the action bar hides and re-appears. To enable overlay mode, +create a theme for your activity and set {@link android.R.attr#windowActionBarOverlay +android:windowActionBarOverlay} to {@code true}. For more information, see the section about <a +href="#Style">Styling the Action Bar</a>.</p> + +<p class="note"><strong>Tip:</strong> If you have a custom activity theme in which you'd like to +remove the action bar, set the {@link android.R.styleable#Theme_windowActionBar +android:windowActionBar} style property to {@code false}. However, if you remove the action bar +using a theme, then the window will not allow the action bar at all, so you cannot add it +later—calling {@link android.app.Activity#getActionBar()} will return null.</p> -<p class="note"><strong>Note:</strong> If you remove the Action Bar using a theme, then the -window will not allow the Action Bar at all, so you cannot add it at runtime—calling -{@link android.app.Activity#getActionBar getActionBar()} will return null.</p> <h2 id="ActionItems">Adding Action Items</h2> -<p>An action item is simply a menu item from the <a -href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">Options Menu</a> which you declare should -appear directly in the Action Bar. An action item can include an icon and/or text. If a menu -item does not appear as an action item, then the system places it in the overflow menu, which -the user can open with the menu icon on the right side of the Action Bar.</p> +<p>Sometimes you might want to give users immediate access to an item from the <a +href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a>. To do this, you can +declare that the menu item should appear in the action bar as an "action item." An action item can +include an icon and/or a text title. If a menu item does not appear as an action item, then the +system places it in the overflow menu. The overflow menu is revealed either by the device MENU +button (if provided by the device) or an additional button in the action bar (if the device does not +provide the MENU button).</p> <div class="figure" style="width:359px"> <img src="{@docRoot}images/ui/actionbar-item-withtext.png" height="57" alt="" /> - <p class="img-caption"><strong>Figure 2.</strong> A screenshot from an Action Bar with two -action items and the overflow menu.</p> + <p class="img-caption"><strong>Figure 2.</strong> Two action items with icon and text titles, and +the overflow menu button.</p> </div> -<p>When the activity first starts, the system populates the Action Bar and overflow menu by calling +<p>When the activity first starts, the system populates the action bar and overflow menu by calling {@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} for your activity. As -discussed in the <a href="{@docRoot}guide/topics/ui/menus.html">Menus</a> developer guid, it's in -this callback method that you define the Options Menu for the activity.</p> +discussed in the <a href="{@docRoot}guide/topics/ui/menus.html">Menus</a> developer guide, it's in +this callback method that you should inflate an XML <a +href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> that defines the +menu items. For example:</p> -<p>You can specify a menu item to appear as an action item—if there is room -for it—from your <a href="{@docRoot}guide/topics/resources/menu-resource.html">menu -resource</a> by declaring {@code +<pre> +@Override +public boolean onCreateOptionsMenu(Menu menu) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.main_activity, menu); + return true; +} +</pre> + +<p>In the XML file, you can request a menu item to appear as an action item by declaring {@code android:showAsAction="ifRoom"} for the {@code <item>} element. This way, the menu item appears -in the Action Bar for quick access only if there is room available for it. If there's not -enough room, the item is placed the overflow menu (revealed by the menu icon on the right side -of the Action Bar).</p> - -<p>You can also declare a menu item to appear as an action item from your application code, by -calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()} on the {@link -android.view.MenuItem} and passing {@link android.view.MenuItem#SHOW_AS_ACTION_IF_ROOM}.</p> - -<p>If your menu item supplies both a title and an icon, then the action item shows only -the icon by defult. If you want to include the text with the action item, add the "with -text" flag: in XML, add {@code withText} to the {@code android:showAsAction} attribute or, in -your application code, use the {@link android.view.MenuItem#SHOW_AS_ACTION_WITH_TEXT} flag when -calling {@link android.view.MenuItem#setShowAsAction setShowAsAction()}. Figure 2 shows an Action -Bar that has two action items with text and the icon for the overflow menu.</p> - -<p>Here's an example of how you can declare a menu item as an action item in a <a -href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> file:</p> +in the action bar for quick access only <em>if there is room</em> available. If there's not +enough room, the item appears in the overflow menu.</p> + +<p>If your menu item supplies both a title and an icon—with the {@code android:title} and +{@code android:icon} attributes—then the action item shows only the icon by default. If you +want to display the text title, add {@code "withText"} to the {@code android:showAsAction} +attribute. For example:</p> + <pre> <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> @@ -191,58 +261,200 @@ href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a> fil </menu> </pre> -<p>In this case, both the {@code ifRoom} and {@code withText} flags are set, so that when this -item appears as an action item, it includes the title text along with the icon.</p> +<p class="note"><strong>Note:</strong> The {@code "withText"} value is a <em>hint</em> to the +action bar that the text title should appear. The action bar will show the title when possible, but +might not if an icon is available and the action bar is constrained for space.</p> -<p>A menu item placed in the Action Bar triggers the same callback methods as other items in the -Options Menu. When the user selects an action item, your activity receives a call to -{@link android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()}, passing the -item ID.</p> +<p>When the user selects an action item, your activity receives a call to +{@link android.app.Activity#onOptionsItemSelected(MenuItem) +onOptionsItemSelected()}, passing the ID supplied by the {@code android:id} attribute—the same +callback received for all items in the options menu.</p> -<p class="note"><strong>Note:</strong> If you added the menu item from a fragment, then the -respective {@link -android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method is called -for that fragment. However the activity gets a chance to handle it first, so the system calls {@link -android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} on the activity -before calling the fragment.</p> - -<p>You can also declare an item to <em>always</em> appear as an action item, but you should avoid -doing so, because it can create a cluttered UI if there are too many action items and they might -collide with other elements in the Action Bar.</p> +<p>It's important that you always define {@code android:title} for each menu item—even if you +don't declare that the title appear with the action item—for three reasons:</p> +<ul> + <li>If there's not enough room in the action bar for the action item, the menu item appears +in the overflow menu and only the title appears.</li> + <li>Screen readers for sight-impaired users read the menu item's title.</li> + <li>If the action item appears with only the icon, a user can long-press the item to reveal a +tool-tip that displays the action item's title.</li> +</ul> -<p>For more information about menus, see the <a +<p>The {@code android:icon} is always optional, but recommended. For icon design recommendations, +see the <a href="{@docRoot}guide/practices/ui_guidelines/icon_design_action_bar.html">Action Bar +Icon</a> design guidelines.</p> + +<p class="note"><strong>Note:</strong> If you added the menu item from a fragment, via the {@link +android.app.Fragment} class's {@link android.app.Fragment#onCreateOptionsMenu onCreateOptionsMenu} +callback, then the system calls the respective {@link +android.app.Fragment#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} method for that +fragment when the user selects one of the fragment's items. However the activity gets a chance to +handle the event first, so the system calls {@link +android.app.Activity#onOptionsItemSelected(MenuItem) onOptionsItemSelected()} on the activity before +calling the same callback for the fragment.</p> + +<p>You can also declare an item to <em>"always"</em> appear as an action item, instead of being +placed in the overflow menu when space is limited. In most cases, you <strong>should not</strong> +force an item to appear in the action bar by using the {@code "always"} value. However, you might +need an item to always appear when it provides an <a href="#ActionView">action view</a> that does +not offer a default action for the overflow menu. Beware that too +many action items can create a cluttered UI and cause layout problems on devices with a narrow +screen. It's best to instead use {@code "ifRoom"} to request that an item appear in the action +bar, but allow the system to move it into the overflow menu when there's not enough room.</p> + +<p>For more information about creating the options menu that defines your action items, see the <a href="{@docRoot}guide/topics/ui/menus.html#options-menu">Menus</a> developer guide.</p> -<h3 id="Home">Using the app icon as an action item</h3> -<p>By default, your application icon appears in the Action Bar on the left side. It also responds -to user interaction (when the user taps it, it visually responds the same way action -items do) and it's your responsibility to do something when the user taps it.</p> +<h3 id="ChoosingActionItems">Choosing your action items</h3> -<img src="{@docRoot}images/ui/actionbar.png" height="36" alt="" /> -<p class="img-caption"><strong>Figure 3.</strong> Email's Action Bar, with the -application icon on the left.</p> +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h4>Menu items vs. other app controls</h4> + <p>As a general rule, all items in the <a +href="{@docRoot}guide/topics/ui/menus.html#OptionsMenu">options menu</a> (let alone action items) +should have a global impact on the app, rather than affect only a small portion of the interface. +For example, if you have a multi-pane layout and one pane shows a video while another lists all +videos, the video player controls should appear within the pane containing the video (not in the +action bar), while the action bar might provide action items to share the video or save the video to +a favorites list.</p> + <p>So, even before deciding whether a menu item should appear as an action item, be sure that +the item has a global scope for the current activity. If it doesn't, then you should place it +as a button in the appropriate context of the activity layout.</p> +</div> +</div> -<p>The normal behavior should be for your application to return to the "home" activity or the -initial state (such as when the activity hasn't changed, but fragments have changed) when the user -taps the icon. If the user is already at home or the initial state, then you don't need to do -anything.</p> +<p>You should carefully choose which items from your options menu should appear as action items by +assessing a few key traits. In general, each action item should be <em>at least one</em> +of the following:</p> -<p>When the user taps the icon, the system calls your activity's {@link +<ol> + <li><strong>Frequently used</strong>: It's an action that your users need seven out of ten visits +or they use it several times in a row. + <p>Example frequent actions: "New message" in the Messaging app and +"Search" in Android Market.</p> + </li> + + <li><strong>Important</strong>: It's an action that you need users to easily discover or, if it's +not frequently used, it's important that it be effortless to perform in the few cases that users do +need it. + <p>Example important actions: "Add network" in Wi-Fi settings and "Switch to camera" in the +Gallery app.</p> + </li> + + <li><strong>Typical</strong>: It's an action that is typically provided in the action bar in +similar apps, so your users expect to find it in yours. + <p>Example typical actions: "Refresh" in an email or social app, and "New contact" in the +People app.</p> +</ol> + +<p>If you believe that more than four of your menu items can be justified as action items, then you +should carefully consider their relative level of importance and try to set no more than four as +action items (and do so using the {@code "ifRoom"} value to allow the system to put some back in the +overflow menu when space is limited on smaller screens). Even if space is available on a wide +screen, you should not create a long stream of action items that clutter the UI and appear like a +desktop toolbar, so keep the number of action items to a minimum.</p> + +<p>Additionally, the following actions should never appear as action items: Settings, Help, +Feedback, or similar. Always keep them in the overflow menu.</p> + +<p class="note"><strong>Note:</strong> Remember that not all devices provide a dedicated hardware +button for Search, so if it's an important feature in your app, it should always appear as an +action item (and usually as the first item, especially if you offer it with an <a +href="#ActionView">action view</a>).</p> + + + +<h3 id="SplitBar">Using split action bar</h3> + +<p>When your application is running on Android 4.0 (API level 14) and higher, there's an extra mode +available for the action bar called "split action bar." When you enable split action bar, a separate +bar appears at the bottom of the screen to display all action items when the activity is running on +a narrow screen (such as a portrait-oriented handset). Splitting the action bar to separate +the action items ensures that a reasonable amount of space is available to display all your action +items on a narrow screen, while leaving room for navigation and title elements at the top.</p> + +<p>To enable split action bar, simply add {@code uiOptions="splitActionBarWhenNarrow"} to your +<a href="{@docRoot}guide/topics/manifest/activity-element.html">{@code <activity>}</a> or +<a href="{@docRoot}guide/topics/manifest/application-element.html">{@code <application>}</a> +manifest element.</p> + +<p>Be aware that Android adjusts the action bar's appearance in a variety of ways, based on the +current screen size. Using split action bar is just one option that you can enable to allow the +action bar to further optimize the user experience for different screen sizes. In doing so, you +may also allow the action bar to collapse navigation tabs into the main action bar. That is, if you +use <a href="#Tabs">navigation tabs</a> in your action bar, once the action items are +separated on a narrow screen, the navigation tabs may be able to fit into the main action bar rather +than be separated into the "stacked action bar." Specifically, if you've disabled the action bar +icon and title (with {@link android.app.ActionBar#setDisplayShowHomeEnabled +setDisplayShowHomeEnabled(false)} and {@link android.app.ActionBar#setDisplayShowTitleEnabled +setDisplayShowTitleEnabled(false)}), then the navigation tabs collapse into the main action bar, as +shown by the second device in figure 3.</p> + +<img src="{@docRoot}images/practices/actionbar-phone-splitaction.png" alt=""/> +<p class="img-caption"><strong>Figure 3.</strong> Mock-ups of split action bar with navigation tabs +on the left; with the app icon and title disabled on the right.</p> + +<p class="note"><strong>Note:</strong> Although the {@link android.R.attr#uiOptions +android:uiOptions} attribute was added in Android 4.0 (API level 14), you can safely include it in +your application even if your <a +href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> is set to +a value lower than {@code "14"} to remain compatible with older versions of Android. When running on +older versions, the system simply ignores the XML attribute because it doesn't understand it. The +only condition to including it in your manifest is that you must compile your application against a +platform version that supports API level 14 or higher. Just be sure that you don't openly use other +APIs in your application code that aren't supported by the version declared by your <a +href="{@docRoot}guide/topics/manifest/uses-sdk-element.html#min">{@code minSdkVersion}</a> +attribute—only XML attributes are safely ignored by older platforms.</p> + + + + +<h2 id="Home">Using the App Icon for Navigation</h2> + + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h2>Using a logo instead of icon</h2> +<p>By default, the system uses your application icon in the action bar, as specified by the <a +href="{@docRoot}guide/topics/manifest/application-element.html#icon">{@code android:icon}</a> +attribute in the <a href="{@docRoot}guide/topics/manifest/application-element.html">{@code +<application>}</a> or <a +href="{@docRoot}guide/topics/manifest/activity-element.html">{@code +<activity>}</a> element. However, if you also specify the <a +href="{@docRoot}guide/topics/manifest/application-element.html#logo">{@code android:logo}</a> +attribute, then the action bar uses the logo image instead of the icon.</p> +<p>A logo should usually be wider than the icon, but should not include unnecessary text. You +should generally use a logo only when it represents your brand in a traditional format that users +recognize. A good example is the YouTube app's logo—the logo represents the expected user +brand, whereas the app's icon is a modified version that conforms to the square requirement.</p> +</div> +</div> + + +<p>By default, your application icon appears in the action bar on the left side. If you'd like, +you can enable the icon to behave as an action item. In response to user action on the icon, your +application should do one of two things:</p> + +<ul> + <li>Go to the application "home" activity, or</li> + <li>Navigate "up" the application's structural hierarchy</li> +</ul> + +<p>When the user touches the icon, the system calls your activity's {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method with the {@code -android.R.id.home} ID. So, you need to add a condition to your {@link -android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method to listen for {@code -android.R.id.home} and perform the appropriate action, such as start the home activity or pop recent -fragment transactions off the stack.</p> +android.R.id.home} ID. In response, you should either start the home activity or +take the user one step up in your application's structural hierarchy.</p> <p>If you respond to the application icon by returning to the home activity, you should include the {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag in the {@link android.content.Intent}. With this flag, if the activity you're starting already exists in the current task, then all activities on top of it are destroyed and it is brought to the front. -You should favor this approach, because going "home" is an action that's equivalent to "going +Adding this flag is often important because going "home" is an action that's equivalent to "going back" and you should usually not create a new instance of the home activity. Otherwise, you -might end up with a long stack of activities in the current task.</p> +might end up with a long stack of activities in the current task with multiple instances of the +home activity.</p> <p>For example, here's an implementation of {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} that returns to the application's "home" activity:</p> @@ -252,7 +464,7 @@ onOptionsItemSelected()} that returns to the application's "home" activity:</p> public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - // app icon in Action Bar clicked; go home + // app icon in action bar clicked; go home Intent intent = new Intent(this, HomeActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); @@ -263,297 +475,641 @@ public boolean onOptionsItemSelected(MenuItem item) { } </pre> +<p>In case the user can enter the current activity from another application, you might also want to +add the {@link android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag. This flag ensures that, when the +user navigates either "home" or "up", the new activity is <strong>not</strong> added to the current +task, but instead started in a task that belongs to your application. For example, if the user +starts an activity in your application through an intent invoked by another application, then +selects the action bar icon to navigate home or up, the {@link +android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag starts the activity in a task that belongs to +your application (not the current task). The system either starts a new task with your new activity +as the root activity or, if an existing task exists in the background with an instance of that +activity, then that task is brought forward and the target activity receives {@link +android.app.Activity#onNewIntent onNewIntent()}. So if your activity accepts intents from other +applications (it declares any generic intent filters), you should usually add the {@link +android.content.Intent#FLAG_ACTIVITY_NEW_TASK} flag to the intent:</p> + +<pre> +intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); +</pre> +<p>For more information about these flags and other back stack behaviors, read the <a +href="{@docRoot}guide/topics/fundamentals/tasks-and-back-stack.html">Tasks and Back Stack</a> +developer guide.</p> +<p class="note"><strong>Note:</strong> If you're using the icon to navigate to the home +activity, beware that beginning with Android 4.0 (API level 14), you must explicitly enable the +icon as an action item by calling {@link android.app.ActionBar#setHomeButtonEnabled +setHomeButtonEnabled(true)} (in previous versions, the icon was enabled as an action item by +default).</p> -<h4>Using the app icon to navigate "up"</h4> -<div class="figure" style="width:144px"> - <img src="{@docRoot}images/ui/actionbar-logo.png" height="140" alt="" /> - <p class="img-caption"><strong>Figure 4.</strong> The standard icon for the Email application -(top) and the "up" icon (bottom).</p> + +<h3 id="Up">Navigating up</h3> + +<div class="figure" style="width:230px;margin-top:-1em"> + <img src="{@docRoot}images/ui/actionbar-logo.png" alt="" /> + <p class="img-caption"><strong>Figure 4.</strong> The Email app's standard icon +(left) and the "navigate up" icon (right). The system automatically adds the "up" indicator.</p> </div> -<p>You can also use the application icon to provide "up" navigation for the user. This is especially -useful when your application is composed of activities that generally appear in a certain order and -you want to facilitate the ability for the user to navigate up the activity hierarchy -(regardless of how they entered the current activity).</p> +<p>As a supplement to traditional "back" navigation—which takes the user to the previous +screen in the task history—you can enable the action bar icon to offer "up" +navigation, which should take the user one step up in your application's structural hierarchy. For +instance, if the current screen is somewhere deep in the hierarchy of the application, touching the +app icon should navigate upward one level, to the parent of the current screen.</p> + +<p>For example, figure 5 illustrates how the BACK button behaves when the user navigates from one +application to an activity belonging to a different application (specifically, when composing an +email to a person selected from the People app).</p> -<p>The way you respond to this event is the same as when navigating home (as -discussed above, except you start a different activity, based on the current activity). All you -need to do to indicate to the user that the behavior is different is set the Action Bar to "show -home as up." You can do so by calling {@link android.app.ActionBar#setDisplayHomeAsUpEnabled -setDisplayHomeAsUpEnabled(true)} on your activity's {@link android.app.ActionBar}. When you do, the -system draws your application icon with an arrow indicating the up behavior, as shown in figure -4.</p> +<img src="{@docRoot}images/ui/actionbar-navigate-back.png" alt="" /> +<p class="img-caption"><strong>Figure 5.</strong> The BACK button behavior +after entering the Email app from the People (or Contacts) app.</p> -<p>For example, here's how you can show the application icon as an "up" action:</p> +<p>However, if the user wants to stay within the email application after composing the email, +up navigation allows the user to navigate upward in the email application, rather than go back +to the previous activity. Figure 6 illustrates this scenario, in which the user again comes into +the email application, but presses the action bar icon to navigate up, rather than back.</p> + +<img src="{@docRoot}images/ui/actionbar-navigate-up.png" alt="" /> +<p class="img-caption"><strong>Figure 6.</strong> Example behavior for UP navigation after +entering the Email app from the People app.</p> + +<p>To enable the icon for up navigation (which displays the "up" indicator next to the icon), call +{@link android.app.ActionBar#setDisplayHomeAsUpEnabled setDisplayHomeAsUpEnabled(true)} on your +{@link android.app.ActionBar}:</p> <pre> -@Override -protected void onStart() { - super.onStart(); - ActionBar actionBar = this.getActionBar(); +protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setContentView(R.layout.main); + ActionBar actionBar = getActionBar(); actionBar.setDisplayHomeAsUpEnabled(true); + ... } </pre> -<p>Then, your activity should respond to the user tapping the icon, from the {@link -android.app.Activity#onOptionsItemSelected -onOptionsItemSelected()}, by listening for the {@code android.R.id.home} ID (as shown above). In -this case, when navigating up, it's even more important that you use the {@link -android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag in the {@link android.content.Intent}, so that -you don't create a new instance of the parent activity if one already exists.</p> +<p>When the user touches the icon, the system calls your activity's {@link +android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} method with the {@code +android.R.id.home} ID, as shown in the above section about <a href="#Home">Using the App Icon +for Navigation</a>.</p> + +<p>Remember to use the {@link android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag in the {@link +android.content.Intent}, so that you don't create a new instance of the parent activity if one +already exists. For instance, if you don't use the {@link +android.content.Intent#FLAG_ACTIVITY_CLEAR_TOP} flag, then after navigating up, the BACK button will +actually take the user "forward", with respect to the application structure, which would be +strange.</p> + +<p class="note"><strong>Note:</strong> If there are many paths that the user could have taken to +reach the current activity within your application, the up icon should navigate backward along the +path the user actually followed to get to the current activity.</p> <h2 id="ActionView">Adding an Action View</h2> -<div class="figure" style="width:429px"> - <img src="{@docRoot}images/ui/actionbar-actionview.png" alt="" /> - <p class="img-caption"><strong>Figure 5.</strong> An action view with a {@link -android.widget.SearchView} widget.</p> +<div class="figure" style="width:300px;margin-top:-1em"> + <img src="/images/ui/actionbar-searchview.png" alt="" /> + <p class="img-caption"><strong>Figure 7.</strong> An action bar with a collapsed action +view for Search (top), then expanded action view with the <code><a +href="/reference/android/widget/SearchView.html">SearchView</a></code> widget (bottom).</p> </div> -<p>An action view is a widget that appears in the Action Bar as a substitute for an action -item. For example, if you have an item in the Options Menu for "Search", you can add an action view -for the item that provides a {@link android.widget.SearchView} widget in the Action Bar whenever -the item is enabled as an action item.</p> -<p>When adding an action view for a menu item, it's important that you still allow the item to -behave as a normal menu item when it does not appear in the Action Bar. For example, a menu item to -perform a search should, by default, bring up the Android search dialog, but if the item is -placed in the Action Bar, the action view appears with a {@link android.widget.SearchView} -widget. Figure 4 shows an example of the {@link android.widget.SearchView} widget in an action -view.</p> +<p>An action view is a widget that appears in the action bar as a substitute for an action item's +button. For example, if you have an item in the options menu for "Search," you can add an action +view that replaces the button with a {@link android.widget.SearchView} widget, as shown in figure +7.</p> -<p>The best way to declare an action view for an item is in your <a -href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, using the {@code -android:actionLayout} or {@code android:actionViewClass} attribute:</p> +<p>To declare an action view for an item in your <a +href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, use either the {@code +android:actionLayout} or {@code android:actionViewClass} attribute to specify either a layout +resource or widget class to use, respectively. For example:</p> -<ul> - <li>The value for {@code android:actionLayout} must be a resource pointer to a layout file. -For example: <pre> <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/menu_search" - android:title="Search" - android:icon="@drawable/ic_menu_search" - android:showAsAction="ifRoom" - <b>android:actionLayout="@layout/searchview"</b> /> + android:title="@string/menu_search" + android:icon="@drawable/ic_menu_search" + android:showAsAction="ifRoom|collapseActionView" + <b>android:actionViewClass="android.widget.SearchView"</b> /> </menu> </pre> -</li> - <li>The value for {@code android:actionViewClass} must be a fully-qualified class name for -the {@link android.view.View} you want to use. For example: +<p>Notice that the {@code android:showAsAction} attribute also includes {@code +"collapseActionView"}. This is optional and declares that the action view should be collapsed into a +button. When the user selects the button, the action view expands. Otherwise, the action view is +visible by default and might consume valuable action bar space even when the user is not using it. +For more information, see the next section about <a href="#ActionViewCollapsing">Handling +collapsible action views</a>.</p> + +<p>If you need to add some event hooks to your action view, you can do so during the {@link +android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()} callback. You can acquire elements +in an action view by calling {@link android.view.Menu#findItem findItem()} with the ID of the menu +item, then call {@link android.view.MenuItem#getActionView}. For +example, the search widget from the above sample is acquired like this:</p> + +<pre> +@Override +public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.options, menu); + SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); + // Configure the search info and add any event listeners + ... + return super.onCreateOptionsMenu(menu); +} +</pre> + +<p>For more information about using the search widget, see <a +href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>.</p> + + +<h3 id="ActionViewCollapsing">Handling collapsible action views</h3> + +<div class="sidebox-wrapper"> +<div class="sidebox"> + <h3>Supporting Android 3.0 with an action view</h3> + <p>The {@code "collapseActionView"} option was added with Android 4.0 (API level 14). However, if +your application supports older versions, you should +still declare {@code "collapseActionView"} in order to better support smaller screens. +Devices running Android 4.0 and higher will show the action view collapsed, while older versions +work as designed otherwise.</p> + <p>Adding this value requires that you set your build target to Android 4.0 or higher in order to +compile. Older versions of Android ignore the {@code "collapseActionView"} value because they don't +understand it. Just be sure not to use other APIs in your source code that are not supported in the +version declared by your <a href="{@docRoot}guide/topics/manifest/uses-sdk-elementl.html#min">{@code +minSdkVersion}</a>, unless you add the appropriate version check at runtime.</p> +</div> +</div> + + +<p>Action views allow you to provide fast access to rich actions without changing activities or +fragments, or replacing the action bar. However, it might not be appropriate to make an action view +visible by default. To preserve the action bar space (especially when running on smaller screens), +you can collapse your action view into an action item button. When the user selects the +button, the action view appears in the action bar. When collapsed, the system might place the item +into the overflow menu if you've defined {@code android:showAsAction} with {@code "ifRoom"}, but the +action view still appears in the action bar when the user selects the item. You can make your action +view collapsible by adding {@code "collapseActionView"} to the {@code android:showAsAction} +attribute, as shown in the XML above.</p> + +<p>Because the system will expand the action view when the user selects the item, so you +<em>do not</em> need to respond to the item in the {@link +android.app.Activity#onOptionsItemSelected onOptionsItemSelected} callback. The system still calls +{@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} when the user selects it, +but the system will always expand the action view unless you return {@code true} (indicating +you've handled the event instead).</p> + +<p>The system also collapses your action view when the user selects the "up" icon in the action +bar or presses the BACK button.</p> + +<p>If necessary, you can expand or collapse the action view in your own code by calling {@link +android.view.MenuItem#expandActionView()} and {@link android.view.MenuItem#collapseActionView()} on +the {@link android.view.MenuItem}.</p> + +<p class="note"><strong>Note:</strong> Although collapsing your action view is optional, we +recommend that you always collapse your action view if it includes {@link +android.widget.SearchView}. Also be aware that some devices provide a dedicated SEARCH button and +you should expand your search action view if the user presses the SEARCH button. Simply override +your activity's {@link android.app.Activity#onKeyUp onKeyUp()} callback method, listen for the +{@link android.view.KeyEvent#KEYCODE_SEARCH} event, then call {@link +android.view.MenuItem#expandActionView()}.</p> + +<p>If you need to update your activity based on the visibility of your action view, you can receive +callbacks when it's expanded and collapsed by defining an {@link +android.view.MenuItem.OnActionExpandListener OnActionExpandListener} and registering it with {@link +android.view.MenuItem#setOnActionExpandListener setOnActionExpandListener()}. For example:</p> + +<pre> +@Override +public boolean onCreateOptionsMenu(Menu menu) { + getMenuInflater().inflate(R.menu.options, menu); + MenuItem menuItem = menu.findItem(R.id.actionItem); + ... + + menuItem.setOnActionExpandListener(new OnActionExpandListener() { + @Override + public boolean onMenuItemActionCollapse(MenuItem item) { + // Do something when collapsed + return true; // Return true to collapse action view + } + + @Override + public boolean onMenuItemActionExpand(MenuItem item) { + // Do something when expanded + return true; // Return true to expand action view + } + }); +} +</pre> + + + + +<h2 id="ActionProvider">Adding an Action Provider</h2> + +<div class="figure" style="width:200px"> + <img src="{@docRoot}images/ui/actionbar-shareaction.png" alt="" /> + <p class="img-caption"><strong>Figure 8.</strong> Screenshot from the Gallery app, with the + {@link android.widget.ShareActionProvider} submenu expanded to show share targets.</p> +</div> + +<p>Similar to an <a href="#ActionView">action view</a>, an action provider (defined by the {@link +android.view.ActionProvider} class) replaces an action item with a customized layout, but it also +takes control of all the item's behaviors. When you declare an action provider for a menu +item in the action bar, it not only controls the appearance of the item in the action bar with a +custom layout, but also handles the default event for the menu item when it appears in the overflow +menu. It can also provide a submenu from either the action bar or the overflow menu.</p> + +<p>For example, the {@link android.widget.ShareActionProvider} is an extension of {@link +android.view.ActionProvider} that facilitates a “share" action by showing a list of available share +targets from the action bar. Instead of using a +traditional action item that invokes the {@link android.content.Intent#ACTION_SEND} intent, you can +declare an instance of {@link android.widget.ShareActionProvider} to handle an action item. This +action provider presents an action view with a drop-down list of applications that handle +the {@link android.content.Intent#ACTION_SEND} intent, even when the menu item appears in the +overflow menu. Hence, when you use an action provider such as this one, you don't +have to handle user events on the menu item.</p> + +<p>To declare an action provider for an action item, define the {@code android:actionProviderClass} +attribute for the appropriate the {@code <item>} element in your <a +href="{@docRoot}guide/topics/resources/menu-resource.html">menu resource</a>, using the +fully-qualified class name of the action provider. For example:</p> + <pre> <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/menu_search" - android:title="Search" - android:icon="@drawable/ic_menu_search" - android:showAsAction="ifRoom" - <b>android:actionViewClass="android.widget.SearchView"</b> /> + <item android:id="@+id/menu_share" + android:title="@string/share" + android:showAsAction="ifRoom" + <strong>android:actionProviderClass="android.widget.ShareActionProvider"</strong> /> + ... </menu> -</pre></li> -</ul> +</pre> + +<p>In this example, the {@link android.widget.ShareActionProvider} is used as the action provider. +At this point, the action provider officially takes control of the menu item and handles both +its appearance and behavior in the action bar and its behavior in the overflow menu. You must +still provide a text title for the item to be used in the overflow menu.</p> + +<p>Although the action provider can perform the default action for the menu item when it appears in +the overflow menu, your activity (or fragment) can override that behavior by +also handling the click event from the {@link android.app.Activity#onOptionsItemSelected +onOptionsItemSelected()} callback method. If you do not handle the event in that callback, then +the action provider receives the {@link android.view.ActionProvider#onPerformDefaultAction()} +callback to handle the event. However, if the action provider provides a submenu, then your +activity will not receive the {@link android.app.Activity#onOptionsItemSelected +onOptionsItemSelected()} callback, because the submenu is shown instead of invoking the default +menu item behavior when selected.</p> + + + +<h3 id="ShareActionProvider">Using the ShareActionProvider</h3> + +<p>If you want to provide a "share" action in your action bar by leveraging other applications +installed on the device (for example, to share a photo using a messaging or social app), then using +{@link android.widget.ShareActionProvider} is an effective way to do so, rather than adding an +action item that invokes the {@link android.content.Intent#ACTION_SEND} intent. When +you use {@link android.widget.ShareActionProvider} for an action item, it presents an action view +with a drop-down list of applications that handle the {@link android.content.Intent#ACTION_SEND} +intent (as shown in figure 8).</p> + +<p>All the logic for creating the submenu, populating it with share targets, and handling click +events (including when the item appears in the overflow menu) is implemented by the {@link +android.widget.ShareActionProvider}—the only code you need to write is to declare the action +provider for the menu item and specify the share intent.</p> + +<p>By default, the {@link android.widget.ShareActionProvider} retains a ranking for each +share target based on how often the user selects each one. The share targets used more frequently +appear at the top of the drop-down list and the target used most often appears directly in the +action bar as the default share target. By default, the ranking information is +saved in a private file with a name specified by {@link +android.widget.ShareActionProvider#DEFAULT_SHARE_HISTORY_FILE_NAME}. If you use the {@link +android.widget.ShareActionProvider} or an extension of it for only one type of action, then you +should continue to use this default history file and there's nothing you need to do. However, if you +use {@link android.widget.ShareActionProvider} or an extension of it for multiple actions with +semantically different meanings, then each {@link android.widget.ShareActionProvider} should specify +its own history file in order to maintain its own history. To specify a +different history file for the {@link android.widget.ShareActionProvider}, call {@link +android.widget.ShareActionProvider#setShareHistoryFileName setShareHistoryFileName()} and provide +an XML file name (for example, {@code "custom_share_history.xml"}).</p> + +<p class="note"><strong>Note:</strong> Although the {@link android.widget.ShareActionProvider} ranks +share targets based on frequency of use, the behavior is extensible and extensions of {@link +android.widget.ShareActionProvider} can perform different behaviors and ranking based on the history +file (if appropriate).</p> + +<p>To add {@link android.widget.ShareActionProvider}, simply define the {@code +android:actionProviderClass} attribute with {@code "android.widget.ShareActionProvider"}, as shown +in the XML example above. The only thing left to do is define +the {@link android.content.Intent} you want to use for sharing. To do so, you must call {@link +android.view.MenuItem#getActionProvider} to retrieve the {@link android.widget.ShareActionProvider} +that's associated with a {@link android.view.MenuItem}, then call {@link +android.widget.ShareActionProvider#setShareIntent setShareIntent()}.</p> + +<p>If the format for the share intent depends on the selected item or other variables that change +during the activity lifecycle, you should save the {@link android.widget.ShareActionProvider} in a +member field and update it by calling {@link android.widget.ShareActionProvider#setShareIntent +setShareIntent()} as necessary. For example:</p> -<p class="note">You must include {@code android:showAsAction="ifRoom"} in order for the item to -appear as an action view when room is available. If necessary, however, you can force the item to -always appear as an action view by setting {@code android:showAsAction} to {@code "always"}.</p> +<pre> +private ShareActionProvider mShareActionProvider; +... -<p>Now, when the menu item is displayed as an action item, it's action view appears instead of -the icon and/or title text. However, if there's not enough room in the Action Bar, the item appears -in the overflow menu as a normal menu item and you must respond to it from the {@link +@Override +public boolean onCreateOptionsMenu(Menu menu) { + mShareActionProvider = (ShareActionProvider) menu.findItem(R.id.menu_share).getActionProvider(); + + // If you use more than one ShareActionProvider, each for a different action, + // use the following line to specify a unique history file for each one. + // mShareActionProvider.setShareHistoryFileName("custom_share_history.xml"); + + // Set the default share intent + mShareActionProvider.setShareIntent(getDefaultShareIntent()); + + return true; +} +// When you need to update the share intent somewhere else in the app, call +// mShareActionProvider.{@link android.widget.ShareActionProvider#setShareIntent setShareIntent()} +</pre> + +<p>The {@link android.widget.ShareActionProvider} now handles all user interaction with the item and +you <em>do not</em> need to handle click events from the {@link android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} callback method.</p> -<p>When the activity first starts, the system populates the Action Bar and overflow menu by calling -{@link android.app.Activity#onCreateOptionsMenu onCreateOptionsMenu()}. -After you've inflated your menu in this method, you can acquire elements in an action view -(perhaps in order to attach listeners) by calling {@link android.view.Menu#findItem -findItem()} with the ID of the menu item, then {@link android.view.MenuItem#getActionView} on -the returned {@link android.view.MenuItem}. For example, the search widget from the above samples is -acquired like this:</p> +<p>For a sample using the share action provider, see +<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ActionBarActionProviderActivity.html" +>ActionBarActionProviderActivity</a>. + + + +<h3 id="CreatingActionProvider">Creating a custom action provider</h3> + +<p>When you want to create an action view that has dynamic behaviors and a default action in the +overflow menu, extending {@link android.view.ActionProvider} to define those behaviors is a good +solution. Creating your own action provider offers you an organized and reusable component, rather +than handling the various action item transformations and behaviors in your fragment or activity +code. As shown in the previous section, Android provides one implementation of {@link +android.view.ActionProvider} for share actions: the {@link android.widget.ShareActionProvider}.</p> +<p>To create your own, simply extend the {@link android.view.ActionProvider} class and implement +its callback methods as appropriate. Most importantly, you should implement the following:</p> + +<dl> + <dt>{@link android.view.ActionProvider#ActionProvider ActionProvider()}</dt> + <dd>This constructor passes you the application {@link android.content.Context}, which you +should save in a member field to use in the other callback methods.</dd> + + <dt>{@link android.view.ActionProvider#onCreateActionView()}</dt> + <dd>This is where you define the action view for the item. Use the {@link +android.content.Context} acquired from the constructor to instantiate a {@link +android.view.LayoutInflater} and inflate your action view layout from an XML resource, then hook +up event listeners. For example: <pre> -@Override -public boolean onCreateOptionsMenu(Menu menu) { - getMenuInflater().inflate(R.menu.options, menu); - SearchView searchView = (SearchView) menu.findItem(R.id.menu_search).getActionView(); - // Set appropriate listeners for searchView - ... - return super.onCreateOptionsMenu(menu); +public View onCreateActionView() { + // Inflate the action view to be shown on the action bar. + LayoutInflater layoutInflater = LayoutInflater.from(mContext); + View view = layoutInflater.inflate(R.layout.action_provider, null); + ImageButton button = (ImageButton) view.findViewById(R.id.button); + button.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + // Do something... + } + }); + return view; } </pre> +</dd> + + <dt>{@link android.view.ActionProvider#onPerformDefaultAction()}</dt> + <dd>The system calls this when the menu item is selected from the overflow menu and the +action provider should perform a default action for the menu item. + <p>However, if your action provider provides a submenu, through the {@link +android.view.ActionProvider#onPrepareSubMenu onPrepareSubMenu()} callback, then the submenu +appears even when the menu item is in the overflow menu. Thus, {@link +android.view.ActionProvider#onPerformDefaultAction()} is never called when there is a +submenu.</p> + <p class="note"><strong>Note:</strong> An activity or a fragment that implements {@link +android.app.Activity#onOptionsItemSelected onOptionsItemSelected()} can override the action +provider's default behavior by handling the item-selected event (and returning true), in which +case, the system does not call {@link android.view.ActionProvider#onPerformDefaultAction()}.</p> +</dd> +</dl> -<p>For more information about using the search widget, see <a -href="{@docRoot}guide/topics/search/search-dialog.html">Creating a Search Interface</a>.</p> +<p>For an example extension of {@link android.view.ActionProvider}, see <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/ActionBarSettingsActionProviderActivity.html" +>ActionBarSettingsActionProviderActivity</a>.</p> -<h2 id="Tabs">Adding Tabs</h2> +<h2 id="Tabs">Adding Navigation Tabs</h2> -<div class="figure" style="width:504px"> - <img src="{@docRoot}images/ui/actionbar-tabs.png" alt="" /> - <p class="img-caption"><strong>Figure 6.</strong> Screenshot of tabs in the -Action Bar, from the <a -href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> sample -application.</p> +<div class="figure" style="width:450px"> + <img src="{@docRoot}images/ui/actionbar.png" alt="" /> + <p class="img-caption"><strong>Figure 9.</strong> Screenshot of action bar tabs from the <a +href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> app.</p> </div> -<p>The Action Bar can display tabs that allow the user navigate between different fragments in the -activity. Each tab can include a title and/or an icon.</p> +<div class="figure" style="width:250px"> + <img src="{@docRoot}images/ui/actionbar-stacked.png" alt="" /> + <p class="img-caption"><strong>Figure 10.</strong> Screenshot of tabs in the stacked action +bar on a narrow screen.</p> +</div> + +<p>When you want to provide navigation tabs in an activity, using the action bar's +tabs is a great option (instead of using {@link android.widget.TabWidget}), because the +system adapts the action bar tabs for different screen sizes—placing them in the main action +bar when the screen is sufficiently wide, or in a separate bar (known as the "stacked action bar") +when the screen is too narrow, as shown in figures 9 and 10.</p> -<p>To begin, your layout must include a {@link android.view.View} in which each {@link -android.app.Fragment} associated with a tab is displayed. Be sure the view has an ID that you -can use to reference it from your code.</p> +<p>To switch between fragments using the tabs, you must perform a fragment +transaction each time a tab is selected. If you're not familiar with how to change fragments +using {@link android.app.FragmentTransaction}, first read the <a +href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> -<p>To add tabs to the Action Bar:</p> +<p>To get started, your layout must include a {@link android.view.ViewGroup} in which you place each +{@link android.app.Fragment} associated with a tab. Be sure the {@link android.view.ViewGroup} has a +resource ID so you can reference it from your tab-swapping code. Alternatively, if the tab content +will fill the activity layout (excluding the action bar), then your activity doesn't need a layout +at all (you don't even need to call {@link android.app.Activity#setContentView +setContentView()}). Instead, you can place each fragment in the default root {@link +android.view.ViewGroup}, which you can refer to with the {@code android.R.id.content} ID (you can +see this ID used in the sample code below, during fragment transactions).</p> + +<p>Once you determine where the fragments appear in the layout, the basic procedure to add tabs +is:</p> <ol> - <li>Create an implementation of {@link android.app.ActionBar.TabListener} to handle the -interaction events on the Action Bar tabs. You must implement all methods: {@link -android.app.ActionBar.TabListener#onTabSelected onTabSelected()}, {@link -android.app.ActionBar.TabListener#onTabUnselected onTabUnselected()}, and {@link -android.app.ActionBar.TabListener#onTabReselected onTabReselected()}. - <p>Each callback method passes the {@link android.app.ActionBar.Tab} that received the -event and a {@link android.app.FragmentTransaction} for you to perform the fragment -transactions (add or remove fragments).</p> - <p>For example:</p> -<pre> -private class MyTabListener implements ActionBar.TabListener { - private TabContentFragment mFragment; + <li>Implement the {@link android.app.ActionBar.TabListener} interface. Callbacks in this +interface respond to user events on the tabs so you can swap fragments.</li> + <li>For each tab you want to add, instantiate an {@link android.app.ActionBar.Tab} and set the +{@link android.app.ActionBar.TabListener} by calling {@link android.app.ActionBar.Tab#setTabListener +setTabListener()}. Also set the tab's title and/or icon with {@link +android.app.ActionBar.Tab#setText setText()} and/or {@link android.app.ActionBar.Tab#setIcon +setIcon()}.</li> + <li>Add each tab to the action bar by calling {@link android.app.ActionBar#addTab addTab()}.</li> +</ol> - // Called to create an instance of the listener when adding a new tab - public MyTabListener(TabContentFragment fragment) { - mFragment = fragment; +<p>When looking at the {@link android.app.ActionBar.TabListener} interface, notice that the +callback methods provide only the {@link android.app.ActionBar.Tab} that was selected and a {@link +android.app.FragmentTransaction} for you to perform fragment transactions—it doesn't say +anything about what fragment you should swap in or out. Thus, you must define your own association +between each {@link android.app.ActionBar.Tab} and the appropriate {@link android.app.Fragment} that +it represents (in order to perform the appropriate fragment transaction). There are several ways you +can define the association, depending on your design. In the example below, the {@link +android.app.ActionBar.TabListener} implementation provides a constructor such that each new tab uses +its own instance of the listener. Each instance of the listener defines several fields that are +necessary to later perform a transaction on the appropriate fragment.</p> + +<p>For example, here's how you might implement the {@link android.app.ActionBar.TabListener} +such that each tab uses its own instance of the listener:</p> +<pre> +public static class TabListener<T extends Fragment> implements ActionBar.TabListener { + private Fragment mFragment; + private final Activity mActivity; + private final String mTag; + private final Class<T> mClass; + + /** Constructor used each time a new tab is created. + * @param activity The host Activity, used to instantiate the fragment + * @param tag The identifier tag for the fragment + * @param clz The fragment's Class, used to instantiate the fragment + */ + public TabListener(Activity activity, String tag, Class<T> clz) { + mActivity = activity; + mTag = tag; + mClass = clz; } + /* The following are each of the {@link android.app.ActionBar.TabListener} callbacks */ + public void onTabSelected(Tab tab, FragmentTransaction ft) { - ft.add(R.id.fragment_content, mFragment, null); + // Check if the fragment is already initialized + if (mFragment == null) { + // If not, instantiate and add it to the activity + mFragment = Fragment.instantiate(mActivity, mClass.getName()); + ft.add(android.R.id.content, mFragment, mTag); + } else { + // If it exists, simply attach it in order to show it + ft.attach(mFragment); + } } public void onTabUnselected(Tab tab, FragmentTransaction ft) { - ft.remove(mFragment); + if (mFragment != null) { + // Detach the fragment, because another one is being attached + ft.detach(mFragment); + } } public void onTabReselected(Tab tab, FragmentTransaction ft) { - // do nothing + // User selected the already selected tab. Usually do nothing. } - } </pre> - <p>This implementation of {@link android.app.ActionBar.TabListener} adds a constructor -that saves the {@link android.app.Fragment} associated with a tab so that each callback -can add or remove that fragment.</p> - </li> - <li>Get the {@link android.app.ActionBar} for your activity by calling {@link -android.app.Activity#getActionBar} from your {@link android.app.Activity}, during {@link -android.app.Activity#onCreate onCreate()} (but be sure you do so <em>after</em> you've called -{@link android.app.Activity#setContentView setContentView()}).</li> - <li>Call {@link android.app.ActionBar#setNavigationMode(int) -setNavigationMode(NAVIGATION_MODE_TABS)} to enable tab mode for the {@link -android.app.ActionBar}.</li> - <li>Create each tab for the Action Bar: - <ol> - <li>Create a new {@link android.app.ActionBar.Tab} by calling {@link -android.app.ActionBar#newTab()} on the {@link android.app.ActionBar}.</li> - <li>Add title text and/or an icon for the tab by calling {@link -android.app.ActionBar.Tab#setText setText()} and/or {@link android.app.ActionBar.Tab#setIcon -setIcon()}. - <p class="note"><strong>Tip:</strong> These methods return the same {@link -android.app.ActionBar.Tab} instance, so you can chain the calls together.</p></li> - <li>Declare the {@link android.app.ActionBar.TabListener} to use for the tab by passing an -instance of your implementation to {@link android.app.ActionBar.Tab#setTabListener -setTabListener()}. - </ol> - </li> - <li>Add each {@link android.app.ActionBar.Tab} to the Action Bar by calling {@link -android.app.ActionBar#addTab addTab()} on the {@link android.app.ActionBar} and passing the -{@link android.app.ActionBar.Tab}.</li> -</ol> -<p>For example, the following code combines steps 2 - 5 to create two tabs and add them to -the Action Bar:</p> + +<p class="caution"><strong>Caution:</strong> You <strong>must not</strong> call {@link +android.app.FragmentTransaction#commit} for the fragment transaction in each of these +callbacks—the system calls it for you and it may throw an exception if you call it yourself. +You also <strong>cannot</strong> add these fragment transactions to the back stack.</p> + +<p>In this example, the listener simply attaches ({@link android.app.FragmentTransaction#attach +attach()}) a fragment to the activity layout—or if not instantiated, creates the fragment and +adds ({@link android.app.FragmentTransaction#add add()}) it to the layout (as a child of the {@code +android.R.id.content} view group)—when the respective tab is selected, and detaches ({@link +android.app.FragmentTransaction#detach detach()}) it when the tab is unselected.</p> + +<p>The {@link android.app.ActionBar.TabListener} implementation is the bulk of the work. All that +remains is to create each {@link android.app.ActionBar.Tab} and add it to the {@link +android.app.ActionBar}. Additionally, you must call {@link +android.app.ActionBar#setNavigationMode(int) setNavigationMode(NAVIGATION_MODE_TABS)} to make the +tabs visible. You might also want to disable the activity title by calling {@link +android.app.ActionBar#setDisplayShowTitleEnabled setDisplayShowTitleEnabled(false)} if the tab +titles actually indicate the current view.</p> + +<p>For example, the following code adds two tabs using the listener defined above:</p> + <pre> @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - setContentView(R.layout.main); + // Notice that setContentView() is not used, because we use the root + // android.R.id.content as the container for each fragment - // setup Action Bar for tabs - final ActionBar actionBar = getActionBar(); + // setup action bar for tabs + ActionBar actionBar = getActionBar(); actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); - // remove the activity title to make space for tabs actionBar.setDisplayShowTitleEnabled(false); - // instantiate fragment for the tab - Fragment artistsFragment = new ArtistsFragment(); - // add a new tab and set its title text and tab listener - actionBar.addTab(actionBar.newTab().setText(R.string.tab_artists) - .setTabListener(new TabListener(artistsFragment))); - - Fragment albumsFragment = new AlbumsFragment(); - actionBar.addTab(actionBar.newTab().setText(R.string.tab_albums) - .setTabListener(new TabListener(albumsFragment))); + Tab tab = actionBar.newTab() + .setText(R.string.artist) + .setTabListener(new TabListener<ArtistFragment>( + this, "artist", ArtistFragment.class)); + actionBar.addTab(tab); + + tab = actionBar.newTab() + .setText(R.string.album) + .setTabListener(new TabListener<AlbumFragment>( + this, "album", AlbumFragment.class)); + actionBar.addTab(tab); } </pre> -<p>All the behaviors that occur when a tab is selected must be defined by your {@link -android.app.ActionBar.TabListener} callback methods. When a tab is selected, it receives a call to -{@link android.app.ActionBar.TabListener#onTabSelected onTabSelected()} and that's where you should -add the appropriate fragment to the designated view in your layout, using {@link -android.app.FragmentTransaction#add add()} with the provided {@link -android.app.FragmentTransaction}. Likewise, when a tab is deselected (because another tab becomes -selected), you should remove that fragment from the layout, using {@link -android.app.FragmentTransaction#remove remove()}.</p> +<p class="note"><strong>Note:</strong> The above implementation for {@link +android.app.ActionBar.TabListener} is one of several possible techniques. You can see more of +this style in the <a +href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html" +>API Demos</a> app.</p> -<p class="caution"><strong>Caution:</strong> You <strong>must not</strong> call {@link -android.app.FragmentTransaction#commit} for these transactions—the system calls it for you -and it may throw an exception if you call it yourself. You also <strong>cannot</strong> add these -fragment transactions to the back stack.</p> -<p>If your activity is stopped, you should retain the currently selected tab with the saved state so -that when the user returns to your application, you can open the tab. When it's time to save the +<p>If your activity stops, you should retain the currently selected tab with the <a +href="{@docRoot}guide/topics/fundamentals/activities.html#SavingActivityState">saved instance +state</a> so you can open the appropriate tab when the user returns. When it's time to save the state, you can query the currently selected tab with {@link android.app.ActionBar#getSelectedNavigationIndex()}. This returns the index position of the selected tab.</p> -<p class="caution"><strong>Caution:</strong> It's important that you save -the state of each fragment as necessary, so when the user switches fragments with the tabs, -then returns to a previous fragment, it appears the way they left. For information about saving -the state of your fragment, see the <a -href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> developer guide.</p> +<p class="caution"><strong>Caution:</strong> It's important that you save the state of each fragment +as necessary, so that when users switch fragments with the tabs and then return to a previous +fragment, it looks the way it did when they left. For information about saving the state of your +fragment, see the <a href="{@docRoot}guide/topics/fundamentals/fragments.html">Fragments</a> +developer guide.</p> +<p class="note"><strong>Note:</strong> In some cases, the Android system will show your action +bar tabs as a drop-down list in order to ensure the best fit in the action bar.</p> <h2 id="Dropdown">Adding Drop-down Navigation</h2> -<p>As another mode of navigation within your activity, you can provide a drop-down list in the -Action Bar. For example, the drop-down list can provide alternative modes for sorting the content in -the activity or switching the user's account.</p> +<p>As another mode of navigation (or filtering) within your activity, the action bar offers a +built in drop-down list. For example, the drop-down list can offer different modes by which content +in the activity is sorted.</p> <!-- <div class="figure" style="width:135px"> <img src="{@docRoot}images/ui/actionbar-dropdown.png" alt="" /> <p class="img-caption"><strong>Figure 5.</strong> Screenshot of a drop-down navigation list in the -Action Bar.</p> +action bar.</p> </div> --> -<p>Here's a quick list of steps to enable drop-down navigation:</p> +<p>The basic procedure to enable drop-down navigation is:</p> <ol> <li>Create a {@link android.widget.SpinnerAdapter} that provides the list of selectable items for the drop-down and the layout to use when drawing each item in the list.</li> - <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior when the -user selects an item from the list.</li> - <li>Enable navigation mode for the Action Bar with {@link + <li>Implement {@link android.app.ActionBar.OnNavigationListener} to define the behavior that +occurs when the user selects an item from the list.</li> + <li>Enable navigation mode for the action bar with {@link android.app.ActionBar#setNavigationMode setNavigationMode()}. For example: <pre> ActionBar actionBar = getActionBar(); @@ -563,13 +1119,13 @@ actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_LIST); android.app.Activity#onCreate onCreate()} method.</p> </li> - <li>Then, set the callback for the drop-down list with {@link + <li>Set the callback for the drop-down list with {@link android.app.ActionBar#setListNavigationCallbacks setListNavigationCallbacks()}. For example: <pre> actionBar.setListNavigationCallbacks(mSpinnerAdapter, mNavigationCallback); </pre> <p>This method takes your {@link android.widget.SpinnerAdapter} and {@link -android.app.ActionBar.OnNavigationListener}. More about these next.</p> +android.app.ActionBar.OnNavigationListener}.</p> </li> </ol> @@ -594,7 +1150,7 @@ reveal the sample).</p> <div class="toggle-content-toggleme"> <p>{@link android.widget.SpinnerAdapter} is an adapter that provides data for a spinner widget, -such as the drop-down list in the Action Bar. {@link android.widget.SpinnerAdapter} is an interface +such as the drop-down list in the action bar. {@link android.widget.SpinnerAdapter} is an interface that you can implement, but Android includes some useful implementations that you can extend, such as {@link android.widget.ArrayAdapter} and {@link android.widget.SimpleCursorAdapter}. For example, here's an easy way to create a {@link @@ -713,139 +1269,198 @@ public class ListContentFragment extends Fragment { <h2 id="Style">Styling the Action Bar</h2> -<p>The Action Bar is the heading for your application and a primary interaction point for users, -so you might want to modify some of its design in order to make it feel more integrated with your -application design. There are several ways you can do this if you wish.</p> +<p>If you've implemented a custom design for the widgets in your application, you might +also want to redesign some of the action bar to match your app design. To do so, you need to use +Android's <a +href="{@docRoot}guide/topics/ui/themes.html">style and theme</a> framework to restyle the action +bar using special style properties.</p> -<p>For simple modifications to the {@link android.app.ActionBar}, you can use the following -methods:</p> -<dl> - <dt>{@link android.app.ActionBar#setBackgroundDrawable setBackgroundDrawable()}</dt> - <dd>Sets a drawable to use as the Action Bar's background. The drawable should be a <a -href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">Nine-patch</a> image, a <a -href="{@docRoot}guide/topics/resources/drawable-resource.html#Shape">shape</a>, or a <a -href="{@docRoot}guide/topics/resources/more-resources.html#Color">solid color</a>, so the system can -resize the drawable based on the size of the Action Bar (you should <em>not</em> use a fixed-size -bitmap image).</dd> - - <dt>{@link android.app.ActionBar#setDisplayUseLogoEnabled setDisplayUseLogoEnabled()}</dt> - <dd>Enables the use of an alternative image (a "logo") in the Action Bar, instead of the default -application icon. A logo is often a wider, more detailed image that represents the application. -When this is enabled, the system uses the logo image defined for the application (or the -individual activity) in the manifest file, with the <a -href="{@docRoot}guide/topics/manifest/application-element.html#logo">{@code android:logo}</a> -attribute. The logo will be resized as necessary to fit the height of the Action Bar. (Best -practice is to design the logo at the same size as your application icon.)</dd> -</dl> +<p class="note"><strong>Note:</strong> In order for background images to change appearance +depending on the current button state (selected, pressed, unselected), the drawable resource you use +must be a <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state +list drawable</a>.</p> +<p class="caution"><strong>Caution:</strong> For all background drawables you provide, be sure to use <a +href="{@docRoot}guide/topics/graphics/2d-graphics.html#nine-patch">Nine-Patch drawables</a> to allow +stretching. The Nine-Patch image should be <em>smaller</em> than 40px tall and 30px wide (for the mdpi asset).</p> -<p>For more complex customizations, you can use Android's <a -href="{@docRoot}guide/topics/ui/themes.html">style and theme</a> framework to restyle your Action -Bar in several ways.</p> -<p>The Action Bar has two standard themes, "dark" and "light". The dark theme is applied with -the default holographic theme, as specified by the {@link android.R.style#Theme_Holo Theme.Holo} -theme. If you want a white background with dark text, instead, you can apply the {@link -android.R.style#Theme_Holo_Light Theme.Holo.Light} theme to the activity in the manifest file. For -example:</p> +<h3 id="GeneralStyles">General appearance</h3> +<dl> + + <dt>{@link android.R.attr#windowActionBarOverlay + android:windowActionBarOverlay}</dt> + <dd>Declares whether the action bar should overlay the activity layout rather than offset the +activity's layout position (for example, the Gallery app uses overlay mode). This is +{@code false} by default. + <p>Normally, the action bar requires its own space on the screen and your activity layout fills in +what's left over. When the action bar is in overlay mode, your activity layout uses all the +available space and the system draws the action bar on top. Overlay mode can be useful if you want +your content to keep a fixed size and position when the action bar is hidden and shown. You might +also like to use it purely as a visual effect, because you can use a semi-transparent background +for the action bar so the user can still see some of your activity layout behind the action +bar.</p> + <p class="note"><strong>Note:</strong> The {@link android.R.style#Theme_Holo Holo} theme families +draw the action bar with a semi-transparent background by default. However, you can modify it with +your own styles and the {@link android.R.style#Theme_DeviceDefault DeviceDefault} theme on +different devices might use an opaque background by default.</p> + <p>When overlay mode is enabled, your activity layout has no awareness of the action bar laying on +top of it. So, you must be careful not to place any important information or UI components in the +area overlayed by the action bar. If appropriate, you can refer to the platform's value for {@link +android.R.attr#actionBarSize} to determine the height of the action bar, by referencing it +in your XML layout. For example:</p> <pre> -<activity android:name=".ExampleActivity" - android:theme="@android:style/Theme.Holo.Light" /> +<SomeView + ... + android:layout_marginTop="?android:attr/actionBarSize" /> </pre> + <p>You can also retrieve the action bar height at runtime with {@link +android.app.ActionBar#getHeight()}. This reflects the height of the action bar at the time it's +called, which might not include the stacked action bar (due to navigation tabs) if called during early +activity lifecycle methods. To see how you can determine the total height at runtime, including the +stacked action bar, see the <a +href="{@docRoot}resources/samples/HoneycombGallery/src/com/example/android/hcgallery/TitlesFragment.html" +>{@code TitlesFragment}</a> class in the <a +href="{@docRoot}resources/samples/HoneycombGallery/index.html" +>Honeycomb Gallery</a> sample app.</p> +</dd> + +</dl> -<p>For more control, you can override either the {@link android.R.style#Theme_Holo -Theme.Holo} or {@link android.R.style#Theme_Holo_Light Theme.Holo.Light} theme and apply custom -styles to certain aspects of the Action Bar. Some of the Action Bar properties you can customize -include the following:</p> + +<h3 id="ActionItemStyles">Action items</h3> <dl> - <dt>{@link android.R.styleable#Theme_actionBarTabStyle + <dt>{@link android.R.attr#actionButtonStyle + android:actionButtonStyle}</dt> + <dd>Defines a style resource for the action item buttons.</dd> + + <dt>{@link android.R.attr#actionBarItemBackground + android:actionBarItemBackground}</dt> + <dd>Defines a drawable resource for each action item's background. (Added in API level 14.)</dd> + + <dt>{@link android.R.attr#itemBackground + android:itemBackground}</dt> + <dd>Defines a drawable resource for each overflow menu item's background.</dd> + + <dt>{@link android.R.attr#actionBarDivider + android:actionBarDivider}</dt> + <dd>Defines a drawable resource for the divider between action items. (Added in API level +14.)</dd> + + <dt>{@link android.R.attr#actionMenuTextColor + android:actionMenuTextColor}</dt> + <dd>Defines a color for text that appears in an action item.</dd> + + <dt>{@link android.R.attr#actionMenuTextAppearance + android:actionMenuTextAppearance}</dt> + <dd>Defines a style resource for text that appears in an action item.</dd> + + <dt>{@link android.R.attr#actionBarWidgetTheme + android:actionBarWidgetTheme}</dt> + <dd>Defines a theme resource for widgets that are inflated into the action bar as <a +href="#ActionView">action views</a>. (Added in API level 14.)</dd> +</dl> + + +<h3 id="NavigationStyles">Navigation tabs</h3> + +<dl> + <dt>{@link android.R.attr#actionBarTabStyle android:actionBarTabStyle}</dt> - <dd>Style for tabs in the Action Bar.</dd> + <dd>Defines a style resource for tabs in the action bar.</dd> - <dt>{@link android.R.styleable#Theme_actionBarTabBarStyle + <dt>{@link android.R.attr#actionBarTabBarStyle android:actionBarTabBarStyle}</dt> - <dd>Style for the bar that appears below tabs in the Action Bar.</dd> + <dd>Defines a style resource for the thin bar that appears below the navigation tabs.</dd> - <dt>{@link android.R.styleable#Theme_actionBarTabTextStyle + <dt>{@link android.R.attr#actionBarTabTextStyle android:actionBarTabTextStyle}</dt> - <dd>Style for the text in the tabs.</dd> + <dd>Defines a style resource for text in the navigation tabs.</dd> +</dl> - <dt>{@link android.R.styleable#Theme_actionDropDownStyle - android:actionDropDownStyle}</dt> - <dd>Style for the drop-down list used for the overflow menu and drop-down navigation.</dd> - <dt>{@link android.R.styleable#Theme_actionButtonStyle - android:actionButtonStyle}</dt> - <dd>Style for the background image used for buttons in the Action Bar.</dd> +<h3 id="DropDownStyles">Drop-down lists</h3> +<dl> + <dt>{@link android.R.attr#actionDropDownStyle + android:actionDropDownStyle}</dt> + <dd>Defines a style for the drop-down navigation (such as the background and text styles).</dd> </dl> -<p>For example, here's a resource file that defines a custom theme for the Action Bar, based on -the standard {@link android.R.style#Theme_Holo Theme.Holo} theme:</p> + + +<p>For example, here's a file that defines a few custom styles for the action bar:</p> <pre> <?xml version="1.0" encoding="utf-8"?> <resources> <!-- the theme applied to the application or activity --> - <style name="CustomActionBar" parent="android:style/Theme.Holo.Light"> - <item name="android:actionBarTabTextStyle">@style/customActionBarTabTextStyle</item> - <item name="android:actionBarTabStyle">@style/customActionBarTabStyle</item> - <item name="android:actionBarTabBarStyle">@style/customActionBarTabBarStyle</item> + <style name="CustomActivityTheme" parent="@android:style/Theme.Holo"> + <item name="android:actionBarTabTextStyle">@style/CustomTabTextStyle</item> + <item name="android:actionBarDivider">@drawable/ab_divider</item> + <item name="android:actionBarItemBackground">@drawable/ab_item_background</item> </style> - <!-- style for the tab text --> - <style name="customActionBarTabTextStyle"> - <item name="android:textColor">#2966c2</item> - <item name="android:textSize">20sp</item> - <item name="android:typeface">sans</item> - </style> - - <!-- style for the tabs --> - <style name="customActionBarTabStyle"> - <item name="android:background">@drawable/actionbar_tab_bg</item> - <item name="android:paddingLeft">20dp</item> - <item name="android:paddingRight">20dp</item> - </style> - - <!-- style for the tab bar --> - <style name="customActionBarTabBarStyle"> - <item name="android:background">@drawable/actionbar_tab_bar</item> + <!-- style for the action bar tab text --> + <style name="CustomTabTextStyle"> + <item name="android:textColor">#2456c2</item> </style> </resources> </pre> -<p class="note"><strong>Note:</strong> In order for the tab background image to change, -depending on the current tab state (selected, pressed, unselected), the drawable resource used -must be a <a href="{@docRoot}guide/topics/resources/drawable-resource.html#StateList">state -list drawable</a>. Also be certain that your theme declares a parent theme, from which it -inherits all styles not explicitly declared in your theme.</p> +<p class="note"><strong>Note:</strong> Be certain that your theme declares a parent theme in the +{@code <style>} tag, from which it inherits all styles not explicitly declared by your theme. +When modifying the action bar, using a parent theme is important so that you can simply override the +action bar styles you want to change without re-implementing the styles you want to leave alone +(such as text appearance or padding in action items).</p> <p>You can apply your custom theme to the entire application or to individual activities in your -manifest file, like this:</p> +manifest file like this:</p> <pre> -<application android:theme="@style/CustomActionBar" +<application android:theme="@style/CustomActivityTheme" ... /> </pre> -<p>Additionally, if you want to create a custom theme for your activity that removes the Action -Bar completely, use the following style attributes:</p> +<p>For more information about using style and theme resources in your application, read <a +href="{@docRoot}guide/topics/ui/themes.html">Styles and Themes</a>.</p> -<dl> - <dt>{@link android.R.styleable#Theme_windowActionBar - android:windowActionBar}</dt> - <dd>Set this style property {@code false} to remove the Action Bar.</dd> - <dt>{@link android.R.styleable#Theme_windowNoTitle - android:windowNoTitle}</dt> - <dd>Set this style property {@code true} to also remove the traditional title bar.</dd> -</dl> - -<p>For more information about using themes in your application, read <a -href="{@docRoot}guide/topics/ui/themes.html">Styles and Themes</a>.</p> +<h3 id="AdvancedStyles">Advanced styling</h3> + + <p>If you need more advanced styling for the action bar than is available with the +properties above, you can include {@link android.R.attr#actionBarStyle android:actionBarStyle} and +{@link android.R.attr#actionBarSplitStyle android:actionBarSplitStyle} in your activity's theme. +Each of these specifies another style that can define various properties for the action bar, +including different backgrounds with {@link android.R.attr#background android:background}, {@link +android.R.attr#backgroundSplit android:backgroundSplit}, and {@link android.R.attr#backgroundStacked +android:backgroundStacked}. If you override these action bar styles, be sure that you define a +parent action bar style such as {@link android.R.style#Widget_Holo_ActionBar +Widget.Holo.ActionBar}.</p> + +<p>For example, if you want to change the action bar's background, you could use the following +styles:</p> + +<pre> +<?xml version="1.0" encoding="utf-8"?> +<resources> + <!-- the theme applied to the application or activity --> + <style name="CustomActivityTheme" parent="@android:style/Theme.Holo"> + <item name="android:actionBarTabTextStyle">@style/customTabTextStyle</item> + <!-- other activity and action bar styles here --> + </style> + + <!-- style for the action bar, simply to change the background --> + <style parent="@android:style/Widget.Holo.ActionBar"> + <item name="android:background">@drawable/ab_background</item> + <item name="android:backgroundSplit">@drawable/ab_background</item> + </style> +</resources> +</pre> + diff --git a/docs/html/guide/topics/ui/menus.jd b/docs/html/guide/topics/ui/menus.jd index 29482444d8e8..7b5b3dc2f3c1 100644 --- a/docs/html/guide/topics/ui/menus.jd +++ b/docs/html/guide/topics/ui/menus.jd @@ -198,10 +198,11 @@ public boolean onCreateOptionsMenu(Menu menu) { } </pre> -<div class="figure" style="width:500px"> -<img src="{@docRoot}images/ui/actionbar.png" height="34" alt="" /> -<p class="img-caption"><strong>Figure 2.</strong> Screenshot of the Action Bar in the Email -application, with two action items from the Options Menu, plus the overflow menu.</p> +<div class="figure" style="width:450px"> +<img src="{@docRoot}images/ui/actionbar.png" alt="" /> +<p class="img-caption"><strong>Figure 2.</strong> Action bar from the <a +href="{@docRoot}resources/samples/HoneycombGallery/index.html">Honeycomb Gallery</a> app, including +navigation tabs and a camera action item (plus the overflow menu button).</p> </div> <p>You can also populate the menu in code, using {@link android.view.Menu#add(int,int,int,int) diff --git a/docs/html/images/ui/actionbar-actionview.png b/docs/html/images/ui/actionbar-actionview.png Binary files differindex cc18f9b764e8..9103dc41745d 100644 --- a/docs/html/images/ui/actionbar-actionview.png +++ b/docs/html/images/ui/actionbar-actionview.png diff --git a/docs/html/images/ui/actionbar-item-withtext.png b/docs/html/images/ui/actionbar-item-withtext.png Binary files differindex 61742d3f62a8..5f7aecc900de 100644 --- a/docs/html/images/ui/actionbar-item-withtext.png +++ b/docs/html/images/ui/actionbar-item-withtext.png diff --git a/docs/html/images/ui/actionbar-logo.png b/docs/html/images/ui/actionbar-logo.png Binary files differindex df914bcf3d6e..c62b3e211492 100644 --- a/docs/html/images/ui/actionbar-logo.png +++ b/docs/html/images/ui/actionbar-logo.png diff --git a/docs/html/images/ui/actionbar-navigate-back.png b/docs/html/images/ui/actionbar-navigate-back.png Binary files differnew file mode 100644 index 000000000000..9431bcabcd4b --- /dev/null +++ b/docs/html/images/ui/actionbar-navigate-back.png diff --git a/docs/html/images/ui/actionbar-navigate-up.png b/docs/html/images/ui/actionbar-navigate-up.png Binary files differnew file mode 100644 index 000000000000..09243b47c772 --- /dev/null +++ b/docs/html/images/ui/actionbar-navigate-up.png diff --git a/docs/html/images/ui/actionbar-navigate-up.xcf b/docs/html/images/ui/actionbar-navigate-up.xcf Binary files differnew file mode 100644 index 000000000000..bf1849a587d4 --- /dev/null +++ b/docs/html/images/ui/actionbar-navigate-up.xcf diff --git a/docs/html/images/ui/actionbar-searchview.png b/docs/html/images/ui/actionbar-searchview.png Binary files differnew file mode 100644 index 000000000000..7ae46a0ac011 --- /dev/null +++ b/docs/html/images/ui/actionbar-searchview.png diff --git a/docs/html/images/ui/actionbar-shareaction.png b/docs/html/images/ui/actionbar-shareaction.png Binary files differnew file mode 100644 index 000000000000..fdbb02ccba3f --- /dev/null +++ b/docs/html/images/ui/actionbar-shareaction.png diff --git a/docs/html/images/ui/actionbar-stacked.png b/docs/html/images/ui/actionbar-stacked.png Binary files differnew file mode 100644 index 000000000000..76c8908b1a55 --- /dev/null +++ b/docs/html/images/ui/actionbar-stacked.png diff --git a/docs/html/images/ui/actionbar.png b/docs/html/images/ui/actionbar.png Binary files differindex dcd8449f18fe..1d01583a4bce 100644 --- a/docs/html/images/ui/actionbar.png +++ b/docs/html/images/ui/actionbar.png diff --git a/docs/html/resources/resources-data.js b/docs/html/resources/resources-data.js index 41a5a510fa11..a35e6848f1b9 100644 --- a/docs/html/resources/resources-data.js +++ b/docs/html/resources/resources-data.js @@ -468,6 +468,16 @@ var ANDROID_RESOURCES = [ } }, { + tags: ['sample', 'communication', 'new'], + path: 'samples/BluetoothHDP/index.html', + title: { + en: 'Bluetooth HDP Demo' + }, + description: { + en: 'A sample application that demonstrates how to communicate with a Bluetooth Health Device Profile (HDP) device.' + } + }, + { tags: ['sample', 'accountsync'], path: 'samples/BusinessCard/index.html', title: { diff --git a/docs/html/resources/samples/images/BluetoothHDP.png b/docs/html/resources/samples/images/BluetoothHDP.png Binary files differnew file mode 100644 index 000000000000..c04cfde46681 --- /dev/null +++ b/docs/html/resources/samples/images/BluetoothHDP.png diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd index 941f6931f48b..50b20cea9e64 100644 --- a/docs/html/sdk/eclipse-adt.jd +++ b/docs/html/sdk/eclipse-adt.jd @@ -1,8 +1,8 @@ page.title=ADT Plugin for Eclipse -adt.zip.version=15.0.0 -adt.zip.download=ADT-15.0.0.zip -adt.zip.bytes=6750682 -adt.zip.checksum=264f40a89a1107b0c422adae4e1ce0d1 +adt.zip.version=15.0.1 +adt.zip.download=ADT-15.0.1.zip +adt.zip.bytes=6752327 +adt.zip.checksum=2c12a71d7124aa512b8ee016e19c0e69 @jd:body @@ -109,10 +109,44 @@ padding: .25em 1em; </style> + <div class="toggleable opened"> <a href="#" onclick="return toggleDiv(this)"> <img src="{@docRoot}assets/images/triangle-opened.png" class="toggle-img" height="9px" width="9px" /> +ADT 15.0.1</a> <em>(November 2011)</em> + <div class="toggleme"> +<dl> + <dt>Dependencies:</dt> + + <dd>ADT 15.0.1 is designed for use with <a href="{@docRoot}sdk/tools-notes.html">SDK Tools r15</a>. + If you haven't already installed SDK Tools r15 into your SDK, use the Android SDK and AVD Manager to + do so.</dd> + + <dt>Bug fixes:</dt> + <dd> + <ul> + <li>Fixed how source files are attached to library project <code>.jar</code> files.</li> + <li>Fixed how the <code>bin/</code> folder for library projects are refreshed. This ensures that parent projects pick up changes in library projects.</li> + <li>Fixed how a parent project's library container is updated when a library project is recompiled. This ensures that parent projects are + recompiled when code in a library project changes.</li> + <li>Fixed how <code>res/</code> folders are checked in library projects. This ensures that all <code>res</code> folders are properly included + even if Eclipse is not aware of them due to refresh issues.</li> + <li>Fixed issue that prevented <code>aapt</code> from running when editing certain XML files.</li> + <li>Fixed minor XML formatting issues.</li> + </ul> + </dd> +</dl> + +</div> +</div> + + + +<div class="toggleable closed"> + <a href="#" onclick="return toggleDiv(this)"> + <img src="{@docRoot}assets/images/triangle-closed.png" class="toggle-img" height="9px" +width="9px" /> ADT 15.0.0</a> <em>(October 2011)</em> <div class="toggleme"> <dl> diff --git a/docs/html/sdk/sdk_toc.cs b/docs/html/sdk/sdk_toc.cs index afe6a6e094bd..0ae2c6dba37b 100644 --- a/docs/html/sdk/sdk_toc.cs +++ b/docs/html/sdk/sdk_toc.cs @@ -169,7 +169,7 @@ class="new">new!</span></li> <span style="display:none" class="zh-TW"></span> </h2> <ul> - <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 15.0.0 + <li><a href="<?cs var:toroot ?>sdk/eclipse-adt.html">ADT 15.0.1 <span style="display:none" class="de"></span> <span style="display:none" class="es"></span> <span style="display:none" class="fr"></span> diff --git a/graphics/java/android/renderscript/Element.java b/graphics/java/android/renderscript/Element.java index 8a9ca8550992..29306e4b52f5 100644 --- a/graphics/java/android/renderscript/Element.java +++ b/graphics/java/android/renderscript/Element.java @@ -690,7 +690,11 @@ public class Element extends BaseObj { if ((dt != DataType.UNSIGNED_5_6_5) && (dt != DataType.UNSIGNED_4_4_4_4) && (dt != DataType.UNSIGNED_5_5_5_1)) { - mSize = dt.mSize * size; + if (size == 3) { + mSize = dt.mSize * 4; + } else { + mSize = dt.mSize * size; + } } else { mSize = dt.mSize; } @@ -885,6 +889,7 @@ public class Element extends BaseObj { String[] mElementNames; int[] mArraySizes; int mCount; + int mSkipPadding; /** * Create a builder object. @@ -910,6 +915,21 @@ public class Element extends BaseObj { if (arraySize < 1) { throw new RSIllegalArgumentException("Array size cannot be less than 1."); } + + // Skip padding fields after a vector 3 type. + if (mSkipPadding != 0) { + if (name.startsWith("#padding_")) { + mSkipPadding = 0; + return this; + } + } + + if (element.mVectorSize == 3) { + mSkipPadding = 1; + } else { + mSkipPadding = 0; + } + if(mCount == mElements.length) { Element[] e = new Element[mCount + 8]; String[] s = new String[mCount + 8]; diff --git a/graphics/java/android/renderscript/Mesh.java b/graphics/java/android/renderscript/Mesh.java index 7b3b73f505c6..b6ca58c3760b 100644 --- a/graphics/java/android/renderscript/Mesh.java +++ b/graphics/java/android/renderscript/Mesh.java @@ -514,6 +514,7 @@ public class Mesh extends BaseObj { public static class TriangleMeshBuilder { float mVtxData[]; int mVtxCount; + int mMaxIndex; short mIndexData[]; int mIndexCount; RenderScript mRS; @@ -548,6 +549,7 @@ public class Mesh extends BaseObj { public TriangleMeshBuilder(RenderScript rs, int vtxSize, int flags) { mRS = rs; mVtxCount = 0; + mMaxIndex = 0; mIndexCount = 0; mVtxData = new float[128]; mIndexData = new short[128]; @@ -581,11 +583,13 @@ public class Mesh extends BaseObj { mVtxData[mVtxCount++] = mT0; } if ((mFlags & NORMAL) != 0) { - makeSpace(3); + makeSpace(4); mVtxData[mVtxCount++] = mNX; mVtxData[mVtxCount++] = mNY; mVtxData[mVtxCount++] = mNZ; + mVtxData[mVtxCount++] = 0.0f; } + mMaxIndex ++; } /** @@ -622,10 +626,11 @@ public class Mesh extends BaseObj { if (mVtxSize != 3) { throw new IllegalStateException("add mistmatch with declared components."); } - makeSpace(3); + makeSpace(4); mVtxData[mVtxCount++] = x; mVtxData[mVtxCount++] = y; mVtxData[mVtxCount++] = z; + mVtxData[mVtxCount++] = 1.0f; latch(); return this; } @@ -697,9 +702,9 @@ public class Mesh extends BaseObj { * @return this **/ public TriangleMeshBuilder addTriangle(int idx1, int idx2, int idx3) { - if((idx1 >= mVtxCount) || (idx1 < 0) || - (idx2 >= mVtxCount) || (idx2 < 0) || - (idx3 >= mVtxCount) || (idx3 < 0)) { + if((idx1 >= mMaxIndex) || (idx1 < 0) || + (idx2 >= mMaxIndex) || (idx2 < 0) || + (idx3 >= mMaxIndex) || (idx3 < 0)) { throw new IllegalStateException("Index provided greater than vertex count."); } if ((mIndexCount + 3) >= mIndexData.length) { @@ -729,20 +734,16 @@ public class Mesh extends BaseObj { **/ public Mesh create(boolean uploadToBufferObject) { Element.Builder b = new Element.Builder(mRS); - int floatCount = mVtxSize; b.add(Element.createVector(mRS, Element.DataType.FLOAT_32, mVtxSize), "position"); if ((mFlags & COLOR) != 0) { - floatCount += 4; b.add(Element.F32_4(mRS), "color"); } if ((mFlags & TEXTURE_0) != 0) { - floatCount += 2; b.add(Element.F32_2(mRS), "texture0"); } if ((mFlags & NORMAL) != 0) { - floatCount += 3; b.add(Element.F32_3(mRS), "normal"); } mElement = b.create(); @@ -753,12 +754,12 @@ public class Mesh extends BaseObj { } Builder smb = new Builder(mRS, usage); - smb.addVertexType(mElement, mVtxCount / floatCount); + smb.addVertexType(mElement, mMaxIndex); smb.addIndexSetType(Element.U16(mRS), mIndexCount, Primitive.TRIANGLE); Mesh sm = smb.create(); - sm.getVertexAllocation(0).copy1DRangeFromUnchecked(0, mVtxCount / floatCount, mVtxData); + sm.getVertexAllocation(0).copy1DRangeFromUnchecked(0, mMaxIndex, mVtxData); if(uploadToBufferObject) { if (uploadToBufferObject) { sm.getVertexAllocation(0).syncAll(Allocation.USAGE_SCRIPT); diff --git a/graphics/java/android/renderscript/RSSurfaceView.java b/graphics/java/android/renderscript/RSSurfaceView.java index 20eb93fc1d56..a8e3107606c9 100644 --- a/graphics/java/android/renderscript/RSSurfaceView.java +++ b/graphics/java/android/renderscript/RSSurfaceView.java @@ -77,10 +77,12 @@ public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback * This method is part of the SurfaceHolder.Callback interface, and is * not normally called or subclassed by clients of RSSurfaceView. */ - public synchronized void surfaceDestroyed(SurfaceHolder holder) { - // Surface will be destroyed when we return - if (mRS != null) { - mRS.setSurface(null, 0, 0); + public void surfaceDestroyed(SurfaceHolder holder) { + synchronized (this) { + // Surface will be destroyed when we return + if (mRS != null) { + mRS.setSurface(null, 0, 0); + } } } @@ -88,9 +90,11 @@ public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback * This method is part of the SurfaceHolder.Callback interface, and is * not normally called or subclassed by clients of RSSurfaceView. */ - public synchronized void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { - if (mRS != null) { - mRS.setSurface(holder, w, h); + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + synchronized (this) { + if (mRS != null) { + mRS.setSurface(holder, w, h); + } } } @@ -125,9 +129,11 @@ public class RSSurfaceView extends SurfaceView implements SurfaceHolder.Callback return rs; } - public synchronized void destroyRenderScriptGL() { - mRS.destroy(); - mRS = null; + public void destroyRenderScriptGL() { + synchronized (this) { + mRS.destroy(); + mRS = null; + } } public void setRenderScriptGL(RenderScriptGL rs) { diff --git a/include/gui/SurfaceTexture.h b/include/gui/SurfaceTexture.h index d7dd4d6cb41e..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 @@ -271,7 +275,8 @@ private: mRequestBufferCalled(false), mTransform(0), mScalingMode(NATIVE_WINDOW_SCALING_MODE_FREEZE), - mTimestamp(0) { + mTimestamp(0), + mFrameNumber(0) { mCrop.makeInvalid(); } @@ -340,6 +345,10 @@ private: // mTimestamp is the current timestamp for this buffer slot. This gets // to set by queueBuffer each time this slot is queued. int64_t mTimestamp; + + // mFrameNumber is the number of the queued frame for this slot. + uint64_t mFrameNumber; + }; // mSlots is the array of buffer slots that must be mirrored on the client @@ -476,6 +485,12 @@ private: // around a GL driver limitation on the number of FBO attachments, which the // browser's tile cache exceeds. const GLenum mTexTarget; + + // mFrameCounter is the free running counter, incremented for every buffer queued + // with the surface Texture. + uint64_t mFrameCounter; + + }; // ---------------------------------------------------------------------------- diff --git a/include/gui/SurfaceTextureClient.h b/include/gui/SurfaceTextureClient.h index 57f9e151deee..971a1b80dc88 100644 --- a/include/gui/SurfaceTextureClient.h +++ b/include/gui/SurfaceTextureClient.h @@ -40,6 +40,7 @@ public: protected: SurfaceTextureClient(); + virtual ~SurfaceTextureClient(); void setISurfaceTexture(const sp<ISurfaceTexture>& surfaceTexture); private: diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h index 605680a1a7ee..2fb69b6edf78 100644 --- a/include/media/AudioRecord.h +++ b/include/media/AudioRecord.h @@ -385,6 +385,8 @@ private: uint32_t mChannelMask; audio_io_handle_t mInput; int mSessionId; + int mPreviousPriority; // before start() + int mPreviousSchedulingGroup; }; }; // namespace android diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index 911a53505cfd..cb3d8338bc84 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -487,6 +487,8 @@ private: int mAuxEffectId; Mutex mLock; status_t mRestoreStatus; + int mPreviousPriority; // before start() + int mPreviousSchedulingGroup; }; diff --git a/include/media/JetPlayer.h b/include/media/JetPlayer.h index 16764a9192bd..6d539897429d 100644 --- a/include/media/JetPlayer.h +++ b/include/media/JetPlayer.h @@ -65,7 +65,6 @@ public: private: - static int renderThread(void*); int render(); void fireUpdateOnStatusChange(); void fireEventsFromJetQueue(); @@ -97,6 +96,28 @@ private: char mJetFilePath[256]; + class JetPlayerThread : public Thread { + public: + JetPlayerThread(JetPlayer *player) : mPlayer(player) { + } + + protected: + virtual ~JetPlayerThread() {} + + private: + JetPlayer *mPlayer; + + bool threadLoop() { + int result; + result = mPlayer->render(); + return false; + } + + JetPlayerThread(const JetPlayerThread &); + JetPlayerThread &operator=(const JetPlayerThread &); + }; + + sp<JetPlayerThread> mThread; }; // end class JetPlayer 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/include/ui/Input.h b/include/ui/Input.h index 438a1a0351b2..c2cbe1d6e871 100644 --- a/include/ui/Input.h +++ b/include/ui/Input.h @@ -826,6 +826,9 @@ public: inline void setKeyboardType(int32_t keyboardType) { mKeyboardType = keyboardType; } inline int32_t getKeyboardType() const { return mKeyboardType; } + inline void setKeyCharacterMapFile(const String8& value) { mKeyCharacterMapFile = value; } + inline const String8& getKeyCharacterMapFile() const { return mKeyCharacterMapFile; } + inline const Vector<MotionRange>& getMotionRanges() const { return mMotionRanges; } @@ -835,6 +838,7 @@ private: String8 mName; uint32_t mSources; int32_t mKeyboardType; + String8 mKeyCharacterMapFile; Vector<MotionRange> mMotionRanges; }; diff --git a/include/ui/KeyCharacterMap.h b/include/ui/KeyCharacterMap.h index 10a38109d553..be14432ae59b 100644 --- a/include/ui/KeyCharacterMap.h +++ b/include/ui/KeyCharacterMap.h @@ -53,7 +53,6 @@ public: ~KeyCharacterMap(); static status_t load(const String8& filename, KeyCharacterMap** outMap); - static status_t loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap); /* Gets the keyboard type. */ int32_t getKeyboardType() const; diff --git a/include/ui/Keyboard.h b/include/ui/Keyboard.h index 609f31991393..274f5264f463 100644 --- a/include/ui/Keyboard.h +++ b/include/ui/Keyboard.h @@ -81,24 +81,6 @@ extern bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentif const PropertyMap* deviceConfiguration, const KeyMap* keyMap); /** - * Sets keyboard system properties. - */ -extern void setKeyboardProperties(int32_t deviceId, const InputDeviceIdentifier& deviceIdentifier, - const String8& keyLayoutFile, const String8& keyCharacterMapFile); - -/** - * Clears keyboard system properties. - */ -extern void clearKeyboardProperties(int32_t deviceId); - -/** - * Gets the key character map filename for a device using inspecting system properties - * and then falling back on a default key character map if necessary. - * Returns a NAME_NOT_FOUND if none found. - */ -extern status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile); - -/** * Gets a key code by its short form label, eg. "HOME". * Returns 0 if unknown. */ diff --git a/include/utils/BasicHashtable.h b/include/utils/BasicHashtable.h new file mode 100644 index 000000000000..fdf97385f976 --- /dev/null +++ b/include/utils/BasicHashtable.h @@ -0,0 +1,393 @@ +/* + * 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. + */ + +#ifndef ANDROID_BASIC_HASHTABLE_H +#define ANDROID_BASIC_HASHTABLE_H + +#include <stdint.h> +#include <sys/types.h> +#include <utils/SharedBuffer.h> +#include <utils/TypeHelpers.h> + +namespace android { + +/* Implementation type. Nothing to see here. */ +class BasicHashtableImpl { +protected: + struct Bucket { + // The collision flag indicates that the bucket is part of a collision chain + // such that at least two entries both hash to this bucket. When true, we + // may need to seek further along the chain to find the entry. + static const uint32_t COLLISION = 0x80000000UL; + + // The present flag indicates that the bucket contains an initialized entry value. + static const uint32_t PRESENT = 0x40000000UL; + + // Mask for 30 bits worth of the hash code that are stored within the bucket to + // speed up lookups and rehashing by eliminating the need to recalculate the + // hash code of the entry's key. + static const uint32_t HASH_MASK = 0x3fffffffUL; + + // Combined value that stores the collision and present flags as well as + // a 30 bit hash code. + uint32_t cookie; + + // Storage for the entry begins here. + char entry[0]; + }; + + BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, + size_t minimumInitialCapacity, float loadFactor); + BasicHashtableImpl(const BasicHashtableImpl& other); + + void dispose(); + + inline void edit() { + if (mBuckets && !SharedBuffer::bufferFromData(mBuckets)->onlyOwner()) { + clone(); + } + } + + void setTo(const BasicHashtableImpl& other); + void clear(); + + ssize_t next(ssize_t index) const; + ssize_t find(ssize_t index, hash_t hash, const void* __restrict__ key) const; + size_t add(hash_t hash, const void* __restrict__ entry); + void removeAt(size_t index); + void rehash(size_t minimumCapacity, float loadFactor); + + const size_t mBucketSize; // number of bytes per bucket including the entry + const bool mHasTrivialDestructor; // true if the entry type does not require destruction + size_t mCapacity; // number of buckets that can be filled before exceeding load factor + float mLoadFactor; // load factor + size_t mSize; // number of elements actually in the table + size_t mFilledBuckets; // number of buckets for which collision or present is true + size_t mBucketCount; // number of slots in the mBuckets array + void* mBuckets; // array of buckets, as a SharedBuffer + + inline const Bucket& bucketAt(const void* __restrict__ buckets, size_t index) const { + return *reinterpret_cast<const Bucket*>( + static_cast<const uint8_t*>(buckets) + index * mBucketSize); + } + + inline Bucket& bucketAt(void* __restrict__ buckets, size_t index) const { + return *reinterpret_cast<Bucket*>(static_cast<uint8_t*>(buckets) + index * mBucketSize); + } + + virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const = 0; + virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const = 0; + virtual void destroyBucketEntry(Bucket& bucket) const = 0; + +private: + void clone(); + + // Allocates a bucket array as a SharedBuffer. + void* allocateBuckets(size_t count) const; + + // Releases a bucket array's associated SharedBuffer. + void releaseBuckets(void* __restrict__ buckets, size_t count) const; + + // Destroys the contents of buckets (invokes destroyBucketEntry for each + // populated bucket if needed). + void destroyBuckets(void* __restrict__ buckets, size_t count) const; + + // Copies the content of buckets (copies the cookie and invokes copyBucketEntry + // for each populated bucket if needed). + void copyBuckets(const void* __restrict__ fromBuckets, + void* __restrict__ toBuckets, size_t count) const; + + // Determines the appropriate size of a bucket array to store a certain minimum + // number of entries and returns its effective capacity. + static void determineCapacity(size_t minimumCapacity, float loadFactor, + size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity); + + // Trim a hash code to 30 bits to match what we store in the bucket's cookie. + inline static hash_t trimHash(hash_t hash) { + return (hash & Bucket::HASH_MASK) ^ (hash >> 30); + } + + // Returns the index of the first bucket that is in the collision chain + // for the specified hash code, given the total number of buckets. + // (Primary hash) + inline static size_t chainStart(hash_t hash, size_t count) { + return hash % count; + } + + // Returns the increment to add to a bucket index to seek to the next bucket + // in the collision chain for the specified hash code, given the total number of buckets. + // (Secondary hash) + inline static size_t chainIncrement(hash_t hash, size_t count) { + return ((hash >> 7) | (hash << 25)) % (count - 1) + 1; + } + + // Returns the index of the next bucket that is in the collision chain + // that is defined by the specified increment, given the total number of buckets. + inline static size_t chainSeek(size_t index, size_t increment, size_t count) { + return (index + increment) % count; + } +}; + +/* + * A BasicHashtable stores entries that are indexed by hash code in place + * within an array. The basic operations are finding entries by key, + * adding new entries and removing existing entries. + * + * This class provides a very limited set of operations with simple semantics. + * It is intended to be used as a building block to construct more complex + * and interesting data structures such as HashMap. Think very hard before + * adding anything extra to BasicHashtable, it probably belongs at a + * higher level of abstraction. + * + * TKey: The key type. + * TEntry: The entry type which is what is actually stored in the array. + * + * TKey must support the following contract: + * bool operator==(const TKey& other) const; // return true if equal + * bool operator!=(const TKey& other) const; // return true if unequal + * + * TEntry must support the following contract: + * const TKey& getKey() const; // get the key from the entry + * + * This class supports storing entries with duplicate keys. Of course, it can't + * tell them apart during removal so only the first entry will be removed. + * We do this because it means that operations like add() can't fail. + */ +template <typename TKey, typename TEntry> +class BasicHashtable : private BasicHashtableImpl { +public: + /* Creates a hashtable with the specified minimum initial capacity. + * The underlying array will be created when the first entry is added. + * + * minimumInitialCapacity: The minimum initial capacity for the hashtable. + * Default is 0. + * loadFactor: The desired load factor for the hashtable, between 0 and 1. + * Default is 0.75. + */ + BasicHashtable(size_t minimumInitialCapacity = 0, float loadFactor = 0.75f); + + /* Copies a hashtable. + * The underlying storage is shared copy-on-write. + */ + BasicHashtable(const BasicHashtable& other); + + /* Clears and destroys the hashtable. + */ + virtual ~BasicHashtable(); + + /* Making this hashtable a copy of the other hashtable. + * The underlying storage is shared copy-on-write. + * + * other: The hashtable to copy. + */ + inline BasicHashtable<TKey, TEntry>& operator =(const BasicHashtable<TKey, TEntry> & other) { + setTo(other); + return *this; + } + + /* Returns the number of entries in the hashtable. + */ + inline size_t size() const { + return mSize; + } + + /* Returns the capacity of the hashtable, which is the number of elements that can + * added to the hashtable without requiring it to be grown. + */ + inline size_t capacity() const { + return mCapacity; + } + + /* Returns the number of buckets that the hashtable has, which is the size of its + * underlying array. + */ + inline size_t bucketCount() const { + return mBucketCount; + } + + /* Returns the load factor of the hashtable. */ + inline float loadFactor() const { + return mLoadFactor; + }; + + /* Returns a const reference to the entry at the specified index. + * + * index: The index of the entry to retrieve. Must be a valid index within + * the bounds of the hashtable. + */ + inline const TEntry& entryAt(size_t index) const { + return entryFor(bucketAt(mBuckets, index)); + } + + /* Returns a non-const reference to the entry at the specified index. + * + * index: The index of the entry to edit. Must be a valid index within + * the bounds of the hashtable. + */ + inline TEntry& editEntryAt(size_t index) { + edit(); + return entryFor(bucketAt(mBuckets, index)); + } + + /* Clears the hashtable. + * All entries in the hashtable are destroyed immediately. + * If you need to do something special with the entries in the hashtable then iterate + * over them and do what you need before clearing the hashtable. + */ + inline void clear() { + BasicHashtableImpl::clear(); + } + + /* Returns the index of the next entry in the hashtable given the index of a previous entry. + * If the given index is -1, then returns the index of the first entry in the hashtable, + * if there is one, or -1 otherwise. + * If the given index is not -1, then returns the index of the next entry in the hashtable, + * in strictly increasing order, or -1 if there are none left. + * + * index: The index of the previous entry that was iterated, or -1 to begin + * iteration at the beginning of the hashtable. + */ + inline ssize_t next(ssize_t index) const { + return BasicHashtableImpl::next(index); + } + + /* Finds the index of an entry with the specified key. + * If the given index is -1, then returns the index of the first matching entry, + * otherwise returns the index of the next matching entry. + * If the hashtable contains multiple entries with keys that match the requested + * key, then the sequence of entries returned is arbitrary. + * Returns -1 if no entry was found. + * + * index: The index of the previous entry with the specified key, or -1 to + * find the first matching entry. + * hash: The hashcode of the key. + * key: The key. + */ + inline ssize_t find(ssize_t index, hash_t hash, const TKey& key) const { + return BasicHashtableImpl::find(index, hash, &key); + } + + /* Adds the entry to the hashtable. + * Returns the index of the newly added entry. + * If an entry with the same key already exists, then a duplicate entry is added. + * If the entry will not fit, then the hashtable's capacity is increased and + * its contents are rehashed. See rehash(). + * + * hash: The hashcode of the key. + * entry: The entry to add. + */ + inline size_t add(hash_t hash, const TEntry& entry) { + return BasicHashtableImpl::add(hash, &entry); + } + + /* Removes the entry with the specified index from the hashtable. + * The entry is destroyed immediately. + * The index must be valid. + * + * The hashtable is not compacted after an item is removed, so it is legal + * to continue iterating over the hashtable using next() or find(). + * + * index: The index of the entry to remove. Must be a valid index within the + * bounds of the hashtable, and it must refer to an existing entry. + */ + inline void removeAt(size_t index) { + BasicHashtableImpl::removeAt(index); + } + + /* Rehashes the contents of the hashtable. + * Grows the hashtable to at least the specified minimum capacity or the + * current number of elements, whichever is larger. + * + * Rehashing causes all entries to be copied and the entry indices may change. + * Although the hash codes are cached by the hashtable, rehashing can be an + * expensive operation and should be avoided unless the hashtable's size + * needs to be changed. + * + * Rehashing is the only way to change the capacity or load factor of the + * hashtable once it has been created. It can be used to compact the + * hashtable by choosing a minimum capacity that is smaller than the current + * capacity (such as 0). + * + * minimumCapacity: The desired minimum capacity after rehashing. + * loadFactor: The desired load factor after rehashing. + */ + inline void rehash(size_t minimumCapacity, float loadFactor) { + BasicHashtableImpl::rehash(minimumCapacity, loadFactor); + } + +protected: + static inline const TEntry& entryFor(const Bucket& bucket) { + return reinterpret_cast<const TEntry&>(bucket.entry); + } + + static inline TEntry& entryFor(Bucket& bucket) { + return reinterpret_cast<TEntry&>(bucket.entry); + } + + virtual bool compareBucketKey(const Bucket& bucket, const void* __restrict__ key) const; + virtual void initializeBucketEntry(Bucket& bucket, const void* __restrict__ entry) const; + virtual void destroyBucketEntry(Bucket& bucket) const; + +private: + // For dumping the raw contents of a hashtable during testing. + friend class BasicHashtableTest; + inline uint32_t cookieAt(size_t index) const { + return bucketAt(mBuckets, index).cookie; + } +}; + +template <typename TKey, typename TEntry> +BasicHashtable<TKey, TEntry>::BasicHashtable(size_t minimumInitialCapacity, float loadFactor) : + BasicHashtableImpl(sizeof(TEntry), traits<TEntry>::has_trivial_dtor, + minimumInitialCapacity, loadFactor) { +} + +template <typename TKey, typename TEntry> +BasicHashtable<TKey, TEntry>::BasicHashtable(const BasicHashtable<TKey, TEntry>& other) : + BasicHashtableImpl(other) { +} + +template <typename TKey, typename TEntry> +BasicHashtable<TKey, TEntry>::~BasicHashtable() { + dispose(); +} + +template <typename TKey, typename TEntry> +bool BasicHashtable<TKey, TEntry>::compareBucketKey(const Bucket& bucket, + const void* __restrict__ key) const { + return entryFor(bucket).getKey() == *static_cast<const TKey*>(key); +} + +template <typename TKey, typename TEntry> +void BasicHashtable<TKey, TEntry>::initializeBucketEntry(Bucket& bucket, + const void* __restrict__ entry) const { + if (!traits<TEntry>::has_trivial_copy) { + new (&entryFor(bucket)) TEntry(*(static_cast<const TEntry*>(entry))); + } else { + memcpy(&entryFor(bucket), entry, sizeof(TEntry)); + } +} + +template <typename TKey, typename TEntry> +void BasicHashtable<TKey, TEntry>::destroyBucketEntry(Bucket& bucket) const { + if (!traits<TEntry>::has_trivial_dtor) { + entryFor(bucket).~TEntry(); + } +} + +}; // namespace android + +#endif // ANDROID_BASIC_HASHTABLE_H diff --git a/include/utils/TypeHelpers.h b/include/utils/TypeHelpers.h index a1663f30e50d..7b4fb70ba641 100644 --- a/include/utils/TypeHelpers.h +++ b/include/utils/TypeHelpers.h @@ -213,6 +213,9 @@ void move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { template <typename KEY, typename VALUE> struct key_value_pair_t { + typedef KEY key_t; + typedef VALUE value_t; + KEY key; VALUE value; key_value_pair_t() { } @@ -222,6 +225,12 @@ struct key_value_pair_t { inline bool operator < (const key_value_pair_t& o) const { return strictly_order_type(key, o.key); } + inline const KEY& getKey() const { + return key; + } + inline const VALUE& getValue() const { + return value; + } }; template<> @@ -243,6 +252,41 @@ struct trait_trivial_move< key_value_pair_t<K, V> > // --------------------------------------------------------------------------- +/* + * Hash codes. + */ +typedef uint32_t hash_t; + +template <typename TKey> +hash_t hash_type(const TKey& key); + +/* Built-in hash code specializations. + * Assumes pointers are 32bit. */ +#define ANDROID_INT32_HASH(T) \ + template <> inline hash_t hash_type(const T& value) { return hash_t(value); } +#define ANDROID_INT64_HASH(T) \ + template <> inline hash_t hash_type(const T& value) { \ + return hash_t((value >> 32) ^ value); } +#define ANDROID_REINTERPRET_HASH(T, R) \ + template <> inline hash_t hash_type(const T& value) { \ + return hash_type(*reinterpret_cast<const R*>(&value)); } + +ANDROID_INT32_HASH(bool) +ANDROID_INT32_HASH(int8_t) +ANDROID_INT32_HASH(uint8_t) +ANDROID_INT32_HASH(int16_t) +ANDROID_INT32_HASH(uint16_t) +ANDROID_INT32_HASH(int32_t) +ANDROID_INT32_HASH(uint32_t) +ANDROID_INT64_HASH(int64_t) +ANDROID_INT64_HASH(uint64_t) +ANDROID_REINTERPRET_HASH(float, uint32_t) +ANDROID_REINTERPRET_HASH(double, uint64_t) + +template <typename T> inline hash_t hash_type(const T*& value) { + return hash_type(uintptr_t(value)); +} + }; // namespace android // --------------------------------------------------------------------------- diff --git a/libs/gui/SurfaceTexture.cpp b/libs/gui/SurfaceTexture.cpp index ba7c35bca35c..b4d01a961f71 100644 --- a/libs/gui/SurfaceTexture.cpp +++ b/libs/gui/SurfaceTexture.cpp @@ -116,7 +116,8 @@ SurfaceTexture::SurfaceTexture(GLuint tex, bool allowSynchronousMode, mAllowSynchronousMode(allowSynchronousMode), mConnectedApi(NO_CONNECTED_API), mAbandoned(false), - mTexTarget(texTarget) { + mTexTarget(texTarget), + mFrameCounter(0) { // Choose a name using the PID and a process-unique ID. mName = String8::format("unnamed-%d-%d", getpid(), createProcessUniqueId()); @@ -264,7 +265,8 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, status_t returnFlags(OK); - int found, foundSync; + int found = -1; + int foundSync = -1; int dequeuedCount = 0; bool tryAgain = true; while (tryAgain) { @@ -337,9 +339,14 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, } } else { if (state == BufferSlot::FREE) { - foundSync = i; - found = i; - break; + /** For Asynchronous mode, we need to return the oldest of free buffers + * There is only one instance when the Framecounter overflows, this logic + * might return the earlier buffer to client. Which is a negligible impact + **/ + if (found < 0 || mSlots[i].mFrameNumber < mSlots[found].mFrameNumber) { + foundSync = i; + found = i; + } } } } @@ -434,6 +441,11 @@ status_t SurfaceTexture::dequeueBuffer(int *outBuf, uint32_t w, uint32_t h, mSlots[buf].mEglImage = EGL_NO_IMAGE_KHR; mSlots[buf].mEglDisplay = EGL_NO_DISPLAY; } + if (mCurrentTexture == buf) { + // The current texture no longer references the buffer in this slot + // since we just allocated a new buffer. + mCurrentTexture = INVALID_BUFFER_SLOT; + } returnFlags |= ISurfaceTexture::BUFFER_NEEDS_REALLOCATION; } ST_LOGV("dequeueBuffer: returning slot=%d buf=%p flags=%#x", buf, @@ -531,6 +543,9 @@ status_t SurfaceTexture::queueBuffer(int buf, int64_t timestamp, mSlots[buf].mTransform = mNextTransform; mSlots[buf].mScalingMode = mNextScalingMode; mSlots[buf].mTimestamp = timestamp; + mFrameCounter++; + mSlots[buf].mFrameNumber = mFrameCounter; + mDequeueCondition.signal(); *outWidth = mDefaultWidth; @@ -564,6 +579,7 @@ void SurfaceTexture::cancelBuffer(int buf) { return; } mSlots[buf].mBufferState = BufferSlot::FREE; + mSlots[buf].mFrameNumber = 0; mDequeueCondition.signal(); } @@ -630,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; @@ -897,6 +914,7 @@ void SurfaceTexture::setFrameAvailableListener( void SurfaceTexture::freeBufferLocked(int i) { mSlots[i].mGraphicBuffer = 0; mSlots[i].mBufferState = BufferSlot::FREE; + mSlots[i].mFrameNumber = 0; if (mSlots[i].mEglImage != EGL_NO_IMAGE_KHR) { eglDestroyImageKHR(mSlots[i].mEglDisplay, mSlots[i].mEglImage); mSlots[i].mEglImage = EGL_NO_IMAGE_KHR; @@ -992,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/SurfaceTextureClient.cpp b/libs/gui/SurfaceTextureClient.cpp index f66e25f1dfd4..3d47f053a540 100644 --- a/libs/gui/SurfaceTextureClient.cpp +++ b/libs/gui/SurfaceTextureClient.cpp @@ -36,6 +36,12 @@ SurfaceTextureClient::SurfaceTextureClient() { SurfaceTextureClient::init(); } +SurfaceTextureClient::~SurfaceTextureClient() { + if (mConnectedToCpu) { + SurfaceTextureClient::disconnect(NATIVE_WINDOW_API_CPU); + } +} + void SurfaceTextureClient::init() { // Initialize the ANativeWindow function pointers. ANativeWindow::setSwapInterval = hook_setSwapInterval; 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/libs/rs/driver/rsdBcc.cpp b/libs/rs/driver/rsdBcc.cpp index c16091c58b3f..917b419fd16b 100644 --- a/libs/rs/driver/rsdBcc.cpp +++ b/libs/rs/driver/rsdBcc.cpp @@ -72,7 +72,7 @@ bool rsdScriptInit(const Context *rsc, //LOGE("rsdScriptCreate %p %p %p %p %i %i %p", rsc, resName, cacheDir, bitcode, bitcodeSize, flags, lookupFunc); pthread_mutex_lock(&rsdgInitMutex); - char *cachePath = NULL; + size_t exportFuncCount = 0; size_t exportVarCount = 0; size_t objectSlotCount = 0; @@ -122,8 +122,6 @@ bool rsdScriptInit(const Context *rsc, goto error; } - free(cachePath); - drv->mRoot = reinterpret_cast<int (*)()>(bccGetFuncAddr(drv->mBccScript, "root")); drv->mInit = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, "init")); drv->mFreeChildren = reinterpret_cast<void (*)()>(bccGetFuncAddr(drv->mBccScript, ".rs.dtor")); diff --git a/libs/rs/rsAllocation.cpp b/libs/rs/rsAllocation.cpp index 35d812d40f25..c1192fefff7a 100644 --- a/libs/rs/rsAllocation.cpp +++ b/libs/rs/rsAllocation.cpp @@ -195,6 +195,81 @@ void Allocation::dumpLOGV(const char *prefix) const { prefix, getPtr(), mHal.state.usageFlags, mHal.state.mipmapControl); } +uint32_t Allocation::getPackedSize() const { + uint32_t numItems = mHal.state.type->getSizeBytes() / mHal.state.type->getElementSizeBytes(); + return numItems * mHal.state.type->getElement()->getSizeBytesUnpadded(); +} + +void Allocation::writePackedData(const Type *type, + uint8_t *dst, const uint8_t *src, bool dstPadded) { + const Element *elem = type->getElement(); + uint32_t unpaddedBytes = elem->getSizeBytesUnpadded(); + uint32_t paddedBytes = elem->getSizeBytes(); + uint32_t numItems = type->getSizeBytes() / paddedBytes; + + uint32_t srcInc = !dstPadded ? paddedBytes : unpaddedBytes; + uint32_t dstInc = dstPadded ? paddedBytes : unpaddedBytes; + + // no sub-elements + uint32_t fieldCount = elem->getFieldCount(); + if (fieldCount == 0) { + for (uint32_t i = 0; i < numItems; i ++) { + memcpy(dst, src, unpaddedBytes); + src += srcInc; + dst += dstInc; + } + return; + } + + // Cache offsets + uint32_t *offsetsPadded = new uint32_t[fieldCount]; + uint32_t *offsetsUnpadded = new uint32_t[fieldCount]; + uint32_t *sizeUnpadded = new uint32_t[fieldCount]; + + for (uint32_t i = 0; i < fieldCount; i++) { + offsetsPadded[i] = elem->getFieldOffsetBytes(i); + offsetsUnpadded[i] = elem->getFieldOffsetBytesUnpadded(i); + sizeUnpadded[i] = elem->getField(i)->getSizeBytesUnpadded(); + } + + uint32_t *srcOffsets = !dstPadded ? offsetsPadded : offsetsUnpadded; + uint32_t *dstOffsets = dstPadded ? offsetsPadded : offsetsUnpadded; + + // complex elements, need to copy subelem after subelem + for (uint32_t i = 0; i < numItems; i ++) { + for (uint32_t fI = 0; fI < fieldCount; fI++) { + memcpy(dst + dstOffsets[fI], src + srcOffsets[fI], sizeUnpadded[fI]); + } + src += srcInc; + dst += dstInc; + } + + delete[] offsetsPadded; + delete[] offsetsUnpadded; + delete[] sizeUnpadded; +} + +void Allocation::unpackVec3Allocation(const void *data, uint32_t dataSize) { + const uint8_t *src = (const uint8_t*)data; + uint8_t *dst = (uint8_t*)getPtr(); + + writePackedData(getType(), dst, src, true); +} + +void Allocation::packVec3Allocation(OStream *stream) const { + uint32_t paddedBytes = getType()->getElement()->getSizeBytes(); + uint32_t unpaddedBytes = getType()->getElement()->getSizeBytesUnpadded(); + uint32_t numItems = mHal.state.type->getSizeBytes() / paddedBytes; + + const uint8_t *src = (const uint8_t*)getPtr(); + uint8_t *dst = new uint8_t[numItems * unpaddedBytes]; + + writePackedData(getType(), dst, src, false); + stream->addByteArray(dst, getPackedSize()); + + delete[] dst; +} + void Allocation::serialize(OStream *stream) const { // Need to identify ourselves stream->addU32((uint32_t)getClassId()); @@ -207,10 +282,17 @@ void Allocation::serialize(OStream *stream) const { mHal.state.type->serialize(stream); uint32_t dataSize = mHal.state.type->getSizeBytes(); + // 3 element vectors are padded to 4 in memory, but padding isn't serialized + uint32_t packedSize = getPackedSize(); // Write how much data we are storing - stream->addU32(dataSize); - // Now write the data - stream->addByteArray(getPtr(), dataSize); + stream->addU32(packedSize); + if (dataSize == packedSize) { + // Now write the data + stream->addByteArray(getPtr(), dataSize); + } else { + // Now write the data + packVec3Allocation(stream); + } } Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) { @@ -230,22 +312,30 @@ Allocation *Allocation::createFromStream(Context *rsc, IStream *stream) { } type->compute(); + Allocation *alloc = Allocation::createAllocation(rsc, type, RS_ALLOCATION_USAGE_SCRIPT); + type->decUserRef(); + // Number of bytes we wrote out for this allocation uint32_t dataSize = stream->loadU32(); - if (dataSize != type->getSizeBytes()) { + // 3 element vectors are padded to 4 in memory, but padding isn't serialized + uint32_t packedSize = alloc->getPackedSize(); + if (dataSize != type->getSizeBytes() && + dataSize != packedSize) { LOGE("failed to read allocation because numbytes written is not the same loaded type wants\n"); + ObjectBase::checkDelete(alloc); ObjectBase::checkDelete(type); return NULL; } - Allocation *alloc = Allocation::createAllocation(rsc, type, RS_ALLOCATION_USAGE_SCRIPT); alloc->setName(name.string(), name.size()); - type->decUserRef(); - - uint32_t count = dataSize / type->getElementSizeBytes(); - // Read in all of our allocation data - alloc->data(rsc, 0, 0, count, stream->getPtr() + stream->getPos(), dataSize); + if (dataSize == type->getSizeBytes()) { + uint32_t count = dataSize / type->getElementSizeBytes(); + // Read in all of our allocation data + alloc->data(rsc, 0, 0, count, stream->getPtr() + stream->getPos(), dataSize); + } else { + alloc->unpackVec3Allocation(stream->getPtr() + stream->getPos(), dataSize); + } stream->reset(stream->getPos() + dataSize); return alloc; diff --git a/libs/rs/rsAllocation.h b/libs/rs/rsAllocation.h index 714798aa1e0c..4ce863ab927f 100644 --- a/libs/rs/rsAllocation.h +++ b/libs/rs/rsAllocation.h @@ -135,6 +135,11 @@ protected: private: void freeChildrenUnlocked(); Allocation(Context *rsc, const Type *, uint32_t usages, RsAllocationMipmapControl mc); + + uint32_t getPackedSize() const; + static void writePackedData(const Type *type, uint8_t *dst, const uint8_t *src, bool dstPadded); + void unpackVec3Allocation(const void *data, uint32_t dataSize); + void packVec3Allocation(OStream *stream) const; }; } diff --git a/libs/rs/rsComponent.cpp b/libs/rs/rsComponent.cpp index 7d9cf0ba954c..21b98f6193a2 100644 --- a/libs/rs/rsComponent.cpp +++ b/libs/rs/rsComponent.cpp @@ -169,7 +169,8 @@ void Component::set(RsDataType dt, RsDataKind dk, bool norm, uint32_t vecSize) { break; } - mBits = mTypeBits * mVectorSize; + mBitsUnpadded = mTypeBits * mVectorSize; + mBits = mTypeBits * rsHigherPow2(mVectorSize); } bool Component::isReference() const { diff --git a/libs/rs/rsComponent.h b/libs/rs/rsComponent.h index 6ddc990b2486..8629d0d9d6b9 100644 --- a/libs/rs/rsComponent.h +++ b/libs/rs/rsComponent.h @@ -41,6 +41,7 @@ public: bool getIsFloat() const {return mIsFloat;} bool getIsSigned() const {return mIsSigned;} uint32_t getBits() const {return mBits;} + uint32_t getBitsUnpadded() const {return mBitsUnpadded;} // Helpers for reading / writing this class out void serialize(OStream *stream) const; @@ -56,6 +57,7 @@ protected: // derived uint32_t mBits; + uint32_t mBitsUnpadded; uint32_t mTypeBits; bool mIsFloat; bool mIsSigned; diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index f8213a1d2b11..293fc3ad953b 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -472,6 +472,30 @@ void Context::setSurface(uint32_t w, uint32_t h, RsNativeWindow sur) { } } +uint32_t Context::getCurrentSurfaceWidth() const { + for (uint32_t i = 0; i < mFBOCache.mHal.state.colorTargetsCount; i ++) { + if (mFBOCache.mHal.state.colorTargets[i] != NULL) { + return mFBOCache.mHal.state.colorTargets[i]->getType()->getDimX(); + } + } + if (mFBOCache.mHal.state.depthTarget != NULL) { + return mFBOCache.mHal.state.depthTarget->getType()->getDimX(); + } + return mWidth; +} + +uint32_t Context::getCurrentSurfaceHeight() const { + for (uint32_t i = 0; i < mFBOCache.mHal.state.colorTargetsCount; i ++) { + if (mFBOCache.mHal.state.colorTargets[i] != NULL) { + return mFBOCache.mHal.state.colorTargets[i]->getType()->getDimY(); + } + } + if (mFBOCache.mHal.state.depthTarget != NULL) { + return mFBOCache.mHal.state.depthTarget->getType()->getDimY(); + } + return mHeight; +} + void Context::pause() { rsAssert(mIsGraphicsContext); mPaused = true; diff --git a/libs/rs/rsContext.h b/libs/rs/rsContext.h index 199cc5a36051..c6582c9dffbc 100644 --- a/libs/rs/rsContext.h +++ b/libs/rs/rsContext.h @@ -164,6 +164,9 @@ public: uint32_t getWidth() const {return mWidth;} uint32_t getHeight() const {return mHeight;} + uint32_t getCurrentSurfaceWidth() const; + uint32_t getCurrentSurfaceHeight() const; + mutable ThreadIO mIO; // Timers diff --git a/libs/rs/rsElement.cpp b/libs/rs/rsElement.cpp index df90ce499e85..56c31b66405d 100644 --- a/libs/rs/rsElement.cpp +++ b/libs/rs/rsElement.cpp @@ -23,6 +23,7 @@ using namespace android::renderscript; Element::Element(Context *rsc) : ObjectBase(rsc) { mBits = 0; + mBitsUnpadded = 0; mFields = NULL; mFieldCount = 0; mHasReference = false; @@ -60,6 +61,18 @@ size_t Element::getSizeBits() const { return total; } +size_t Element::getSizeBitsUnpadded() const { + if (!mFieldCount) { + return mBitsUnpadded; + } + + size_t total = 0; + for (size_t ct=0; ct < mFieldCount; ct++) { + total += mFields[ct].e->mBitsUnpadded * mFields[ct].arraySize; + } + return total; +} + void Element::dumpLOGV(const char *prefix) const { ObjectBase::dumpLOGV(prefix); ALOGV("%s Element: fieldCount: %zu, size bytes: %zu", prefix, mFieldCount, getSizeBytes()); @@ -146,14 +159,18 @@ Element *Element::createFromStream(Context *rsc, IStream *stream) { void Element::compute() { if (mFieldCount == 0) { mBits = mComponent.getBits(); + mBitsUnpadded = mComponent.getBitsUnpadded(); mHasReference = mComponent.isReference(); return; } size_t bits = 0; + size_t bitsUnpadded = 0; for (size_t ct=0; ct < mFieldCount; ct++) { mFields[ct].offsetBits = bits; + mFields[ct].offsetBitsUnpadded = bitsUnpadded; bits += mFields[ct].e->getSizeBits() * mFields[ct].arraySize; + bitsUnpadded += mFields[ct].e->getSizeBitsUnpadded() * mFields[ct].arraySize; if (mFields[ct].e->mHasReference) { mHasReference = true; diff --git a/libs/rs/rsElement.h b/libs/rs/rsElement.h index bfdec53b9982..04010faafac8 100644 --- a/libs/rs/rsElement.h +++ b/libs/rs/rsElement.h @@ -43,6 +43,11 @@ public: uint32_t getGLType() const; uint32_t getGLFormat() const; + size_t getSizeBitsUnpadded() const; + size_t getSizeBytesUnpadded() const { + return (getSizeBitsUnpadded() + 7) >> 3; + } + size_t getSizeBits() const; size_t getSizeBytes() const { return (getSizeBits() + 7) >> 3; @@ -55,6 +60,10 @@ public: return mFields[componentNumber].offsetBits >> 3; } + size_t getFieldOffsetBytesUnpadded(uint32_t componentNumber) const { + return mFields[componentNumber].offsetBitsUnpadded >> 3; + } + uint32_t getFieldCount() const {return mFieldCount;} const Element * getField(uint32_t idx) const {return mFields[idx].e.get();} const char * getFieldName(uint32_t idx) const {return mFields[idx].name.string();} @@ -64,6 +73,7 @@ public: RsDataType getType() const {return mComponent.getType();} RsDataKind getKind() const {return mComponent.getKind();} uint32_t getBits() const {return mBits;} + uint32_t getBitsUnpadded() const {return mBitsUnpadded;} void dumpLOGV(const char *prefix) const; virtual void serialize(OStream *stream) const; @@ -112,6 +122,7 @@ protected: String8 name; ObjectBaseRef<const Element> e; uint32_t offsetBits; + uint32_t offsetBitsUnpadded; uint32_t arraySize; } ElementField_t; ElementField_t *mFields; @@ -123,6 +134,7 @@ protected: Element(Context *); Component mComponent; + uint32_t mBitsUnpadded; uint32_t mBits; void compute(); diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index 7efed9d9f948..7b3aa70a9f94 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -651,14 +651,10 @@ void FontState::appendMeshQuad(float x1, float y1, float z1, float x4, float y4, float z4, float u4, float v4) { const uint32_t vertsPerQuad = 4; - const uint32_t floatsPerVert = 5; + const uint32_t floatsPerVert = 6; float *currentPos = mTextMeshPtr + mCurrentQuadIndex * vertsPerQuad * floatsPerVert; - // Cull things that are off the screen - float width = (float)mRSC->getWidth(); - float height = (float)mRSC->getHeight(); - - if (x1 > width || y1 < 0.0f || x2 < 0 || y4 > height) { + if (x1 > mSurfaceWidth || y1 < 0.0f || x2 < 0 || y4 > mSurfaceHeight) { return; } @@ -670,24 +666,28 @@ void FontState::appendMeshQuad(float x1, float y1, float z1, (*currentPos++) = x1; (*currentPos++) = y1; (*currentPos++) = z1; + (*currentPos++) = 0; (*currentPos++) = u1; (*currentPos++) = v1; (*currentPos++) = x2; (*currentPos++) = y2; (*currentPos++) = z2; + (*currentPos++) = 0; (*currentPos++) = u2; (*currentPos++) = v2; (*currentPos++) = x3; (*currentPos++) = y3; (*currentPos++) = z3; + (*currentPos++) = 0; (*currentPos++) = u3; (*currentPos++) = v3; (*currentPos++) = x4; (*currentPos++) = y4; (*currentPos++) = z4; + (*currentPos++) = 0; (*currentPos++) = u4; (*currentPos++) = v4; @@ -746,6 +746,10 @@ void FontState::renderText(const char *text, uint32_t len, int32_t x, int32_t y, return; } + // Cull things that are off the screen + mSurfaceWidth = (float)mRSC->getCurrentSurfaceWidth(); + mSurfaceHeight = (float)mRSC->getCurrentSurfaceHeight(); + currentFont->renderUTF(text, len, x, y, startIndex, numGlyphs, mode, bounds, bitmap, bitmapW, bitmapH); diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h index 679591ca434f..4ca794dcee1c 100644 --- a/libs/rs/rsFont.h +++ b/libs/rs/rsFont.h @@ -160,6 +160,9 @@ public: protected: + float mSurfaceWidth; + float mSurfaceHeight; + friend class Font; struct CacheTextureLine { diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index 4a1362254b0b..871caacd6f66 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -206,8 +206,11 @@ void ProgramVertexState::init(Context *rsc) { void ProgramVertexState::updateSize(Context *rsc) { float *f = static_cast<float *>(mDefaultAlloc->getPtr()); + float surfaceWidth = (float)rsc->getCurrentSurfaceWidth(); + float surfaceHeight = (float)rsc->getCurrentSurfaceHeight(); + Matrix4x4 m; - m.loadOrtho(0,rsc->getWidth(), rsc->getHeight(),0, -1,1); + m.loadOrtho(0, surfaceWidth, surfaceHeight, 0, -1, 1); memcpy(&f[RS_PROGRAM_VERTEX_PROJECTION_OFFSET], m.m, sizeof(m)); memcpy(&f[RS_PROGRAM_VERTEX_MVP_OFFSET], m.m, sizeof(m)); diff --git a/libs/rs/rsScript.cpp b/libs/rs/rsScript.cpp index 16446ddd5c6c..7fc128e8d243 100644 --- a/libs/rs/rsScript.cpp +++ b/libs/rs/rsScript.cpp @@ -26,6 +26,7 @@ Script::Script(Context *rsc) : ObjectBase(rsc) { mSlots = NULL; mTypes = NULL; + mInitialized = false; } Script::~Script() { diff --git a/libs/rs/rsScript.h b/libs/rs/rsScript.h index abb55b8faa4c..99dceafaba37 100644 --- a/libs/rs/rsScript.h +++ b/libs/rs/rsScript.h @@ -67,7 +67,6 @@ public: }; Enviroment_t mEnviroment; - void initSlots(); void setSlot(uint32_t slot, Allocation *a); void setVar(uint32_t slot, const void *val, size_t len); void setVarObj(uint32_t slot, ObjectBase *val); @@ -85,6 +84,7 @@ public: virtual void setupScript(Context *rsc) = 0; virtual uint32_t run(Context *) = 0; protected: + bool mInitialized; ObjectBaseRef<Allocation> *mSlots; ObjectBaseRef<const Type> *mTypes; diff --git a/libs/rs/rsScriptC.cpp b/libs/rs/rsScriptC.cpp index b45366beb109..ce3c643c32a3 100644 --- a/libs/rs/rsScriptC.cpp +++ b/libs/rs/rsScriptC.cpp @@ -21,6 +21,7 @@ #ifndef ANDROID_RS_SERIALIZE #include <bcinfo/BitcodeTranslator.h> +#include <bcinfo/BitcodeWrapper.h> #endif using namespace android; @@ -44,8 +45,10 @@ ScriptC::~ScriptC() { BT = NULL; } #endif - mRSC->mHal.funcs.script.invokeFreeChildren(mRSC, this); - mRSC->mHal.funcs.script.destroy(mRSC, this); + if (mInitialized) { + mRSC->mHal.funcs.script.invokeFreeChildren(mRSC, this); + mRSC->mHal.funcs.script.destroy(mRSC, this); + } } void ScriptC::setupScript(Context *rsc) { @@ -196,7 +199,24 @@ bool ScriptC::runCompiler(Context *rsc, //LOGE("runCompiler %p %p %p %p %p %i", rsc, this, resName, cacheDir, bitcode, bitcodeLen); #ifndef ANDROID_RS_SERIALIZE - uint32_t sdkVersion = rsc->getTargetSdkVersion(); + uint32_t sdkVersion = 0; + bcinfo::BitcodeWrapper bcWrapper((const char *)bitcode, bitcodeLen); + if (!bcWrapper.unwrap()) { + LOGE("Bitcode is not in proper container format (raw or wrapper)"); + return false; + } + + rsAssert(bcWrapper.getHeaderVersion() == 0); + if (bcWrapper.getBCFileType() == bcinfo::BC_WRAPPER) { + sdkVersion = bcWrapper.getTargetAPI(); + } + + if (sdkVersion == 0) { + // This signals that we didn't have a wrapper containing information + // about the bitcode. + sdkVersion = rsc->getTargetSdkVersion(); + } + if (BT) { delete BT; } @@ -212,8 +232,11 @@ bool ScriptC::runCompiler(Context *rsc, bitcodeLen = BT->getTranslatedBitcodeSize(); #endif - rsc->mHal.funcs.script.init(rsc, this, resName, cacheDir, bitcode, bitcodeLen, 0); + if (!rsc->mHal.funcs.script.init(rsc, this, resName, cacheDir, bitcode, bitcodeLen, 0)) { + return false; + } + mInitialized = true; mEnviroment.mFragment.set(rsc->getDefaultProgramFragment()); mEnviroment.mVertex.set(rsc->getDefaultProgramVertex()); mEnviroment.mFragmentStore.set(rsc->getDefaultProgramStore()); diff --git a/libs/rs/rsScriptC_LibGL.cpp b/libs/rs/rsScriptC_LibGL.cpp index 7862f3c7d1d3..26e2374719ce 100644 --- a/libs/rs/rsScriptC_LibGL.cpp +++ b/libs/rs/rsScriptC_LibGL.cpp @@ -79,23 +79,28 @@ void rsrBindProgramRaster(Context *rsc, Script *sc, ProgramRaster *pr) { void rsrBindFrameBufferObjectColorTarget(Context *rsc, Script *sc, Allocation *a, uint32_t slot) { CHECK_OBJ(va); rsc->mFBOCache.bindColorTarget(rsc, a, slot); + rsc->mStateVertex.updateSize(rsc); } void rsrBindFrameBufferObjectDepthTarget(Context *rsc, Script *sc, Allocation *a) { CHECK_OBJ(va); rsc->mFBOCache.bindDepthTarget(rsc, a); + rsc->mStateVertex.updateSize(rsc); } void rsrClearFrameBufferObjectColorTarget(Context *rsc, Script *sc, uint32_t slot) { rsc->mFBOCache.bindColorTarget(rsc, NULL, slot); + rsc->mStateVertex.updateSize(rsc); } void rsrClearFrameBufferObjectDepthTarget(Context *rsc, Script *sc) { rsc->mFBOCache.bindDepthTarget(rsc, NULL); + rsc->mStateVertex.updateSize(rsc); } void rsrClearFrameBufferObjectTargets(Context *rsc, Script *sc) { rsc->mFBOCache.resetAll(rsc); + rsc->mStateVertex.updateSize(rsc); } ////////////////////////////////////////////////////////////////////////////// diff --git a/libs/rs/scriptc/rs_allocation.rsh b/libs/rs/scriptc/rs_allocation.rsh index 154a09910e5a..1cb3a99a7f89 100644 --- a/libs/rs/scriptc/rs_allocation.rsh +++ b/libs/rs/scriptc/rs_allocation.rsh @@ -14,6 +14,31 @@ * limitations under the License. */ +/*! \mainpage notitle + * + * Renderscript is a high-performance runtime that provides graphics rendering and + * compute operations at the native level. Renderscript code is compiled on devices + * at runtime to allow platform-independence as well. + * This reference documentation describes the Renderscript runtime APIs, which you + * can utilize to write Renderscript code in C99. The Renderscript header + * files are automatically included for you, except for the rs_graphics.rsh header. If + * you are doing graphics rendering, include the graphics header file like this: + * + * <code>#include "rs_graphics.rsh"</code> + * + * To use Renderscript, you need to utilize the Renderscript runtime APIs documented here + * as well as the Android framework APIs for Renderscript. + * For documentation on the Android framework APIs, see the <a href= + * "http://developer.android.com/reference/android/renderscript/package-summary.html"> + * android.renderscript</a> package reference. + * For more information on how to develop with Renderscript and how the runtime and + * Android framework APIs interact, see the <a href= + * "http://developer.android.com/guide/topics/renderscript/index.html">Renderscript + * developer guide</a> and the <a href= + * "http://developer.android.com/resources/samples/RenderScript/index.html"> + * Renderscript samples</a>. + */ + /** @file rs_allocation.rsh * \brief Allocation routines * diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/ui/KeyCharacterMap.cpp index 2decfe93215a..77f18dec5c9f 100644 --- a/libs/ui/KeyCharacterMap.cpp +++ b/libs/ui/KeyCharacterMap.cpp @@ -124,17 +124,6 @@ status_t KeyCharacterMap::load(const String8& filename, KeyCharacterMap** outMap return status; } -status_t KeyCharacterMap::loadByDeviceId(int32_t deviceId, KeyCharacterMap** outMap) { - *outMap = NULL; - - String8 filename; - status_t result = getKeyCharacterMapFile(deviceId, filename); - if (!result) { - result = load(filename, outMap); - } - return result; -} - int32_t KeyCharacterMap::getKeyboardType() const { return mType; } diff --git a/libs/ui/Keyboard.cpp b/libs/ui/Keyboard.cpp index 600a951d5f90..10bb39c57518 100644 --- a/libs/ui/Keyboard.cpp +++ b/libs/ui/Keyboard.cpp @@ -173,50 +173,6 @@ bool isEligibleBuiltInKeyboard(const InputDeviceIdentifier& deviceIdentifier, return strstr(deviceIdentifier.name.string(), "-keypad"); } -void setKeyboardProperties(int32_t deviceId, - const InputDeviceIdentifier& deviceIdentifier, - const String8& keyLayoutFile, const String8& keyCharacterMapFile) { - char propName[PROPERTY_KEY_MAX]; - snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); - property_set(propName, deviceIdentifier.name.string()); - snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); - property_set(propName, keyLayoutFile.string()); - snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); - property_set(propName, keyCharacterMapFile.string()); -} - -void clearKeyboardProperties(int32_t deviceId) { - char propName[PROPERTY_KEY_MAX]; - snprintf(propName, sizeof(propName), "hw.keyboards.%u.devname", deviceId); - property_set(propName, ""); - snprintf(propName, sizeof(propName), "hw.keyboards.%u.klfile", deviceId); - property_set(propName, ""); - snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); - property_set(propName, ""); -} - -status_t getKeyCharacterMapFile(int32_t deviceId, String8& outKeyCharacterMapFile) { - if (deviceId != DEVICE_ID_VIRTUAL_KEYBOARD) { - char propName[PROPERTY_KEY_MAX]; - char fn[PROPERTY_VALUE_MAX]; - snprintf(propName, sizeof(propName), "hw.keyboards.%u.kcmfile", deviceId); - if (property_get(propName, fn, "") > 0) { - outKeyCharacterMapFile.setTo(fn); - return OK; - } - } - - // Default to Virtual since the keyboard does not appear to be installed. - outKeyCharacterMapFile.setTo(getInputDeviceConfigurationFilePathByName(String8("Virtual"), - INPUT_DEVICE_CONFIGURATION_FILE_TYPE_KEY_CHARACTER_MAP)); - if (!outKeyCharacterMapFile.isEmpty()) { - return OK; - } - - LOGE("Can't find any key character map files including the Virtual key map!"); - return NAME_NOT_FOUND; -} - static int lookupValueByLabel(const char* literal, const KeycodeLabel *list) { while (list->literal) { if (strcmp(literal, list->literal) == 0) { diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index d168d190adff..544ab744e33a 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -21,6 +21,7 @@ commonSources:= \ Asset.cpp \ AssetDir.cpp \ AssetManager.cpp \ + BasicHashtable.cpp \ BlobCache.cpp \ BufferedTextOutput.cpp \ CallStack.cpp \ diff --git a/libs/utils/BasicHashtable.cpp b/libs/utils/BasicHashtable.cpp new file mode 100644 index 000000000000..fb8ec9f83f16 --- /dev/null +++ b/libs/utils/BasicHashtable.cpp @@ -0,0 +1,338 @@ +/* + * 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. + */ + +#define LOG_TAG "BasicHashtable" + +#include <math.h> + +#include <utils/Log.h> +#include <utils/BasicHashtable.h> +#include <utils/misc.h> + +namespace android { + +BasicHashtableImpl::BasicHashtableImpl(size_t entrySize, bool hasTrivialDestructor, + size_t minimumInitialCapacity, float loadFactor) : + mBucketSize(entrySize + sizeof(Bucket)), mHasTrivialDestructor(hasTrivialDestructor), + mLoadFactor(loadFactor), mSize(0), + mFilledBuckets(0), mBuckets(NULL) { + determineCapacity(minimumInitialCapacity, mLoadFactor, &mBucketCount, &mCapacity); +} + +BasicHashtableImpl::BasicHashtableImpl(const BasicHashtableImpl& other) : + mBucketSize(other.mBucketSize), mHasTrivialDestructor(other.mHasTrivialDestructor), + mCapacity(other.mCapacity), mLoadFactor(other.mLoadFactor), + mSize(other.mSize), mFilledBuckets(other.mFilledBuckets), + mBucketCount(other.mBucketCount), mBuckets(other.mBuckets) { + if (mBuckets) { + SharedBuffer::bufferFromData(mBuckets)->acquire(); + } +} + +void BasicHashtableImpl::dispose() { + if (mBuckets) { + releaseBuckets(mBuckets, mBucketCount); + } +} + +void BasicHashtableImpl::clone() { + if (mBuckets) { + void* newBuckets = allocateBuckets(mBucketCount); + copyBuckets(mBuckets, newBuckets, mBucketCount); + releaseBuckets(mBuckets, mBucketCount); + mBuckets = newBuckets; + } +} + +void BasicHashtableImpl::setTo(const BasicHashtableImpl& other) { + if (mBuckets) { + releaseBuckets(mBuckets, mBucketCount); + } + + mCapacity = other.mCapacity; + mLoadFactor = other.mLoadFactor; + mSize = other.mSize; + mFilledBuckets = other.mFilledBuckets; + mBucketCount = other.mBucketCount; + mBuckets = other.mBuckets; + + if (mBuckets) { + SharedBuffer::bufferFromData(mBuckets)->acquire(); + } +} + +void BasicHashtableImpl::clear() { + if (mBuckets) { + if (mFilledBuckets) { + SharedBuffer* sb = SharedBuffer::bufferFromData(mBuckets); + if (sb->onlyOwner()) { + destroyBuckets(mBuckets, mBucketCount); + for (size_t i = 0; i < mSize; i++) { + Bucket& bucket = bucketAt(mBuckets, i); + bucket.cookie = 0; + } + } else { + releaseBuckets(mBuckets, mBucketCount); + mBuckets = NULL; + } + mFilledBuckets = 0; + } + mSize = 0; + } +} + +ssize_t BasicHashtableImpl::next(ssize_t index) const { + if (mSize) { + while (size_t(++index) < mBucketCount) { + const Bucket& bucket = bucketAt(mBuckets, index); + if (bucket.cookie & Bucket::PRESENT) { + return index; + } + } + } + return -1; +} + +ssize_t BasicHashtableImpl::find(ssize_t index, hash_t hash, + const void* __restrict__ key) const { + if (!mSize) { + return -1; + } + + hash = trimHash(hash); + if (index < 0) { + index = chainStart(hash, mBucketCount); + + const Bucket& bucket = bucketAt(mBuckets, size_t(index)); + if (bucket.cookie & Bucket::PRESENT) { + if (compareBucketKey(bucket, key)) { + return index; + } + } else { + if (!(bucket.cookie & Bucket::COLLISION)) { + return -1; + } + } + } + + size_t inc = chainIncrement(hash, mBucketCount); + for (;;) { + index = chainSeek(index, inc, mBucketCount); + + const Bucket& bucket = bucketAt(mBuckets, size_t(index)); + if (bucket.cookie & Bucket::PRESENT) { + if ((bucket.cookie & Bucket::HASH_MASK) == hash + && compareBucketKey(bucket, key)) { + return index; + } + } + if (!(bucket.cookie & Bucket::COLLISION)) { + return -1; + } + } +} + +size_t BasicHashtableImpl::add(hash_t hash, const void* entry) { + if (!mBuckets) { + mBuckets = allocateBuckets(mBucketCount); + } else { + edit(); + } + + hash = trimHash(hash); + for (;;) { + size_t index = chainStart(hash, mBucketCount); + Bucket* bucket = &bucketAt(mBuckets, size_t(index)); + if (bucket->cookie & Bucket::PRESENT) { + size_t inc = chainIncrement(hash, mBucketCount); + do { + bucket->cookie |= Bucket::COLLISION; + index = chainSeek(index, inc, mBucketCount); + bucket = &bucketAt(mBuckets, size_t(index)); + } while (bucket->cookie & Bucket::PRESENT); + } + + uint32_t collision = bucket->cookie & Bucket::COLLISION; + if (!collision) { + if (mFilledBuckets >= mCapacity) { + rehash(mCapacity * 2, mLoadFactor); + continue; + } + mFilledBuckets += 1; + } + + bucket->cookie = collision | Bucket::PRESENT | hash; + mSize += 1; + initializeBucketEntry(*bucket, entry); + return index; + } +} + +void BasicHashtableImpl::removeAt(size_t index) { + edit(); + + Bucket& bucket = bucketAt(mBuckets, index); + bucket.cookie &= ~Bucket::PRESENT; + if (!(bucket.cookie & Bucket::COLLISION)) { + mFilledBuckets -= 1; + } + mSize -= 1; + if (!mHasTrivialDestructor) { + destroyBucketEntry(bucket); + } +} + +void BasicHashtableImpl::rehash(size_t minimumCapacity, float loadFactor) { + if (minimumCapacity < mSize) { + minimumCapacity = mSize; + } + size_t newBucketCount, newCapacity; + determineCapacity(minimumCapacity, loadFactor, &newBucketCount, &newCapacity); + + if (newBucketCount != mBucketCount || newCapacity != mCapacity) { + if (mBuckets) { + void* newBuckets; + if (mSize) { + newBuckets = allocateBuckets(newBucketCount); + for (size_t i = 0; i < mBucketCount; i++) { + const Bucket& fromBucket = bucketAt(mBuckets, i); + if (fromBucket.cookie & Bucket::PRESENT) { + hash_t hash = fromBucket.cookie & Bucket::HASH_MASK; + size_t index = chainStart(hash, newBucketCount); + Bucket* toBucket = &bucketAt(newBuckets, size_t(index)); + if (toBucket->cookie & Bucket::PRESENT) { + size_t inc = chainIncrement(hash, newBucketCount); + do { + toBucket->cookie |= Bucket::COLLISION; + index = chainSeek(index, inc, newBucketCount); + toBucket = &bucketAt(newBuckets, size_t(index)); + } while (toBucket->cookie & Bucket::PRESENT); + } + toBucket->cookie = Bucket::PRESENT | hash; + initializeBucketEntry(*toBucket, fromBucket.entry); + } + } + } else { + newBuckets = NULL; + } + releaseBuckets(mBuckets, mBucketCount); + mBuckets = newBuckets; + mFilledBuckets = mSize; + } + mBucketCount = newBucketCount; + mCapacity = newCapacity; + } + mLoadFactor = loadFactor; +} + +void* BasicHashtableImpl::allocateBuckets(size_t count) const { + size_t bytes = count * mBucketSize; + SharedBuffer* sb = SharedBuffer::alloc(bytes); + LOG_ALWAYS_FATAL_IF(!sb, "Could not allocate %u bytes for hashtable with %u buckets.", + uint32_t(bytes), uint32_t(count)); + void* buckets = sb->data(); + for (size_t i = 0; i < count; i++) { + Bucket& bucket = bucketAt(buckets, i); + bucket.cookie = 0; + } + return buckets; +} + +void BasicHashtableImpl::releaseBuckets(void* __restrict__ buckets, size_t count) const { + SharedBuffer* sb = SharedBuffer::bufferFromData(buckets); + if (sb->release(SharedBuffer::eKeepStorage) == 1) { + destroyBuckets(buckets, count); + SharedBuffer::dealloc(sb); + } +} + +void BasicHashtableImpl::destroyBuckets(void* __restrict__ buckets, size_t count) const { + if (!mHasTrivialDestructor) { + for (size_t i = 0; i < count; i++) { + Bucket& bucket = bucketAt(buckets, i); + if (bucket.cookie & Bucket::PRESENT) { + destroyBucketEntry(bucket); + } + } + } +} + +void BasicHashtableImpl::copyBuckets(const void* __restrict__ fromBuckets, + void* __restrict__ toBuckets, size_t count) const { + for (size_t i = 0; i < count; i++) { + const Bucket& fromBucket = bucketAt(fromBuckets, i); + Bucket& toBucket = bucketAt(toBuckets, i); + toBucket.cookie = fromBucket.cookie; + if (fromBucket.cookie & Bucket::PRESENT) { + initializeBucketEntry(toBucket, fromBucket.entry); + } + } +} + +// Table of 31-bit primes where each prime is no less than twice as large +// as the previous one. Generated by "primes.py". +static size_t PRIMES[] = { + 5, + 11, + 23, + 47, + 97, + 197, + 397, + 797, + 1597, + 3203, + 6421, + 12853, + 25717, + 51437, + 102877, + 205759, + 411527, + 823117, + 1646237, + 3292489, + 6584983, + 13169977, + 26339969, + 52679969, + 105359939, + 210719881, + 421439783, + 842879579, + 1685759167, + 0, +}; + +void BasicHashtableImpl::determineCapacity(size_t minimumCapacity, float loadFactor, + size_t* __restrict__ outBucketCount, size_t* __restrict__ outCapacity) { + LOG_ALWAYS_FATAL_IF(loadFactor <= 0.0f || loadFactor > 1.0f, + "Invalid load factor %0.3f. Must be in the range (0, 1].", loadFactor); + + size_t count = ceilf(minimumCapacity / loadFactor) + 1; + size_t i = 0; + while (count > PRIMES[i] && i < NELEM(PRIMES)) { + i++; + } + count = PRIMES[i]; + LOG_ALWAYS_FATAL_IF(!count, "Could not determine required number of buckets for " + "hashtable with minimum capacity %u and load factor %0.3f.", + uint32_t(minimumCapacity), loadFactor); + *outBucketCount = count; + *outCapacity = ceilf((count - 1) * loadFactor); +} + +}; // namespace android diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index b4c581b03915..c2a5e5534f3a 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -101,17 +101,10 @@ void CallStack::dump(const char* prefix) const { get_backtrace_symbols(mStack, mCount, symbols); for (size_t i = 0; i < mCount; i++) { - const backtrace_frame_t& frame = mStack[i]; - const backtrace_symbol_t& symbol = symbols[i]; - const char* mapName = symbol.map_name ? symbol.map_name : "<unknown>"; - const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; - if (symbolName) { - LOGD("%s#%02d pc %08x %s (%s)\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName, symbolName); - } else { - LOGD("%s#%02d pc %08x %s\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName); - } + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &mStack[i], &symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + LOGD("%s%s", prefix, line); } free_backtrace_symbols(symbols, mCount); } @@ -122,17 +115,12 @@ String8 CallStack::toString(const char* prefix) const { get_backtrace_symbols(mStack, mCount, symbols); for (size_t i = 0; i < mCount; i++) { - const backtrace_frame_t& frame = mStack[i]; - const backtrace_symbol_t& symbol = symbols[i]; - const char* mapName = symbol.map_name ? symbol.map_name : "<unknown>"; - const char* symbolName = symbol.demangled_name ? symbol.demangled_name : symbol.name; - if (symbolName) { - str.appendFormat("%s#%02d pc %08x %s (%s)\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName, symbolName); - } else { - str.appendFormat("%s#%02d pc %08x %s\n", prefix, - int(i), uint32_t(symbol.relative_pc), mapName); - } + char line[MAX_BACKTRACE_LINE_LENGTH]; + format_backtrace_line(i, &mStack[i], &symbols[i], + line, MAX_BACKTRACE_LINE_LENGTH); + str.append(prefix); + str.append(line); + str.append("\n"); } free_backtrace_symbols(symbols, mCount); return str; diff --git a/libs/utils/primes.py b/libs/utils/primes.py new file mode 100755 index 000000000000..e161dd801ed9 --- /dev/null +++ b/libs/utils/primes.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python2.6 +# +# 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. +# + +# +# Generates a table of prime numbers for use in BasicHashtable.cpp. +# +# Each prime is chosen such that it is a little more than twice as large as +# the previous prime in the table. This makes it easier to choose a new +# hashtable size when the underlying array is grown by as nominal factor +# of two each time. +# + +def is_odd_prime(n): + limit = (n - 1) / 2 + d = 3 + while d <= limit: + if n % d == 0: + return False + d += 2 + return True + +print "static size_t PRIMES[] = {" + +n = 5 +max = 2**31 - 1 +while n < max: + print " %d," % (n) + n = n * 2 + 1 + while not is_odd_prime(n): + n += 2 + +print " 0," +print "};" diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index b97f52f5b892..58230f429ed0 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -4,9 +4,10 @@ include $(CLEAR_VARS) # Build the unit tests. test_src_files := \ + BasicHashtable_test.cpp \ BlobCache_test.cpp \ - ObbFile_test.cpp \ Looper_test.cpp \ + ObbFile_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ ZipFileRO_test.cpp \ diff --git a/libs/utils/tests/BasicHashtable_test.cpp b/libs/utils/tests/BasicHashtable_test.cpp new file mode 100644 index 000000000000..764082dc04e1 --- /dev/null +++ b/libs/utils/tests/BasicHashtable_test.cpp @@ -0,0 +1,577 @@ +/* + * 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. + */ + +#define LOG_TAG "BasicHashtable_test" + +#include <utils/BasicHashtable.h> +#include <cutils/log.h> +#include <gtest/gtest.h> +#include <unistd.h> + +namespace android { + +typedef int SimpleKey; +typedef int SimpleValue; +typedef key_value_pair_t<SimpleKey, SimpleValue> SimpleEntry; +typedef BasicHashtable<SimpleKey, SimpleEntry> SimpleHashtable; + +struct ComplexKey { + int k; + + explicit ComplexKey(int k) : k(k) { + instanceCount += 1; + } + + ComplexKey(const ComplexKey& other) : k(other.k) { + instanceCount += 1; + } + + ~ComplexKey() { + instanceCount -= 1; + } + + bool operator ==(const ComplexKey& other) const { + return k == other.k; + } + + bool operator !=(const ComplexKey& other) const { + return k != other.k; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexKey::instanceCount = 0; + +template<> inline hash_t hash_type(const ComplexKey& value) { + return hash_type(value.k); +} + +struct ComplexValue { + int v; + + explicit ComplexValue(int v) : v(v) { + instanceCount += 1; + } + + ComplexValue(const ComplexValue& other) : v(other.v) { + instanceCount += 1; + } + + ~ComplexValue() { + instanceCount -= 1; + } + + static ssize_t instanceCount; +}; + +ssize_t ComplexValue::instanceCount = 0; + +typedef key_value_pair_t<ComplexKey, ComplexValue> ComplexEntry; +typedef BasicHashtable<ComplexKey, ComplexEntry> ComplexHashtable; + +class BasicHashtableTest : public testing::Test { +protected: + virtual void SetUp() { + ComplexKey::instanceCount = 0; + ComplexValue::instanceCount = 0; + } + + virtual void TearDown() { + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + } + + void assertInstanceCount(ssize_t keys, ssize_t values) { + if (keys != ComplexKey::instanceCount || values != ComplexValue::instanceCount) { + FAIL() << "Expected " << keys << " keys and " << values << " values " + "but there were actually " << ComplexKey::instanceCount << " keys and " + << ComplexValue::instanceCount << " values"; + } + } + +public: + template <typename TKey, typename TEntry> + static void cookieAt(const BasicHashtable<TKey, TEntry>& h, size_t index, + bool* collision, bool* present, hash_t* hash) { + uint32_t cookie = h.cookieAt(index); + *collision = cookie & BasicHashtable<TKey, TEntry>::Bucket::COLLISION; + *present = cookie & BasicHashtable<TKey, TEntry>::Bucket::PRESENT; + *hash = cookie & BasicHashtable<TKey, TEntry>::Bucket::HASH_MASK; + } + + template <typename TKey, typename TEntry> + static const void* getBuckets(const BasicHashtable<TKey, TEntry>& h) { + return h.mBuckets; + } +}; + +template <typename TKey, typename TValue> +static size_t add(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, + const TKey& key, const TValue& value) { + return h.add(hash_type(key), key_value_pair_t<TKey, TValue>(key, value)); +} + +template <typename TKey, typename TValue> +static ssize_t find(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, + ssize_t index, const TKey& key) { + return h.find(index, hash_type(key), key); +} + +template <typename TKey, typename TValue> +static bool remove(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h, + const TKey& key) { + ssize_t index = find(h, -1, key); + if (index >= 0) { + h.removeAt(index); + return true; + } + return false; +} + +template <typename TEntry> +static void getKeyValue(const TEntry& entry, int* key, int* value); + +template <> void getKeyValue(const SimpleEntry& entry, int* key, int* value) { + *key = entry.key; + *value = entry.value; +} + +template <> void getKeyValue(const ComplexEntry& entry, int* key, int* value) { + *key = entry.key.k; + *value = entry.value.v; +} + +template <typename TKey, typename TValue> +static void dump(BasicHashtable<TKey, key_value_pair_t<TKey, TValue> >& h) { + LOGD("hashtable %p, size=%u, capacity=%u, bucketCount=%u", + &h, h.size(), h.capacity(), h.bucketCount()); + for (size_t i = 0; i < h.bucketCount(); i++) { + bool collision, present; + hash_t hash; + BasicHashtableTest::cookieAt(h, i, &collision, &present, &hash); + if (present) { + int key, value; + getKeyValue(h.entryAt(i), &key, &value); + LOGD(" [%3u] = collision=%d, present=%d, hash=0x%08x, key=%3d, value=%3d, " + "hash_type(key)=0x%08x", + i, collision, present, hash, key, value, hash_type(key)); + } else { + LOGD(" [%3u] = collision=%d, present=%d", + i, collision, present); + } + } +} + +TEST_F(BasicHashtableTest, DefaultConstructor_WithDefaultProperties) { + SimpleHashtable h; + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithNonUnityLoadFactor) { + SimpleHashtable h(52, 0.8f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(77U, h.capacity()); + EXPECT_EQ(97U, h.bucketCount()); + EXPECT_EQ(0.8f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndExactCapacity) { + SimpleHashtable h(46, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f + EXPECT_EQ(47U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Constructor_WithUnityLoadFactorAndInexactCapacity) { + SimpleHashtable h(42, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(46U, h.capacity()); // must be one less than bucketCount because loadFactor == 1.0f + EXPECT_EQ(47U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_OneEntry) { + SimpleHashtable h; + ssize_t index = find(h, -1, 8); + ASSERT_EQ(-1, index); + + index = add(h, 8, 1); + ASSERT_EQ(1U, h.size()); + + ASSERT_EQ(index, find(h, -1, 8)); + ASSERT_EQ(8, h.entryAt(index).key); + ASSERT_EQ(1, h.entryAt(index).value); + + index = find(h, index, 8); + ASSERT_EQ(-1, index); + + ASSERT_TRUE(remove(h, 8)); + ASSERT_EQ(0U, h.size()); + + index = find(h, -1, 8); + ASSERT_EQ(-1, index); +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithUniqueKey) { + const size_t N = 11; + + SimpleHashtable h; + for (size_t i = 0; i < N; i++) { + ssize_t index = find(h, -1, int(i)); + ASSERT_EQ(-1, index); + + index = add(h, int(i), int(i * 10)); + ASSERT_EQ(i + 1, h.size()); + + ASSERT_EQ(index, find(h, -1, int(i))); + ASSERT_EQ(int(i), h.entryAt(index).key); + ASSERT_EQ(int(i * 10), h.entryAt(index).value); + + index = find(h, index, int(i)); + ASSERT_EQ(-1, index); + } + + for (size_t i = N; --i > 0; ) { + ASSERT_TRUE(remove(h, int(i))) << "i = " << i; + ASSERT_EQ(i, h.size()); + + ssize_t index = find(h, -1, int(i)); + ASSERT_EQ(-1, index); + } +} + +TEST_F(BasicHashtableTest, FindAddFindRemoveFind_MultipleEntryWithDuplicateKey) { + const size_t N = 11; + const int K = 1; + + SimpleHashtable h; + for (size_t i = 0; i < N; i++) { + ssize_t index = find(h, -1, K); + if (i == 0) { + ASSERT_EQ(-1, index); + } else { + ASSERT_NE(-1, index); + } + + add(h, K, int(i)); + ASSERT_EQ(i + 1, h.size()); + + index = -1; + int values = 0; + for (size_t j = 0; j <= i; j++) { + index = find(h, index, K); + ASSERT_GE(index, 0); + ASSERT_EQ(K, h.entryAt(index).key); + values |= 1 << h.entryAt(index).value; + } + ASSERT_EQ(values, (1 << (i + 1)) - 1); + + index = find(h, index, K); + ASSERT_EQ(-1, index); + } + + for (size_t i = N; --i > 0; ) { + ASSERT_TRUE(remove(h, K)) << "i = " << i; + ASSERT_EQ(i, h.size()); + + ssize_t index = -1; + for (size_t j = 0; j < i; j++) { + index = find(h, index, K); + ASSERT_GE(index, 0); + ASSERT_EQ(K, h.entryAt(index).key); + } + + index = find(h, index, K); + ASSERT_EQ(-1, index); + } +} + +TEST_F(BasicHashtableTest, Clear_WhenAlreadyEmpty_DoesNothing) { + SimpleHashtable h; + h.clear(); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_RemovesThem) { + SimpleHashtable h; + add(h, 0, 0); + add(h, 1, 0); + h.clear(); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Clear_AfterElementsAdded_DestroysThem) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + h.clear(); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Remove_AfterElementsAdded_DestroysThem) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + ASSERT_TRUE(remove(h, ComplexKey(0))); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); + + ASSERT_TRUE(remove(h, ComplexKey(1))); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); +} + +TEST_F(BasicHashtableTest, Destructor_AfterElementsAdded_DestroysThem) { + { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(0)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + } // h is destroyed here + + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Next_WhenEmpty_ReturnsMinusOne) { + SimpleHashtable h; + + ASSERT_EQ(-1, h.next(-1)); +} + +TEST_F(BasicHashtableTest, Next_WhenNonEmpty_IteratesOverAllEntries) { + const int N = 88; + + SimpleHashtable h; + for (int i = 0; i < N; i++) { + add(h, i, i * 10); + } + + bool set[N]; + memset(set, 0, sizeof(bool) * N); + int count = 0; + for (ssize_t index = -1; (index = h.next(index)) != -1; ) { + ASSERT_GE(index, 0); + ASSERT_LT(size_t(index), h.bucketCount()); + + const SimpleEntry& entry = h.entryAt(index); + ASSERT_GE(entry.key, 0); + ASSERT_LT(entry.key, N); + ASSERT_EQ(false, set[entry.key]); + ASSERT_EQ(entry.key * 10, entry.value); + + set[entry.key] = true; + count += 1; + } + ASSERT_EQ(N, count); +} + +TEST_F(BasicHashtableTest, Add_RehashesOnDemand) { + SimpleHashtable h; + size_t initialCapacity = h.capacity(); + size_t initialBucketCount = h.bucketCount(); + + for (size_t i = 0; i < initialCapacity; i++) { + add(h, int(i), 0); + } + + EXPECT_EQ(initialCapacity, h.size()); + EXPECT_EQ(initialCapacity, h.capacity()); + EXPECT_EQ(initialBucketCount, h.bucketCount()); + + add(h, -1, -1); + + EXPECT_EQ(initialCapacity + 1, h.size()); + EXPECT_GT(h.capacity(), initialCapacity); + EXPECT_GT(h.bucketCount(), initialBucketCount); + EXPECT_GT(h.bucketCount(), h.capacity()); +} + +TEST_F(BasicHashtableTest, Rehash_WhenCapacityAndBucketCountUnchanged_DoesNothing) { + ComplexHashtable h; + add(h, ComplexKey(0), ComplexValue(0)); + const void* oldBuckets = getBuckets(h); + ASSERT_NE((void*)NULL, oldBuckets); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); + + h.rehash(h.capacity(), h.loadFactor()); + + ASSERT_EQ(oldBuckets, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(1, 1)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasNoBuckets_ButDoesNotAllocateBuckets) { + ComplexHashtable h; + ASSERT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + h.rehash(9, 1.0f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(10U, h.capacity()); + EXPECT_EQ(11U, h.bucketCount()); + EXPECT_EQ(1.0f, h.loadFactor()); + EXPECT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenEmptyAndHasBuckets_ReleasesBucketsAndSetsCapacity) { + ComplexHashtable h(10); + add(h, ComplexKey(0), ComplexValue(0)); + ASSERT_TRUE(remove(h, ComplexKey(0))); + ASSERT_NE((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); + + h.rehash(0, 0.75f); + + EXPECT_EQ(0U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); + EXPECT_EQ((void*)NULL, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(0, 0)); +} + +TEST_F(BasicHashtableTest, Rehash_WhenLessThanCurrentCapacity_ShrinksBuckets) { + ComplexHashtable h(10); + add(h, ComplexKey(0), ComplexValue(0)); + add(h, ComplexKey(1), ComplexValue(1)); + const void* oldBuckets = getBuckets(h); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + + h.rehash(0, 0.75f); + + EXPECT_EQ(2U, h.size()); + EXPECT_EQ(3U, h.capacity()); + EXPECT_EQ(5U, h.bucketCount()); + EXPECT_EQ(0.75f, h.loadFactor()); + EXPECT_NE(oldBuckets, getBuckets(h)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); +} + +TEST_F(BasicHashtableTest, CopyOnWrite) { + ComplexHashtable h1; + add(h1, ComplexKey(0), ComplexValue(0)); + add(h1, ComplexKey(1), ComplexValue(1)); + const void* originalBuckets = getBuckets(h1); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ssize_t index0 = find(h1, -1, ComplexKey(0)); + EXPECT_GE(index0, 0); + + // copy constructor acquires shared reference + ComplexHashtable h2(h1); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h2)); + EXPECT_EQ(h1.size(), h2.size()); + EXPECT_EQ(h1.capacity(), h2.capacity()); + EXPECT_EQ(h1.bucketCount(), h2.bucketCount()); + EXPECT_EQ(h1.loadFactor(), h2.loadFactor()); + EXPECT_EQ(index0, find(h2, -1, ComplexKey(0))); + + // operator= acquires shared reference + ComplexHashtable h3; + h3 = h2; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h3)); + EXPECT_EQ(h1.size(), h3.size()); + EXPECT_EQ(h1.capacity(), h3.capacity()); + EXPECT_EQ(h1.bucketCount(), h3.bucketCount()); + EXPECT_EQ(h1.loadFactor(), h3.loadFactor()); + EXPECT_EQ(index0, find(h3, -1, ComplexKey(0))); + + // editEntryAt copies shared contents + h1.editEntryAt(index0).value.v = 42; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(42, h1.entryAt(index0).value.v); + EXPECT_EQ(0, h2.entryAt(index0).value.v); + EXPECT_EQ(0, h3.entryAt(index0).value.v); + + // clear releases reference to shared contents + h2.clear(); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + EXPECT_EQ(0U, h2.size()); + ASSERT_NE(originalBuckets, getBuckets(h2)); + + // operator= acquires shared reference, destroys unshared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + EXPECT_EQ(h3.size(), h1.size()); + EXPECT_EQ(h3.capacity(), h1.capacity()); + EXPECT_EQ(h3.bucketCount(), h1.bucketCount()); + EXPECT_EQ(h3.loadFactor(), h1.loadFactor()); + EXPECT_EQ(index0, find(h1, -1, ComplexKey(0))); + + // add copies shared contents + add(h1, ComplexKey(2), ComplexValue(2)); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(5, 5)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(3U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); + + // remove copies shared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + h1.removeAt(index0); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(3, 3)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(1U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); + + // rehash copies shared contents + h1 = h3; + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(2, 2)); + ASSERT_EQ(originalBuckets, getBuckets(h1)); + h1.rehash(10, 1.0f); + ASSERT_NO_FATAL_FAILURE(assertInstanceCount(4, 4)); + ASSERT_NE(originalBuckets, getBuckets(h1)); + EXPECT_EQ(2U, h1.size()); + EXPECT_EQ(0U, h2.size()); + EXPECT_EQ(2U, h3.size()); +} + +} // namespace android diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index 826b80a51ad9..f13a6a2b8887 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -2687,21 +2687,21 @@ public class AudioService extends IAudioService.Stub { mCallingUid = uid; } - private void unlinkToDeath() { - if (mSourceRef != null && mHandler != null) { - mSourceRef.unlinkToDeath(mHandler, 0); + public void unlinkToDeath() { + try { + if (mSourceRef != null && mHandler != null) { + mSourceRef.unlinkToDeath(mHandler, 0); + mHandler = null; + } + } catch (java.util.NoSuchElementException e) { + Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()"); } } @Override protected void finalize() throws Throwable { - try { - unlinkToDeath(); - } catch (java.util.NoSuchElementException e) { - Log.w(TAG, e + " thrown by unlinkToDeath() during finalize, ignoring"); - } finally { - super.finalize(); - } + unlinkToDeath(); // unlink exception handled inside method + super.finalize(); } } @@ -2733,11 +2733,12 @@ public class AudioService extends IAudioService.Stub { * focus, notify the next item in the stack it gained focus. */ private void removeFocusStackEntry(String clientToRemove, boolean signal) { - // is the current top of the focus stack abandoning focus? (because of death or request) + // is the current top of the focus stack abandoning focus? (because of request, not death) if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) { //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); - mFocusStack.pop(); + FocusStackEntry fse = mFocusStack.pop(); + fse.unlinkToDeath(); if (signal) { // notify the new top of the stack it gained focus notifyTopOfAudioFocusStack(); @@ -2756,6 +2757,7 @@ public class AudioService extends IAudioService.Stub { Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " + fse.mClientId); stackIterator.remove(); + fse.unlinkToDeath(); } } } @@ -2764,7 +2766,7 @@ public class AudioService extends IAudioService.Stub { /** * Helper function: * Called synchronized on mAudioFocusLock - * Remove focus listeners from the focus stack for a particular client. + * Remove focus listeners from the focus stack for a particular client when it has died. */ private void removeFocusStackEntryForClient(IBinder cb) { // is the owner of the audio focus part of the client to remove? @@ -2777,6 +2779,7 @@ public class AudioService extends IAudioService.Stub { Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " + fse.mClientId); stackIterator.remove(); + // the client just died, no need to unlink to its death } } if (isTopOfStackForClientToRemove) { @@ -2868,7 +2871,8 @@ public class AudioService extends IAudioService.Stub { } // the reason for the audio focus request has changed: remove the current top of // stack and respond as if we had a new focus owner - mFocusStack.pop(); + FocusStackEntry fse = mFocusStack.pop(); + fse.unlinkToDeath(); } // notify current top of stack it is losing focus diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java index eb32563d7388..8d71dcf644d0 100644 --- a/media/java/android/media/MediaPlayer.java +++ b/media/java/android/media/MediaPlayer.java @@ -1149,14 +1149,20 @@ public class MediaPlayer /** * Releases resources associated with this MediaPlayer object. * It is considered good practice to call this method when you're - * done using the MediaPlayer. For instance, whenever the Activity - * of an application is paused, this method should be invoked to - * release the MediaPlayer object. In addition to unnecessary resources - * (such as memory and instances of codecs) being hold, failure to - * call this method immediately if a MediaPlayer object is no longer - * needed may also lead to continuous battery consumption for mobile - * devices, and playback failure if no multiple instances of the - * same codec is supported on a device. + * done using the MediaPlayer. In particular, whenever an Activity + * of an application is paused (its onPause() method is called), + * or stopped (its onStop() method is called), this method should be + * invoked to release the MediaPlayer object, unless the application + * has a special need to keep the object around. In addition to + * unnecessary resources (such as memory and instances of codecs) + * being held, failure to call this method immediately if a + * MediaPlayer object is no longer needed may also lead to + * continuous battery consumption for mobile devices, and playback + * failure for other applications if no multiple instances of the + * same codec are supported on a device. Even if multiple instances + * of the same codec are supported, some performance degradation + * may be expected when unnecessary multiple instances are used + * at the same time. */ public void release() { stayAwake(false); diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index ceeacbb513ea..08e603252c5c 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -926,7 +926,20 @@ public class MediaRecorder /** * Releases resources associated with this MediaRecorder object. * It is good practice to call this method when you're done - * using the MediaRecorder. + * using the MediaRecorder. In particular, whenever an Activity + * of an application is paused (its onPause() method is called), + * or stopped (its onStop() method is called), this method should be + * invoked to release the MediaRecorder object, unless the application + * has a special need to keep the object around. In addition to + * unnecessary resources (such as memory and instances of codecs) + * being held, failure to call this method immediately if a + * MediaRecorder object is no longer needed may also lead to + * continuous battery consumption for mobile devices, and recording + * failure for other applications if no multiple instances of the + * same codec are supported on a device. Even if multiple instances + * of the same codec are supported, some performance degradation + * may be expected when unnecessary multiple instances are used + * at the same time. */ public native void release(); diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java index 4e271c7bf568..98617d200f54 100644..100755 --- a/media/java/android/mtp/MtpDatabase.java +++ b/media/java/android/mtp/MtpDatabase.java @@ -38,6 +38,7 @@ import android.view.WindowManager; import java.io.File; import java.util.HashMap; +import java.util.Locale; /** * {@hide} @@ -120,6 +121,20 @@ public class MtpDatabase { mMediaStoragePath = storagePath; mObjectsUri = Files.getMtpObjectsUri(volumeName); mMediaScanner = new MediaScanner(context); + + // Set locale to MediaScanner. + Locale locale = context.getResources().getConfiguration().locale; + if (locale != null) { + String language = locale.getLanguage(); + String country = locale.getCountry(); + if (language != null) { + if (country != null) { + mMediaScanner.setLocale(language + "_" + country); + } else { + mMediaScanner.setLocale(language); + } + } + } initDeviceProperties(context); } diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp index f32929ef27f3..1d8e15bac77e 100644 --- a/media/libmedia/AudioRecord.cpp +++ b/media/libmedia/AudioRecord.cpp @@ -80,7 +80,8 @@ status_t AudioRecord::getMinFrameCount( // --------------------------------------------------------------------------- AudioRecord::AudioRecord() - : mStatus(NO_INIT), mSessionId(0) + : mStatus(NO_INIT), mSessionId(0), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { } @@ -95,7 +96,8 @@ AudioRecord::AudioRecord( void* user, int notificationFrames, int sessionId) - : mStatus(NO_INIT), mSessionId(0) + : mStatus(NO_INIT), mSessionId(0), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { mStatus = set(inputSource, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, sessionId); @@ -326,9 +328,11 @@ status_t AudioRecord::start() cblk->bufferTimeoutMs = MAX_RUN_TIMEOUT_MS; cblk->waitTimeMs = 0; if (t != 0) { - t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO); + t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO); } else { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); + mPreviousPriority = getpriority(PRIO_PROCESS, 0); + mPreviousSchedulingGroup = androidGetThreadSchedulingGroup(0); + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); } } else { mActive = 0; @@ -363,7 +367,8 @@ status_t AudioRecord::stop() if (t != 0) { t->requestExit(); } else { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + setpriority(PRIO_PROCESS, 0, mPreviousPriority); + androidSetThreadSchedulingGroup(0, mPreviousSchedulingGroup); } } diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index 8ccba8017ea0..7e55fbd7e73e 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -79,7 +79,8 @@ status_t AudioTrack::getMinFrameCount( // --------------------------------------------------------------------------- AudioTrack::AudioTrack() - : mStatus(NO_INIT) + : mStatus(NO_INIT), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { } @@ -94,7 +95,8 @@ AudioTrack::AudioTrack( void* user, int notificationFrames, int sessionId) - : mStatus(NO_INIT) + : mStatus(NO_INIT), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, @@ -112,7 +114,8 @@ AudioTrack::AudioTrack( void* user, int notificationFrames, int sessionId) - : mStatus(NO_INIT) + : mStatus(NO_INIT), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { mStatus = set(streamType, sampleRate, format, channelMask, 0, flags, cbf, user, notificationFrames, @@ -346,9 +349,11 @@ void AudioTrack::start() cblk->waitTimeMs = 0; android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags); if (t != 0) { - t->run("AudioTrackThread", ANDROID_PRIORITY_AUDIO); + t->run("AudioTrackThread", ANDROID_PRIORITY_AUDIO); } else { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_AUDIO); + mPreviousPriority = getpriority(PRIO_PROCESS, 0); + mPreviousSchedulingGroup = androidGetThreadSchedulingGroup(0); + androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO); } ALOGV("start %p before lock cblk %p", this, mCblk); @@ -370,7 +375,8 @@ void AudioTrack::start() if (t != 0) { t->requestExit(); } else { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + setpriority(PRIO_PROCESS, 0, mPreviousPriority); + androidSetThreadSchedulingGroup(0, mPreviousSchedulingGroup); } } } @@ -408,7 +414,8 @@ void AudioTrack::stop() if (t != 0) { t->requestExit(); } else { - setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_NORMAL); + setpriority(PRIO_PROCESS, 0, mPreviousPriority); + androidSetThreadSchedulingGroup(0, mPreviousSchedulingGroup); } } diff --git a/media/libmedia/JetPlayer.cpp b/media/libmedia/JetPlayer.cpp index 3d94b49cb715..afa84b790851 100644 --- a/media/libmedia/JetPlayer.cpp +++ b/media/libmedia/JetPlayer.cpp @@ -100,7 +100,8 @@ int JetPlayer::init() { Mutex::Autolock l(mMutex); ALOGV("JetPlayer::init(): trying to start render thread"); - createThreadEtc(renderThread, this, "jetRenderThread", ANDROID_PRIORITY_AUDIO); + mThread = new JetPlayerThread(this); + mThread->run("jetRenderThread", ANDROID_PRIORITY_AUDIO); mCondition.wait(mMutex); } if (mTid > 0) { @@ -156,12 +157,6 @@ int JetPlayer::release() //------------------------------------------------------------------------------------------------- -int JetPlayer::renderThread(void* p) { - - return ((JetPlayer*)p)->render(); -} - -//------------------------------------------------------------------------------------------------- int JetPlayer::render() { EAS_RESULT result = EAS_FAILURE; EAS_I32 count; diff --git a/media/libmedia/MediaScannerClient.cpp b/media/libmedia/MediaScannerClient.cpp index 7a7aeb638b25..629b165dd491 100644 --- a/media/libmedia/MediaScannerClient.cpp +++ b/media/libmedia/MediaScannerClient.cpp @@ -82,7 +82,7 @@ status_t MediaScannerClient::addStringTag(const char* name, const char* value) // save the strings for later so they can be used for native encoding detection mNames->push_back(name); mValues->push_back(value); - return true; + return OK; } // else fall through } diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp index 85b1f9d8edbf..49469562da4a 100644 --- a/media/libmediaplayerservice/MidiFile.cpp +++ b/media/libmediaplayerservice/MidiFile.cpp @@ -86,7 +86,8 @@ MidiFile::MidiFile() : // create playback thread { Mutex::Autolock l(mMutex); - createThreadEtc(renderThread, this, "midithread", ANDROID_PRIORITY_AUDIO); + mThread = new MidiFileThread(this); + mThread->run("midithread", ANDROID_PRIORITY_AUDIO); mCondition.wait(mMutex); ALOGV("thread started"); } @@ -427,11 +428,6 @@ status_t MidiFile::createOutputTrack() { return NO_ERROR; } -int MidiFile::renderThread(void* p) { - - return ((MidiFile*)p)->render(); -} - int MidiFile::render() { EAS_RESULT result = EAS_FAILURE; EAS_I32 count; diff --git a/media/libmediaplayerservice/MidiFile.h b/media/libmediaplayerservice/MidiFile.h index 34693898700c..2a0dcf9d17a2 100644 --- a/media/libmediaplayerservice/MidiFile.h +++ b/media/libmediaplayerservice/MidiFile.h @@ -24,6 +24,7 @@ namespace android { +// Note that the name MidiFile is misleading; this actually represents a MIDI file player class MidiFile : public MediaPlayerInterface { public: MidiFile(); @@ -65,7 +66,6 @@ public: private: status_t createOutputTrack(); status_t reset_nosync(); - static int renderThread(void*); int render(); void updateState(){ EAS_State(mEasData, mEasHandle, &mState); } @@ -84,6 +84,29 @@ private: bool mPaused; volatile bool mRender; pid_t mTid; + + class MidiFileThread : public Thread { + public: + MidiFileThread(MidiFile *midiPlayer) : mMidiFile(midiPlayer) { + } + + protected: + virtual ~MidiFileThread() {} + + private: + MidiFile *mMidiFile; + + bool threadLoop() { + int result; + result = mMidiFile->render(); + return false; + } + + MidiFileThread(const MidiFileThread &); + MidiFileThread &operator=(const MidiFileThread &); + }; + + sp<MidiFileThread> mThread; }; }; // namespace android diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index d254449d8448..1f225a7108c9 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -224,17 +224,18 @@ AwesomePlayer::~AwesomePlayer() { mClient.disconnect(); } -void AwesomePlayer::cancelPlayerEvents(bool keepBufferingGoing) { +void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) { mQueue.cancelEvent(mVideoEvent->eventID()); mVideoEventPending = false; - mQueue.cancelEvent(mStreamDoneEvent->eventID()); - mStreamDoneEventPending = false; - mQueue.cancelEvent(mCheckAudioStatusEvent->eventID()); - mAudioStatusEventPending = false; mQueue.cancelEvent(mVideoLagEvent->eventID()); mVideoLagEventPending = false; - if (!keepBufferingGoing) { + if (!keepNotifications) { + mQueue.cancelEvent(mStreamDoneEvent->eventID()); + mStreamDoneEventPending = false; + mQueue.cancelEvent(mCheckAudioStatusEvent->eventID()); + mAudioStatusEventPending = false; + mQueue.cancelEvent(mBufferingEvent->eventID()); mBufferingEventPending = false; } @@ -1095,7 +1096,7 @@ status_t AwesomePlayer::pause_l(bool at_eos) { return OK; } - cancelPlayerEvents(true /* keepBufferingGoing */); + cancelPlayerEvents(true /* keepNotifications */); if (mAudioPlayer != NULL && (mFlags & AUDIO_RUNNING)) { if (at_eos) { 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/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index df363c91ae1c..4345184c7c7d 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -94,7 +94,7 @@ static MediaScanResult HandleMIDI( char buffer[20]; sprintf(buffer, "%ld", temp); status_t status = client->addStringTag("duration", buffer); - if (status) { + if (status != OK) { return MEDIA_SCAN_RESULT_ERROR; } return MEDIA_SCAN_RESULT_OK; @@ -178,7 +178,7 @@ MediaScanResult StagefrightMediaScanner::processFileInternal( const char *value; if ((value = mRetriever->extractMetadata(kKeyMap[i].key)) != NULL) { status = client.addStringTag(kKeyMap[i].tag, value); - if (status) { + if (status != OK) { return MEDIA_SCAN_RESULT_ERROR; } } diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index c13d6cba98d7..0985f479d561 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -250,7 +250,7 @@ private: void notifyVideoSize_l(); void seekAudioIfNecessary_l(); - void cancelPlayerEvents(bool keepBufferingGoing = false); + void cancelPlayerEvents(bool keepNotifications = false); void setAudioSource(sp<MediaSource> source); status_t initAudioDecoder(); diff --git a/media/libstagefright/timedtext/TimedTextParser.cpp b/media/libstagefright/timedtext/TimedTextParser.cpp index 0bada16fb693..caea0a4cc6ed 100644 --- a/media/libstagefright/timedtext/TimedTextParser.cpp +++ b/media/libstagefright/timedtext/TimedTextParser.cpp @@ -128,7 +128,7 @@ status_t TimedTextParser::readNextLine(off64_t *offset, AString *data) { * Subtitle number * Start time --> End time * Text of subtitle (one or more lines) - * Blank line + * Blank lines * * .srt file example: * 1 @@ -143,15 +143,20 @@ status_t TimedTextParser::getNextInSrtFileFormat( off64_t *offset, int64_t *startTimeUs, TextInfo *info) { AString data; status_t err; - if ((err = readNextLine(offset, &data)) != OK) { - return err; - } - // to skip the first line + // To skip blank lines. + do { + if ((err = readNextLine(offset, &data)) != OK) { + return err; + } + data.trim(); + } while(data.empty()); + + // Just ignore the first non-blank line which is subtitle sequence number. + if ((err = readNextLine(offset, &data)) != OK) { return err; } - int hour1, hour2, min1, min2, sec1, sec2, msec1, msec2; // the start time format is: hours:minutes:seconds,milliseconds // 00:00:24,600 --> 00:00:27,800 diff --git a/native/android/native_window.cpp b/native/android/native_window.cpp index d266fc681e3d..6b37a127182f 100644 --- a/native/android/native_window.cpp +++ b/native/android/native_window.cpp @@ -32,14 +32,6 @@ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) { return win.get(); } -ANativeWindow* ANativeWindow_fromSurfaceTexture(JNIEnv* env, jobject surfaceTexture) { - sp<ANativeWindow> win = android_SurfaceTexture_getNativeWindow(env, surfaceTexture); - if (win != NULL) { - win->incStrong((void*)ANativeWindow_acquire); - } - return win.get(); -} - void ANativeWindow_acquire(ANativeWindow* window) { window->incStrong((void*)ANativeWindow_acquire); } diff --git a/native/include/android/native_window_jni.h b/native/include/android/native_window_jni.h index 408c263b96e4..b9e72efb7681 100644 --- a/native/include/android/native_window_jni.h +++ b/native/include/android/native_window_jni.h @@ -33,14 +33,6 @@ extern "C" { */ ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface); -/** - * Return the ANativeWindow associated with a Java SurfaceTexture object, - * for interacting with it through native code. This acquires a reference - * on the ANativeWindow that is returned; be sure to use ANativeWindow_release() - * when done with it so that it doesn't leak. - */ -ANativeWindow* ANativeWindow_fromSurfaceTexture(JNIEnv* env, jobject surfaceTexture); - #ifdef __cplusplus }; #endif diff --git a/opengl/include/EGL/eglext.h b/opengl/include/EGL/eglext.h index c9266709ffa8..ca1186337829 100644 --- a/opengl/include/EGL/eglext.h +++ b/opengl/include/EGL/eglext.h @@ -229,14 +229,6 @@ struct ANativeWindowBuffer; #define EGL_NATIVE_BUFFER_ANDROID 0x3140 /* eglCreateImageKHR target */ #endif -#ifndef EGL_ANDROID_swap_rectangle -#define EGL_ANDROID_swap_rectangle 1 -#ifdef EGL_EGLEXT_PROTOTYPES -EGLAPI EGLBoolean EGLAPIENTRY eglSetSwapRectangleANDROID (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height); -#endif /* EGL_EGLEXT_PROTOTYPES */ -typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSWAPRECTANGLEANDROIDPROC) (EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height); -#endif - #ifndef EGL_ANDROID_recordable #define EGL_ANDROID_recordable 1 #define EGL_RECORDABLE_ANDROID 0x3142 /* EGLConfig attribute */ diff --git a/opengl/java/android/opengl/GLES11Ext.java b/opengl/java/android/opengl/GLES11Ext.java index 25d54674ca03..484439a9c143 100644 --- a/opengl/java/android/opengl/GLES11Ext.java +++ b/opengl/java/android/opengl/GLES11Ext.java @@ -125,6 +125,10 @@ public class GLES11Ext { public static final int GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; public static final int GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; public static final int GL_BGRA = 0x80E1; + public static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; + public static final int GL_SAMPLER_EXTERNAL_OES = 0x8D66; + public static final int GL_TEXTURE_BINDING_EXTERNAL_OES = 0x8D67; + public static final int GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES = 0x8D68; native private static void _nativeClassInit(); static { diff --git a/opengl/java/android/opengl/GLSurfaceView.java b/opengl/java/android/opengl/GLSurfaceView.java index 3b077ef07257..8e2294c4abe3 100644 --- a/opengl/java/android/opengl/GLSurfaceView.java +++ b/opengl/java/android/opengl/GLSurfaceView.java @@ -17,6 +17,7 @@ package android.opengl; import java.io.Writer; +import java.lang.ref.WeakReference; import java.util.ArrayList; import javax.microedition.khronos.egl.EGL10; @@ -222,6 +223,19 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback init(); } + @Override + protected void finalize() throws Throwable { + try { + if (mGLThread != null) { + // GLThread may still be running if this view was never + // attached to a window. + mGLThread.requestExitAndWait(); + } + } finally { + super.finalize(); + } + } + private void init() { // Install a SurfaceHolder.Callback so we get notified when the // underlying surface is created and destroyed @@ -341,7 +355,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback mEGLWindowSurfaceFactory = new DefaultWindowSurfaceFactory(); } mRenderer = renderer; - mGLThread = new GLThread(renderer); + mGLThread = new GLThread(mThisWeakRef); mGLThread.start(); } @@ -572,7 +586,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if (mGLThread != null) { renderMode = mGLThread.getRenderMode(); } - mGLThread = new GLThread(mRenderer); + mGLThread = new GLThread(mThisWeakRef); if (renderMode != RENDERMODE_CONTINUOUSLY) { mGLThread.setRenderMode(renderMode); } @@ -969,9 +983,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * An EGL helper class. */ - private class EglHelper { - public EglHelper() { - + private static class EglHelper { + public EglHelper(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) { + mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; } /** @@ -1003,13 +1017,19 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if(!mEgl.eglInitialize(mEglDisplay, version)) { throw new RuntimeException("eglInitialize failed"); } - mEglConfig = mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view == null) { + mEglConfig = null; + mEglContext = null; + } else { + mEglConfig = view.mEGLConfigChooser.chooseConfig(mEgl, mEglDisplay); - /* - * Create an EGL context. We want to do this as rarely as we can, because an - * EGL context is a somewhat heavy object. - */ - mEglContext = mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); + /* + * Create an EGL context. We want to do this as rarely as we can, because an + * EGL context is a somewhat heavy object. + */ + mEglContext = view.mEGLContextFactory.createContext(mEgl, mEglDisplay, mEglConfig); + } if (mEglContext == null || mEglContext == EGL10.EGL_NO_CONTEXT) { mEglContext = null; throwEglException("createContext"); @@ -1027,7 +1047,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * * @return true if the surface was created successfully. */ - public boolean createSurface(SurfaceHolder holder) { + public boolean createSurface() { if (LOG_EGL) { Log.w("EglHelper", "createSurface() tid=" + Thread.currentThread().getId()); } @@ -1043,26 +1063,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if (mEglConfig == null) { throw new RuntimeException("mEglConfig not initialized"); } + /* * The window size has changed, so we need to create a new * surface. */ - if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { - - /* - * Unbind and destroy the old EGL surface, if - * there is one. - */ - mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, - EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); - } + destroySurfaceImp(); /* * Create an EGL surface we can render into. */ - mEglSurface = mEGLWindowSurfaceFactory.createWindowSurface(mEgl, - mEglDisplay, mEglConfig, holder); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + mEglSurface = view.mEGLWindowSurfaceFactory.createWindowSurface(mEgl, + mEglDisplay, mEglConfig, view.getHolder()); + } else { + mEglSurface = null; + } if (mEglSurface == null || mEglSurface == EGL10.EGL_NO_SURFACE) { int error = mEgl.eglGetError(); @@ -1090,20 +1107,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback GL createGL() { GL gl = mEglContext.getGL(); - if (mGLWrapper != null) { - gl = mGLWrapper.wrap(gl); - } - - if ((mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { - int configFlags = 0; - Writer log = null; - if ((mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { - configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + if (view.mGLWrapper != null) { + gl = view.mGLWrapper.wrap(gl); } - if ((mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { - log = new LogWriter(); + + if ((view.mDebugFlags & (DEBUG_CHECK_GL_ERROR | DEBUG_LOG_GL_CALLS)) != 0) { + int configFlags = 0; + Writer log = null; + if ((view.mDebugFlags & DEBUG_CHECK_GL_ERROR) != 0) { + configFlags |= GLDebugHelper.CONFIG_CHECK_GL_ERROR; + } + if ((view.mDebugFlags & DEBUG_LOG_GL_CALLS) != 0) { + log = new LogWriter(); + } + gl = GLDebugHelper.wrap(gl, configFlags, log); } - gl = GLDebugHelper.wrap(gl, configFlags, log); } return gl; } @@ -1142,11 +1162,18 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if (LOG_EGL) { Log.w("EglHelper", "destroySurface() tid=" + Thread.currentThread().getId()); } + destroySurfaceImp(); + } + + private void destroySurfaceImp() { if (mEglSurface != null && mEglSurface != EGL10.EGL_NO_SURFACE) { mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT); - mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mEGLWindowSurfaceFactory.destroySurface(mEgl, mEglDisplay, mEglSurface); + } mEglSurface = null; } } @@ -1156,7 +1183,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback Log.w("EglHelper", "finish() tid=" + Thread.currentThread().getId()); } if (mEglContext != null) { - mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mEGLContextFactory.destroyContext(mEgl, mEglDisplay, mEglContext); + } mEglContext = null; } if (mEglDisplay != null) { @@ -1177,6 +1207,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback throw new RuntimeException(message); } + private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef; EGL10 mEgl; EGLDisplay mEglDisplay; EGLSurface mEglSurface; @@ -1194,14 +1225,14 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * sGLThreadManager object. This avoids multiple-lock ordering issues. * */ - class GLThread extends Thread { - GLThread(Renderer renderer) { + static class GLThread extends Thread { + GLThread(WeakReference<GLSurfaceView> glSurfaceViewWeakRef) { super(); mWidth = 0; mHeight = 0; mRequestRender = true; mRenderMode = RENDERMODE_CONTINUOUSLY; - mRenderer = renderer; + mGLSurfaceViewWeakRef = glSurfaceViewWeakRef; } @Override @@ -1243,7 +1274,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } } private void guardedRun() throws InterruptedException { - mEglHelper = new EglHelper(); + mEglHelper = new EglHelper(mGLSurfaceViewWeakRef); mHaveEglContext = false; mHaveEglSurface = false; try { @@ -1305,7 +1336,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback Log.i("GLThread", "releasing EGL surface because paused tid=" + getId()); } stopEglSurfaceLocked(); - if (!mPreserveEGLContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) { + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + boolean preserveEglContextOnPause = view == null ? + false : view.mPreserveEGLContextOnPause; + if (!preserveEglContextOnPause || sGLThreadManager.shouldReleaseEGLContextWhenPausing()) { stopEglContextLocked(); if (LOG_SURFACE) { Log.i("GLThread", "releasing EGL context because paused tid=" + getId()); @@ -1426,7 +1460,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if (LOG_SURFACE) { Log.w("GLThread", "egl createSurface"); } - if (!mEglHelper.createSurface(getHolder())) { + if (!mEglHelper.createSurface()) { // Couldn't create a surface. Quit quietly. break; } @@ -1444,7 +1478,10 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if (LOG_RENDERER) { Log.w("GLThread", "onSurfaceCreated"); } - mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mRenderer.onSurfaceCreated(gl, mEglHelper.mEglConfig); + } createEglContext = false; } @@ -1452,14 +1489,22 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback if (LOG_RENDERER) { Log.w("GLThread", "onSurfaceChanged(" + w + ", " + h + ")"); } - mRenderer.onSurfaceChanged(gl, w, h); + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mRenderer.onSurfaceChanged(gl, w, h); + } sizeChanged = false; } if (LOG_RENDERER_DRAW_FRAME) { Log.w("GLThread", "onDrawFrame tid=" + getId()); } - mRenderer.onDrawFrame(gl); + { + GLSurfaceView view = mGLSurfaceViewWeakRef.get(); + if (view != null) { + view.mRenderer.onDrawFrame(gl); + } + } if (!mEglHelper.swap()) { if (LOG_SURFACE) { Log.i("GLThread", "egl context lost tid=" + getId()); @@ -1603,9 +1648,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback // Wait for thread to react to resize and render a frame while (! mExited && !mPaused && !mRenderComplete - && (mGLThread != null && mGLThread.ableToDraw())) { + && ableToDraw()) { if (LOG_SURFACE) { - Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + mGLThread.getId()); + Log.i("Main thread", "onWindowResize waiting for render complete from tid=" + getId()); } try { sGLThreadManager.wait(); @@ -1668,11 +1713,19 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback private boolean mRequestRender; private boolean mRenderComplete; private ArrayList<Runnable> mEventQueue = new ArrayList<Runnable>(); + private boolean mSizeChanged = true; // End of member variables protected by the sGLThreadManager monitor. - private Renderer mRenderer; private EglHelper mEglHelper; + + /** + * Set once at thread construction time, nulled out when the parent view is garbage + * called. This weak reference allows the GLSurfaceView to be garbage collected while + * the GLThread is still alive. + */ + private WeakReference<GLSurfaceView> mGLSurfaceViewWeakRef; + } static class LogWriter extends Writer { @@ -1827,8 +1880,9 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } private static final GLThreadManager sGLThreadManager = new GLThreadManager(); - private boolean mSizeChanged = true; + private final WeakReference<GLSurfaceView> mThisWeakRef = + new WeakReference<GLSurfaceView>(this); private GLThread mGLThread; private Renderer mRenderer; private boolean mDetached; diff --git a/opengl/java/android/opengl/ManagedEGLContext.java b/opengl/java/android/opengl/ManagedEGLContext.java new file mode 100644 index 000000000000..61fa565a049c --- /dev/null +++ b/opengl/java/android/opengl/ManagedEGLContext.java @@ -0,0 +1,137 @@ +/* + * Copyright (C) 2010 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.opengl; + +import static javax.microedition.khronos.egl.EGL10.EGL_DEFAULT_DISPLAY; +import static javax.microedition.khronos.egl.EGL10.EGL_NO_DISPLAY; + +import java.util.ArrayList; + +import javax.microedition.khronos.egl.EGL10; +import javax.microedition.khronos.egl.EGLContext; +import javax.microedition.khronos.egl.EGLDisplay; + +import android.os.Looper; +import android.util.Log; + +import com.google.android.gles_jni.EGLImpl; + +/** + * The per-process memory overhead of hardware accelerated graphics can + * be quite large on some devices. For small memory devices, being able to + * terminate all EGL contexts so that this graphics driver memory can be + * reclaimed can significant improve the overall behavior of the device. This + * class helps app developers participate in releasing their EGL context + * when appropriate and possible. + * + * <p>To use, simple instantiate this class with the EGLContext you create. + * When you have done this, if the device is getting low on memory and all + * 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>(); + + final EGLContext mContext; + + /** + * Instantiate to manage the given EGLContext. + */ + public ManagedEGLContext(EGLContext context) { + mContext = context; + synchronized (sActive) { + sActive.add(this); + } + } + + /** + * Retrieve the EGLContext being managed by the class. + */ + public EGLContext getContext() { + return mContext; + } + + /** + * Force-terminate the ManagedEGLContext. This will cause + * {@link #onTerminate(EGLContext)} to be called. You <em>must</em> + * call this when destroying the EGLContext, so that the framework + * knows to stop managing it. + */ + public void terminate() { + execTerminate(); + } + + void execTerminate() { + onTerminate(mContext); + } + + /** + * Override this method to destroy the EGLContext when appropriate. + * <em>Note that this method is always called on the main thread + * of the process.</em> If your EGLContext was created on a different + * thread, you will need to implement this method to hand off the work + * of destroying the context to that thread. + */ + public abstract void onTerminate(EGLContext context); + + /** @hide */ + public static boolean doTerminate() { + ArrayList<ManagedEGLContext> active; + + if (Looper.getMainLooper() != Looper.myLooper()) { + throw new IllegalStateException("Called on wrong thread"); + } + + synchronized (sActive) { + // If there are no active managed contexts, we will not even + // try to terminate. + if (sActive.size() <= 0) { + return false; + } + + // Need to check how many EGL contexts are actually running, + // to compare with how many we are managing. + EGL10 egl = (EGL10) EGLContext.getEGL(); + EGLDisplay display = egl.eglGetDisplay(EGL_DEFAULT_DISPLAY); + + if (display == EGL_NO_DISPLAY) { + Log.w(TAG, "doTerminate failed: no display"); + return false; + } + + if (EGLImpl.getInitCount(display) != sActive.size()) { + Log.w(TAG, "doTerminate failed: EGL count is " + EGLImpl.getInitCount(display) + + " but managed count is " + sActive.size()); + return false; + } + + active = new ArrayList<ManagedEGLContext>(sActive); + sActive.clear(); + } + + for (int i = 0; i < active.size(); i++) { + active.get(i).execTerminate(); + } + + return true; + } +} diff --git a/opengl/libagl/egl.cpp b/opengl/libagl/egl.cpp index 03db8d777aa2..6d4098c72dc9 100644 --- a/opengl/libagl/egl.cpp +++ b/opengl/libagl/egl.cpp @@ -49,6 +49,11 @@ #undef NELEM #define NELEM(x) (sizeof(x)/sizeof(*(x))) + +EGLBoolean EGLAPI eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, + EGLint left, EGLint top, EGLint width, EGLint height); + + // ---------------------------------------------------------------------------- namespace android { // ---------------------------------------------------------------------------- diff --git a/opengl/libagl2/Android.mk b/opengl/libagl2/Android.mk deleted file mode 100644 index b442a2d4a54f..000000000000 --- a/opengl/libagl2/Android.mk +++ /dev/null @@ -1,56 +0,0 @@ -LOCAL_PATH:= $(call my-dir) - -# -# Build the software OpenGL ES library -# - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= \ - src/api.cpp \ - src/egl.cpp \ - src/get.cpp \ - src/shader.cpp \ - src/state.cpp \ - src/texture.cpp \ - src/vertex.cpp - -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH) \ - external/mesa3d/include \ - external/mesa3d/src \ - external/stlport/stlport \ - bionic - -#LOCAL_CFLAGS += -DLOG_TAG=\"libagl2\" -#LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES -#LOCAL_CFLAGS += -fvisibility=hidden -#LOCAL_CFLAGS += -O0 -g -DDEBUG -UNDEBUG -LOCAL_CFLAGS += -O3 -LOCAL_STATIC_LIBRARIES := libMesa -LOCAL_SHARED_LIBRARIES := libstlport libcutils libhardware libutils libbcc libdl -LOCAL_LDLIBS := -lpthread - -ifeq ($(TARGET_ARCH),arm) - LOCAL_CFLAGS += -fstrict-aliasing -endif - -ifeq ($(ARCH_ARM_HAVE_TLS_REGISTER),true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER -endif - -# we need to access the private Bionic header <bionic_tls.h> -# on ARM platforms, we need to mirror the ARCH_ARM_HAVE_TLS_REGISTER -# behavior from the bionic Android.mk file -ifeq ($(TARGET_ARCH)-$(ARCH_ARM_HAVE_TLS_REGISTER),arm-true) - LOCAL_CFLAGS += -DHAVE_ARM_TLS_REGISTER -endif -LOCAL_C_INCLUDES += bionic/libc/private - -LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/egl -#replace libagl for now -LOCAL_MODULE:= libGLES_android -LOCAL_MODULE_TAGS := eng - -## Disable this makefile for now -## include $(BUILD_SHARED_LIBRARY) diff --git a/opengl/libagl2/README b/opengl/libagl2/README deleted file mode 100644 index 34746d3f42b1..000000000000 --- a/opengl/libagl2/README +++ /dev/null @@ -1,26 +0,0 @@ -libAgl2 provides software GL ES 2.0 implementation using Pixelflinger2 in external/mesa3d - -To build, enable Android.mk, which builds libGLES_android.so, then replace the one built from libAgl in system/lib/egl. -ES 1.0 functions are not implemented and will cause exit, so do not setprop debug.egl.hw 0 until launcher is loaded. - -All functions have little to none error checking. -Not thread safe, Pixelflinger2 uses some static data. - -Most shader functions are implemented, however, most Get* functions for shaders/programs/uniforms/attribs are not. -No name system for shaders/programs, just using the pointers as names. - -Basic glTexImage2D, glTexSubImage2D, glCopyImage2D and glCopySubImage2D are implemented, with a range of 8/16/24/32bpp formats. -Cube map support is minimal. No mipmapping. -TexParameter is mostly implemented, supports texcoord wrap modes, and only linear for both min and mag, or nearest for both min and mag filtering. -Texture names are implemented, but bad. - -Frame buffer and render buffers are not implemented. - -Depth and stencil are implemented, but not tested. -Blending seems to work. -Colorbuffer supports RGBA_8888 and RGB_565. - -Vertex buffer objects are implemented. -Some GL_TRIANGLES and GL_TRIANGLE_STRIPS modes for glDrawArrays and glDrawElements are implemented, but vertex order is probably wrong so culling is disabled. - -Basic apps should work, and some libhwui should work, except for frame buffer operations, which will cause exit. diff --git a/opengl/libagl2/libagl2.project b/opengl/libagl2/libagl2.project deleted file mode 100644 index f23442142a10..000000000000 --- a/opengl/libagl2/libagl2.project +++ /dev/null @@ -1,108 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<CodeLite_Project Name="libagl2" InternalType="Console"> - <Plugins> - <Plugin Name="qmake"> - <![CDATA[00010001N0005Debug000000000000]]> - </Plugin> - </Plugins> - <Description/> - <Dependencies/> - <Dependencies Name="Release"/> - <VirtualDirectory Name="src"> - <File Name="src/egl.cpp"/> - <File Name="src/api.cpp"/> - <File Name="src/gles2context.h"/> - <File Name="src/shader.cpp"/> - <File Name="src/vertex.cpp"/> - <File Name="src/state.cpp"/> - <File Name="src/texture.cpp"/> - <File Name="src/get.cpp"/> - </VirtualDirectory> - <VirtualDirectory Name="include"/> - <Settings Type="Executable"> - <Configuration Name="Debug" CompilerType="gnu gcc" DebuggerType="GNU gdb debugger" Type="Executable" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append"> - <Compiler Options="-g;-m32" Required="yes" PreCompiledHeader=""> - <IncludePath Value="/usr/include/c++/4.4"/> - <IncludePath Value="/usr/include/c++/4.4/ext"/> - <IncludePath Value="."/> - <IncludePath Value="include"/> - <IncludePath Value="../../../../external/mesa3d/include"/> - <IncludePath Value="../../../../external/mesa3d/src"/> - <IncludePath Value="../../../../hardware/libhardware/include"/> - <IncludePath Value="../../../../system/core/include"/> - <IncludePath Value="../include"/> - <IncludePath Value="../../include"/> - <IncludePath Value="../../../../development/ndk/platforms/android-9/include"/> - <IncludePath Value="../../../../bionic/libc/include/"/> - <IncludePath Value="/../../../../development/ndk/platforms/android-5/arch-x86/include"/> - <IncludePath Value="../../../../bionic/libc/arch-x86/include"/> - <IncludePath Value="../../../../bionic/libc/kernel/arch-x86"/> - <IncludePath Value="/../../../../external/kernel-headers/original"/> - <IncludePath Value="../../../../prebuilt/ndk/android-ndk-r4/platforms/android-8/arch-x86/usr/include"/> - </Compiler> - <Linker Options="-m32;-lstdc++" Required="yes"/> - <ResourceCompiler Options="" Required="no"/> - <General OutputFile="$(IntermediateDirectory)/$(ProjectName)" IntermediateDirectory="./Debug" Command="./$(ProjectName)" CommandArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes"/> - <Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath=""> - <PostConnectCommands/> - <StartupCommands/> - </Debugger> - <PreBuild/> - <PostBuild/> - <CustomBuild Enabled="no"> - <RebuildCommand/> - <CleanCommand/> - <BuildCommand/> - <PreprocessFileCommand/> - <SingleFileCommand/> - <MakefileGenerationCommand/> - <ThirdPartyToolName>None</ThirdPartyToolName> - <WorkingDirectory/> - </CustomBuild> - <AdditionalRules> - <CustomPostBuild/> - <CustomPreBuild/> - </AdditionalRules> - </Configuration> - <Configuration Name="Release" CompilerType="gnu gcc" DebuggerType="GNU gdb debugger" Type="" BuildCmpWithGlobalSettings="append" BuildLnkWithGlobalSettings="append" BuildResWithGlobalSettings="append"> - <Compiler Options="" Required="yes" PreCompiledHeader=""> - <IncludePath Value="."/> - </Compiler> - <Linker Options="-O2" Required="yes"/> - <ResourceCompiler Options="" Required="no"/> - <General OutputFile="$(IntermediateDirectory)/$(ProjectName)" IntermediateDirectory="./Release" Command="./$(ProjectName)" CommandArguments="" WorkingDirectory="$(IntermediateDirectory)" PauseExecWhenProcTerminates="yes"/> - <Debugger IsRemote="no" RemoteHostName="" RemoteHostPort="" DebuggerPath=""> - <PostConnectCommands/> - <StartupCommands/> - </Debugger> - <PreBuild/> - <PostBuild/> - <CustomBuild Enabled="no"> - <RebuildCommand/> - <CleanCommand/> - <BuildCommand/> - <PreprocessFileCommand/> - <SingleFileCommand/> - <MakefileGenerationCommand/> - <ThirdPartyToolName>None</ThirdPartyToolName> - <WorkingDirectory/> - </CustomBuild> - <AdditionalRules> - <CustomPostBuild/> - <CustomPreBuild/> - </AdditionalRules> - </Configuration> - <GlobalSettings> - <Compiler Options=""> - <IncludePath Value="."/> - </Compiler> - <Linker Options=""> - <LibraryPath Value="."/> - </Linker> - <ResourceCompiler Options=""/> - </GlobalSettings> - </Settings> - <Dependencies Name="Debug"> - <Project Name="libMesa"/> - </Dependencies> -</CodeLite_Project> diff --git a/opengl/libagl2/src/api.cpp b/opengl/libagl2/src/api.cpp deleted file mode 100644 index bb8d62b38756..000000000000 --- a/opengl/libagl2/src/api.cpp +++ /dev/null @@ -1,266 +0,0 @@ -#include "gles2context.h" - -#define API_ENTRY -#define CALL_GL_API(NAME,...) LOGD("?"#NAME); assert(0); -#define CALL_GL_API_RETURN(NAME,...) LOGD("?"#NAME); assert(0); return 0; - - -void API_ENTRY(glBindFramebuffer)(GLenum target, GLuint framebuffer) -{ - CALL_GL_API(glBindFramebuffer, target, framebuffer); -} -void API_ENTRY(glBindRenderbuffer)(GLenum target, GLuint renderbuffer) -{ - CALL_GL_API(glBindRenderbuffer, target, renderbuffer); -} -GLenum API_ENTRY(glCheckFramebufferStatus)(GLenum target) -{ - CALL_GL_API_RETURN(glCheckFramebufferStatus, target); -} -void API_ENTRY(glColorMask)(GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha) -{ - CALL_GL_API(glColorMask, red, green, blue, alpha); -} -void API_ENTRY(glDeleteFramebuffers)(GLsizei n, const GLuint* framebuffers) -{ - CALL_GL_API(glDeleteFramebuffers, n, framebuffers); -} -void API_ENTRY(glDeleteRenderbuffers)(GLsizei n, const GLuint* renderbuffers) -{ - CALL_GL_API(glDeleteRenderbuffers, n, renderbuffers); -} -void API_ENTRY(glDepthFunc)(GLenum func) -{ - CALL_GL_API(glDepthFunc, func); -} -void API_ENTRY(glDepthMask)(GLboolean flag) -{ - CALL_GL_API(glDepthMask, flag); -} -void API_ENTRY(glDepthRangef)(GLclampf zNear, GLclampf zFar) -{ - CALL_GL_API(glDepthRangef, zNear, zFar); -} -void API_ENTRY(glFramebufferRenderbuffer)(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer) -{ - CALL_GL_API(glFramebufferRenderbuffer, target, attachment, renderbuffertarget, renderbuffer); -} -void API_ENTRY(glFramebufferTexture2D)(GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level) -{ - CALL_GL_API(glFramebufferTexture2D, target, attachment, textarget, texture, level); -} -void glGenerateMipmap(GLenum target) -{ - //CALL_GL_API(glGenerateMipmap, target); - LOGD("agl2: glGenerateMipmap not implemented"); -} -void API_ENTRY(glGenFramebuffers)(GLsizei n, GLuint* framebuffers) -{ - CALL_GL_API(glGenFramebuffers, n, framebuffers); -} -void API_ENTRY(glGenRenderbuffers)(GLsizei n, GLuint* renderbuffers) -{ - CALL_GL_API(glGenRenderbuffers, n, renderbuffers); -} -void API_ENTRY(glGetActiveAttrib)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) -{ - CALL_GL_API(glGetActiveAttrib, program, index, bufsize, length, size, type, name); -} -void API_ENTRY(glGetActiveUniform)(GLuint program, GLuint index, GLsizei bufsize, GLsizei* length, GLint* size, GLenum* type, GLchar* name) -{ - CALL_GL_API(glGetActiveUniform, program, index, bufsize, length, size, type, name); -} -void API_ENTRY(glGetAttachedShaders)(GLuint program, GLsizei maxcount, GLsizei* count, GLuint* shaders) -{ - CALL_GL_API(glGetAttachedShaders, program, maxcount, count, shaders); -} -void API_ENTRY(glGetBooleanv)(GLenum pname, GLboolean* params) -{ - CALL_GL_API(glGetBooleanv, pname, params); -} -void API_ENTRY(glGetBufferParameteriv)(GLenum target, GLenum pname, GLint* params) -{ - CALL_GL_API(glGetBufferParameteriv, target, pname, params); -} -GLenum glGetError(void) -{ - puts("agl2: glGetError"); - return GL_NO_ERROR; - //CALL_GL_API_RETURN(glGetError); -} -void API_ENTRY(glGetFloatv)(GLenum pname, GLfloat* params) -{ - CALL_GL_API(glGetFloatv, pname, params); -} -void API_ENTRY(glGetFramebufferAttachmentParameteriv)(GLenum target, GLenum attachment, GLenum pname, GLint* params) -{ - CALL_GL_API(glGetFramebufferAttachmentParameteriv, target, attachment, pname, params); -} -void API_ENTRY(glGetRenderbufferParameteriv)(GLenum target, GLenum pname, GLint* params) -{ - CALL_GL_API(glGetRenderbufferParameteriv, target, pname, params); -} -void API_ENTRY(glGetShaderPrecisionFormat)(GLenum shadertype, GLenum precisiontype, GLint* range, GLint* precision) -{ - CALL_GL_API(glGetShaderPrecisionFormat, shadertype, precisiontype, range, precision); -} -void API_ENTRY(glGetShaderSource)(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* source) -{ - CALL_GL_API(glGetShaderSource, shader, bufsize, length, source); -} -void API_ENTRY(glGetUniformfv)(GLuint program, GLint location, GLfloat* params) -{ - CALL_GL_API(glGetUniformfv, program, location, params); -} -void API_ENTRY(glGetUniformiv)(GLuint program, GLint location, GLint* params) -{ - CALL_GL_API(glGetUniformiv, program, location, params); -} -void API_ENTRY(glGetVertexAttribfv)(GLuint index, GLenum pname, GLfloat* params) -{ - CALL_GL_API(glGetVertexAttribfv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribiv)(GLuint index, GLenum pname, GLint* params) -{ - CALL_GL_API(glGetVertexAttribiv, index, pname, params); -} -void API_ENTRY(glGetVertexAttribPointerv)(GLuint index, GLenum pname, GLvoid** pointer) -{ - CALL_GL_API(glGetVertexAttribPointerv, index, pname, pointer); -} -GLboolean API_ENTRY(glIsBuffer)(GLuint buffer) -{ - CALL_GL_API_RETURN(glIsBuffer, buffer); -} -GLboolean API_ENTRY(glIsEnabled)(GLenum cap) -{ - CALL_GL_API_RETURN(glIsEnabled, cap); -} -GLboolean API_ENTRY(glIsFramebuffer)(GLuint framebuffer) -{ - CALL_GL_API_RETURN(glIsFramebuffer, framebuffer); -} -GLboolean API_ENTRY(glIsProgram)(GLuint program) -{ - CALL_GL_API_RETURN(glIsProgram, program); -} -GLboolean API_ENTRY(glIsRenderbuffer)(GLuint renderbuffer) -{ - CALL_GL_API_RETURN(glIsRenderbuffer, renderbuffer); -} -GLboolean API_ENTRY(glIsShader)(GLuint shader) -{ - CALL_GL_API_RETURN(glIsShader, shader); -} -void API_ENTRY(glLineWidth)(GLfloat width) -{ - CALL_GL_API(glLineWidth, width); -} -void API_ENTRY(glPolygonOffset)(GLfloat factor, GLfloat units) -{ - CALL_GL_API(glPolygonOffset, factor, units); -} -void API_ENTRY(glReadPixels)(GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels) -{ - CALL_GL_API(glReadPixels, x, y, width, height, format, type, pixels); -} -void API_ENTRY(glReleaseShaderCompiler)(void) -{ - CALL_GL_API(glReleaseShaderCompiler); -} -void API_ENTRY(glRenderbufferStorage)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height) -{ - CALL_GL_API(glRenderbufferStorage, target, internalformat, width, height); -} -void API_ENTRY(glSampleCoverage)(GLclampf value, GLboolean invert) -{ - CALL_GL_API(glSampleCoverage, value, invert); -} -void API_ENTRY(glShaderBinary)(GLsizei n, const GLuint* shaders, GLenum binaryformat, const GLvoid* binary, GLsizei length) -{ - CALL_GL_API(glShaderBinary, n, shaders, binaryformat, binary, length); -} -void API_ENTRY(glStencilFunc)(GLenum func, GLint ref, GLuint mask) -{ - CALL_GL_API(glStencilFunc, func, ref, mask); -} -void API_ENTRY(glStencilFuncSeparate)(GLenum face, GLenum func, GLint ref, GLuint mask) -{ - CALL_GL_API(glStencilFuncSeparate, face, func, ref, mask); -} -void API_ENTRY(glStencilMask)(GLuint mask) -{ - CALL_GL_API(glStencilMask, mask); -} -void API_ENTRY(glStencilMaskSeparate)(GLenum face, GLuint mask) -{ - CALL_GL_API(glStencilMaskSeparate, face, mask); -} -void API_ENTRY(glStencilOp)(GLenum fail, GLenum zfail, GLenum zpass) -{ - CALL_GL_API(glStencilOp, fail, zfail, zpass); -} -void API_ENTRY(glStencilOpSeparate)(GLenum face, GLenum fail, GLenum zfail, GLenum zpass) -{ - CALL_GL_API(glStencilOpSeparate, face, fail, zfail, zpass); -} -void API_ENTRY(glUniform1fv)(GLint location, GLsizei count, const GLfloat* v) -{ - CALL_GL_API(glUniform1fv, location, count, v); -} -void API_ENTRY(glUniform1iv)(GLint location, GLsizei count, const GLint* v) -{ - CALL_GL_API(glUniform1iv, location, count, v); -} -void API_ENTRY(glUniform2fv)(GLint location, GLsizei count, const GLfloat* v) -{ - CALL_GL_API(glUniform2fv, location, count, v); -} -void API_ENTRY(glUniform2i)(GLint location, GLint x, GLint y) -{ - CALL_GL_API(glUniform2i, location, x, y); -} -void API_ENTRY(glUniform2iv)(GLint location, GLsizei count, const GLint* v) -{ - CALL_GL_API(glUniform2iv, location, count, v); -} -void API_ENTRY(glUniform3f)(GLint location, GLfloat x, GLfloat y, GLfloat z) -{ - CALL_GL_API(glUniform3f, location, x, y, z); -} -void API_ENTRY(glUniform3fv)(GLint location, GLsizei count, const GLfloat* v) -{ - CALL_GL_API(glUniform3fv, location, count, v); -} -void API_ENTRY(glUniform3i)(GLint location, GLint x, GLint y, GLint z) -{ - CALL_GL_API(glUniform3i, location, x, y, z); -} -void API_ENTRY(glUniform3iv)(GLint location, GLsizei count, const GLint* v) -{ - CALL_GL_API(glUniform3iv, location, count, v); -} -void API_ENTRY(glUniform4fv)(GLint location, GLsizei count, const GLfloat* v) -{ - CALL_GL_API(glUniform4fv, location, count, v); -} -void API_ENTRY(glUniform4i)(GLint location, GLint x, GLint y, GLint z, GLint w) -{ - CALL_GL_API(glUniform4i, location, x, y, z, w); -} -void API_ENTRY(glUniform4iv)(GLint location, GLsizei count, const GLint* v) -{ - CALL_GL_API(glUniform4iv, location, count, v); -} -void API_ENTRY(glUniformMatrix2fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) -{ - CALL_GL_API(glUniformMatrix2fv, location, count, transpose, value); -} -void API_ENTRY(glUniformMatrix3fv)(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) -{ - CALL_GL_API(glUniformMatrix3fv, location, count, transpose, value); -} -void API_ENTRY(glValidateProgram)(GLuint program) -{ - CALL_GL_API(glValidateProgram, program); -} diff --git a/opengl/libagl2/src/egl.cpp b/opengl/libagl2/src/egl.cpp deleted file mode 100644 index 0d02ce66bbc5..000000000000 --- a/opengl/libagl2/src/egl.cpp +++ /dev/null @@ -1,2172 +0,0 @@ -/* - ** - ** Copyright 2007 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. - */ - -#include <errno.h> -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <fcntl.h> -#include <sys/ioctl.h> -#include <sys/types.h> -#include <sys/mman.h> - -#include <cutils/atomic.h> - - -#include <private/ui/android_natives_priv.h> - -#include "gles2context.h" - -// ---------------------------------------------------------------------------- -namespace android -{ -// ---------------------------------------------------------------------------- - -const unsigned int NUM_DISPLAYS = 1; - -static pthread_mutex_t gInitMutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_mutex_t gErrorKeyMutex = PTHREAD_MUTEX_INITIALIZER; -static pthread_key_t gEGLErrorKey = -1; -#ifndef HAVE_ANDROID_OS -namespace gl { -pthread_key_t gGLKey = -1; -}; // namespace gl -#endif - -template<typename T> -static T setError(GLint error, T returnValue) -{ - if (ggl_unlikely(gEGLErrorKey == -1)) { - pthread_mutex_lock(&gErrorKeyMutex); - if (gEGLErrorKey == -1) - pthread_key_create(&gEGLErrorKey, NULL); - pthread_mutex_unlock(&gErrorKeyMutex); - } - pthread_setspecific(gEGLErrorKey, (void*)error); - return returnValue; -} - -static GLint getError() -{ - if (ggl_unlikely(gEGLErrorKey == -1)) - return EGL_SUCCESS; - GLint error = (GLint)pthread_getspecific(gEGLErrorKey); - if (error == 0) { - // The TLS key has been created by another thread, but the value for - // this thread has not been initialized. - return EGL_SUCCESS; - } - pthread_setspecific(gEGLErrorKey, (void*)EGL_SUCCESS); - return error; -} - -// ---------------------------------------------------------------------------- - -struct egl_display_t { - egl_display_t() : type(0), initialized(0) { } - - static egl_display_t& get_display(EGLDisplay dpy); - - static EGLBoolean is_valid(EGLDisplay dpy) { - return ((uintptr_t(dpy)-1U) >= NUM_DISPLAYS) ? EGL_FALSE : EGL_TRUE; - } - - NativeDisplayType type; - volatile int32_t initialized; -}; - -static egl_display_t gDisplays[NUM_DISPLAYS]; - -egl_display_t& egl_display_t::get_display(EGLDisplay dpy) -{ - return gDisplays[uintptr_t(dpy)-1U]; -} - -// ---------------------------------------------------------------------------- - -struct egl_surface_t { - enum { - PAGE_FLIP = 0x00000001, - MAGIC = 0x31415265 - }; - - uint32_t magic; - EGLDisplay dpy; - EGLConfig config; - EGLContext ctx; - - egl_surface_t(EGLDisplay dpy, EGLConfig config, int32_t depthFormat); - virtual ~egl_surface_t(); - bool isValid() const; - virtual bool initCheck() const = 0; - - virtual EGLBoolean bindDrawSurface(GLES2Context* gl) = 0; - virtual EGLBoolean bindReadSurface(GLES2Context* gl) = 0; - virtual EGLBoolean connect() { - return EGL_TRUE; - } - virtual void disconnect() {} - virtual EGLint getWidth() const = 0; - virtual EGLint getHeight() const = 0; - - virtual EGLint getHorizontalResolution() const; - virtual EGLint getVerticalResolution() const; - virtual EGLint getRefreshRate() const; - virtual EGLint getSwapBehavior() const; - virtual EGLBoolean swapBuffers(); - virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h); -protected: - GGLSurface depth; -}; - -egl_surface_t::egl_surface_t(EGLDisplay dpy, - EGLConfig config, - int32_t depthFormat) -: magic(MAGIC), dpy(dpy), config(config), ctx(0) -{ - depth.version = sizeof(GGLSurface); - depth.data = 0; - depth.format = (GGLPixelFormat)depthFormat; -} -egl_surface_t::~egl_surface_t() -{ - magic = 0; - free(depth.data); -} -bool egl_surface_t::isValid() const -{ - LOGE_IF(magic != MAGIC, "invalid EGLSurface (%p)", this); - return magic == MAGIC; -} - -EGLBoolean egl_surface_t::swapBuffers() -{ - return EGL_FALSE; -} -EGLint egl_surface_t::getHorizontalResolution() const -{ - return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_surface_t::getVerticalResolution() const -{ - return (0 * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_surface_t::getRefreshRate() const -{ - return (60 * EGL_DISPLAY_SCALING); -} -EGLint egl_surface_t::getSwapBehavior() const -{ - return EGL_BUFFER_PRESERVED; -} -EGLBoolean egl_surface_t::setSwapRectangle( - EGLint l, EGLint t, EGLint w, EGLint h) -{ - return EGL_FALSE; -} - -// ---------------------------------------------------------------------------- - -struct egl_window_surface_v2_t : public egl_surface_t { - egl_window_surface_v2_t( - EGLDisplay dpy, EGLConfig config, - int32_t depthFormat, - ANativeWindow* window); - - ~egl_window_surface_v2_t(); - - virtual bool initCheck() const { - return true; // TODO: report failure if ctor fails - } - virtual EGLBoolean swapBuffers(); - virtual EGLBoolean bindDrawSurface(GLES2Context* gl); - virtual EGLBoolean bindReadSurface(GLES2Context* gl); - virtual EGLBoolean connect(); - virtual void disconnect(); - virtual EGLint getWidth() const { - return width; - } - virtual EGLint getHeight() const { - return height; - } - virtual EGLint getHorizontalResolution() const; - virtual EGLint getVerticalResolution() const; - virtual EGLint getRefreshRate() const; - virtual EGLint getSwapBehavior() const; - virtual EGLBoolean setSwapRectangle(EGLint l, EGLint t, EGLint w, EGLint h); - -private: - status_t lock(ANativeWindowBuffer* buf, int usage, void** vaddr); - status_t unlock(ANativeWindowBuffer* buf); - ANativeWindow* nativeWindow; - ANativeWindowBuffer* buffer; - ANativeWindowBuffer* previousBuffer; - gralloc_module_t const* module; - int width; - int height; - void* bits; - GGLFormat const* pixelFormatTable; - - struct Rect { - inline Rect() { }; - inline Rect(int32_t w, int32_t h) - : left(0), top(0), right(w), bottom(h) { } - inline Rect(int32_t l, int32_t t, int32_t r, int32_t b) - : left(l), top(t), right(r), bottom(b) { } - Rect& andSelf(const Rect& r) { - left = max(left, r.left); - top = max(top, r.top); - right = min(right, r.right); - bottom = min(bottom, r.bottom); - return *this; - } - bool isEmpty() const { - return (left>=right || top>=bottom); - } - void dump(char const* what) { - LOGD("%s { %5d, %5d, w=%5d, h=%5d }", - what, left, top, right-left, bottom-top); - } - - int32_t left; - int32_t top; - int32_t right; - int32_t bottom; - }; - - struct Region { - inline Region() : count(0) { } - typedef Rect const* const_iterator; - const_iterator begin() const { - return storage; - } - const_iterator end() const { - return storage+count; - } - static Region subtract(const Rect& lhs, const Rect& rhs) { - Region reg; - Rect* storage = reg.storage; - if (!lhs.isEmpty()) { - if (lhs.top < rhs.top) { // top rect - storage->left = lhs.left; - storage->top = lhs.top; - storage->right = lhs.right; - storage->bottom = rhs.top; - storage++; - } - const int32_t top = max(lhs.top, rhs.top); - const int32_t bot = min(lhs.bottom, rhs.bottom); - if (top < bot) { - if (lhs.left < rhs.left) { // left-side rect - storage->left = lhs.left; - storage->top = top; - storage->right = rhs.left; - storage->bottom = bot; - storage++; - } - if (lhs.right > rhs.right) { // right-side rect - storage->left = rhs.right; - storage->top = top; - storage->right = lhs.right; - storage->bottom = bot; - storage++; - } - } - if (lhs.bottom > rhs.bottom) { // bottom rect - storage->left = lhs.left; - storage->top = rhs.bottom; - storage->right = lhs.right; - storage->bottom = lhs.bottom; - storage++; - } - reg.count = storage - reg.storage; - } - return reg; - } - bool isEmpty() const { - return count<=0; - } - private: - Rect storage[4]; - ssize_t count; - }; - - void copyBlt( - ANativeWindowBuffer* dst, void* dst_vaddr, - ANativeWindowBuffer* src, void const* src_vaddr, - const Region& clip); - - Rect dirtyRegion; - Rect oldDirtyRegion; -}; - -egl_window_surface_v2_t::egl_window_surface_v2_t(EGLDisplay dpy, - EGLConfig config, - int32_t depthFormat, - ANativeWindow* window) -: egl_surface_t(dpy, config, depthFormat), - nativeWindow(window), buffer(0), previousBuffer(0), module(0), - bits(NULL) -{ - pixelFormatTable = gglGetPixelFormatTable(); - - // keep a reference on the window - nativeWindow->common.incRef(&nativeWindow->common); - nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &width); - nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &height); - int format = 0; - nativeWindow->query(nativeWindow, NATIVE_WINDOW_FORMAT, &format); - LOGD("agl2: egl_window_surface_v2_t format=0x%.4X", format); - // assert(0); -} - -egl_window_surface_v2_t::~egl_window_surface_v2_t() -{ - if (buffer) { - buffer->common.decRef(&buffer->common); - } - if (previousBuffer) { - previousBuffer->common.decRef(&previousBuffer->common); - } - nativeWindow->common.decRef(&nativeWindow->common); -} - -EGLBoolean egl_window_surface_v2_t::connect() -{ - // we're intending to do software rendering - native_window_set_usage(nativeWindow, - GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); - - // dequeue a buffer - if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) != NO_ERROR) { - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - - // allocate a corresponding depth-buffer - width = buffer->width; - height = buffer->height; - if (depth.format) { - depth.width = width; - depth.height = height; - depth.stride = depth.width; // use the width here - assert(GGL_PIXEL_FORMAT_Z_32 == depth.format); - depth.data = (GGLubyte*)malloc(depth.stride*depth.height*4); - if (depth.data == 0) { - return setError(EGL_BAD_ALLOC, EGL_FALSE); - } - } - - // keep a reference on the buffer - buffer->common.incRef(&buffer->common); - - // Lock the buffer - nativeWindow->lockBuffer(nativeWindow, buffer); - // pin the buffer down - if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) { - LOGE("connect() failed to lock buffer %p (%ux%u)", - buffer, buffer->width, buffer->height); - return setError(EGL_BAD_ACCESS, EGL_FALSE); - // FIXME: we should make sure we're not accessing the buffer anymore - } - return EGL_TRUE; -} - -void egl_window_surface_v2_t::disconnect() -{ - if (buffer && bits) { - bits = NULL; - unlock(buffer); - } - // enqueue the last frame - if (buffer) - nativeWindow->queueBuffer(nativeWindow, buffer); - if (buffer) { - buffer->common.decRef(&buffer->common); - buffer = 0; - } - if (previousBuffer) { - previousBuffer->common.decRef(&previousBuffer->common); - previousBuffer = 0; - } -} - -status_t egl_window_surface_v2_t::lock( - ANativeWindowBuffer* buf, int usage, void** vaddr) -{ - int err; - - err = module->lock(module, buf->handle, - usage, 0, 0, buf->width, buf->height, vaddr); - - return err; -} - -status_t egl_window_surface_v2_t::unlock(ANativeWindowBuffer* buf) -{ - if (!buf) return BAD_VALUE; - int err = NO_ERROR; - - err = module->unlock(module, buf->handle); - - return err; -} - -void egl_window_surface_v2_t::copyBlt( - ANativeWindowBuffer* dst, void* dst_vaddr, - ANativeWindowBuffer* src, void const* src_vaddr, - const Region& clip) -{ - // NOTE: dst and src must be the same format - - Region::const_iterator cur = clip.begin(); - Region::const_iterator end = clip.end(); - - const size_t bpp = pixelFormatTable[src->format].size; - const size_t dbpr = dst->stride * bpp; - const size_t sbpr = src->stride * bpp; - - uint8_t const * const src_bits = (uint8_t const *)src_vaddr; - uint8_t * const dst_bits = (uint8_t *)dst_vaddr; - - while (cur != end) { - const Rect& r(*cur++); - ssize_t w = r.right - r.left; - ssize_t h = r.bottom - r.top; - if (w <= 0 || h<=0) continue; - size_t size = w * bpp; - uint8_t const * s = src_bits + (r.left + src->stride * r.top) * bpp; - uint8_t * d = dst_bits + (r.left + dst->stride * r.top) * bpp; - if (dbpr==sbpr && size==sbpr) { - size *= h; - h = 1; - } - do { - memcpy(d, s, size); - d += dbpr; - s += sbpr; - } while (--h > 0); - } -} - -EGLBoolean egl_window_surface_v2_t::swapBuffers() -{ - if (!buffer) { - return setError(EGL_BAD_ACCESS, EGL_FALSE); - } - - /* - * Handle eglSetSwapRectangleANDROID() - * We copyback from the front buffer - */ - if (!dirtyRegion.isEmpty()) { - dirtyRegion.andSelf(Rect(buffer->width, buffer->height)); - if (previousBuffer) { - // This was const Region copyBack, but that causes an - // internal compile error on simulator builds - /*const*/ - Region copyBack(Region::subtract(oldDirtyRegion, dirtyRegion)); - if (!copyBack.isEmpty()) { - void* prevBits; - if (lock(previousBuffer, - GRALLOC_USAGE_SW_READ_OFTEN, &prevBits) == NO_ERROR) { - // copy from previousBuffer to buffer - copyBlt(buffer, bits, previousBuffer, prevBits, copyBack); - unlock(previousBuffer); - } - } - } - oldDirtyRegion = dirtyRegion; - } - - if (previousBuffer) { - previousBuffer->common.decRef(&previousBuffer->common); - previousBuffer = 0; - } - - unlock(buffer); - previousBuffer = buffer; - nativeWindow->queueBuffer(nativeWindow, buffer); - buffer = 0; - - // dequeue a new buffer - if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) == NO_ERROR) { - - // TODO: lockBuffer should rather be executed when the very first - // direct rendering occurs. - nativeWindow->lockBuffer(nativeWindow, buffer); - - // reallocate the depth-buffer if needed - if ((width != buffer->width) || (height != buffer->height)) { - // TODO: we probably should reset the swap rect here - // if the window size has changed - width = buffer->width; - height = buffer->height; - if (depth.data) { - free(depth.data); - depth.width = width; - depth.height = height; - depth.stride = buffer->stride; - depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_FALSE); - return EGL_FALSE; - } - } - } - - // keep a reference on the buffer - buffer->common.incRef(&buffer->common); - - // finally pin the buffer down - if (lock(buffer, GRALLOC_USAGE_SW_READ_OFTEN | - GRALLOC_USAGE_SW_WRITE_OFTEN, &bits) != NO_ERROR) { - LOGE("eglSwapBuffers() failed to lock buffer %p (%ux%u)", - buffer, buffer->width, buffer->height); - return setError(EGL_BAD_ACCESS, EGL_FALSE); - // FIXME: we should make sure we're not accessing the buffer anymore - } - } else { - return setError(EGL_BAD_CURRENT_SURFACE, EGL_FALSE); - } - - return EGL_TRUE; -} - -EGLBoolean egl_window_surface_v2_t::setSwapRectangle( - EGLint l, EGLint t, EGLint w, EGLint h) -{ - dirtyRegion = Rect(l, t, l+w, t+h); - return EGL_TRUE; -} - -EGLBoolean egl_window_surface_v2_t::bindDrawSurface(GLES2Context* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = this->buffer->width; - buffer.height = this->buffer->height; - buffer.stride = this->buffer->stride; - buffer.data = (GGLubyte*)bits; - buffer.format = (GGLPixelFormat)this->buffer->format; - gl->rasterizer.interface.SetBuffer(&gl->rasterizer.interface, GL_COLOR_BUFFER_BIT, &buffer); - if (depth.data != gl->rasterizer.depthSurface.data) - gl->rasterizer.interface.SetBuffer(&gl->rasterizer.interface, GL_DEPTH_BUFFER_BIT, &depth); - - return EGL_TRUE; -} -EGLBoolean egl_window_surface_v2_t::bindReadSurface(GLES2Context* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = this->buffer->width; - buffer.height = this->buffer->height; - buffer.stride = this->buffer->stride; - buffer.data = (GGLubyte*)bits; // FIXME: hopefully is is LOCKED!!! - buffer.format = (GGLPixelFormat)this->buffer->format; - puts("agl2: readBuffer not implemented"); - //gl->rasterizer.interface.readBuffer(gl, &buffer); - return EGL_TRUE; -} -EGLint egl_window_surface_v2_t::getHorizontalResolution() const -{ - return (nativeWindow->xdpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_window_surface_v2_t::getVerticalResolution() const -{ - return (nativeWindow->ydpi * EGL_DISPLAY_SCALING) * (1.0f / 25.4f); -} -EGLint egl_window_surface_v2_t::getRefreshRate() const -{ - return (60 * EGL_DISPLAY_SCALING); // FIXME -} -EGLint egl_window_surface_v2_t::getSwapBehavior() const -{ - /* - * EGL_BUFFER_PRESERVED means that eglSwapBuffers() completely preserves - * the content of the swapped buffer. - * - * EGL_BUFFER_DESTROYED means that the content of the buffer is lost. - * - * However when ANDROID_swap_retcangle is supported, EGL_BUFFER_DESTROYED - * only applies to the area specified by eglSetSwapRectangleANDROID(), that - * is, everything outside of this area is preserved. - * - * This implementation of EGL assumes the later case. - * - */ - - return EGL_BUFFER_DESTROYED; -} - -// ---------------------------------------------------------------------------- - -struct egl_pixmap_surface_t : public egl_surface_t { - egl_pixmap_surface_t( - EGLDisplay dpy, EGLConfig config, - int32_t depthFormat, - egl_native_pixmap_t const * pixmap); - - virtual ~egl_pixmap_surface_t() { } - - virtual bool initCheck() const { - return !depth.format || depth.data!=0; - } - virtual EGLBoolean bindDrawSurface(GLES2Context* gl); - virtual EGLBoolean bindReadSurface(GLES2Context* gl); - virtual EGLint getWidth() const { - return nativePixmap.width; - } - virtual EGLint getHeight() const { - return nativePixmap.height; - } -private: - egl_native_pixmap_t nativePixmap; -}; - -egl_pixmap_surface_t::egl_pixmap_surface_t(EGLDisplay dpy, - EGLConfig config, - int32_t depthFormat, - egl_native_pixmap_t const * pixmap) -: egl_surface_t(dpy, config, depthFormat), nativePixmap(*pixmap) -{ - if (depthFormat) { - depth.width = pixmap->width; - depth.height = pixmap->height; - depth.stride = depth.width; // use the width here - depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - } - } -} -EGLBoolean egl_pixmap_surface_t::bindDrawSurface(GLES2Context* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = nativePixmap.width; - buffer.height = nativePixmap.height; - buffer.stride = nativePixmap.stride; - buffer.data = nativePixmap.data; - buffer.format = (GGLPixelFormat)nativePixmap.format; - - gl->rasterizer.interface.SetBuffer(&gl->rasterizer.interface, GL_COLOR_BUFFER_BIT, &buffer); - if (depth.data != gl->rasterizer.depthSurface.data) - gl->rasterizer.interface.SetBuffer(&gl->rasterizer.interface, GL_DEPTH_BUFFER_BIT, &depth); - return EGL_TRUE; -} -EGLBoolean egl_pixmap_surface_t::bindReadSurface(GLES2Context* gl) -{ - GGLSurface buffer; - buffer.version = sizeof(GGLSurface); - buffer.width = nativePixmap.width; - buffer.height = nativePixmap.height; - buffer.stride = nativePixmap.stride; - buffer.data = nativePixmap.data; - buffer.format = (GGLPixelFormat)nativePixmap.format; - puts("agl2: readBuffer not implemented"); - //gl->rasterizer.interface.readBuffer(gl, &buffer); - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- - -struct egl_pbuffer_surface_t : public egl_surface_t { - egl_pbuffer_surface_t( - EGLDisplay dpy, EGLConfig config, int32_t depthFormat, - int32_t w, int32_t h, int32_t f); - - virtual ~egl_pbuffer_surface_t(); - - virtual bool initCheck() const { - return pbuffer.data != 0; - } - virtual EGLBoolean bindDrawSurface(GLES2Context* gl); - virtual EGLBoolean bindReadSurface(GLES2Context* gl); - virtual EGLint getWidth() const { - return pbuffer.width; - } - virtual EGLint getHeight() const { - return pbuffer.height; - } -private: - GGLSurface pbuffer; -}; - -egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy, - EGLConfig config, int32_t depthFormat, - int32_t w, int32_t h, int32_t f) -: egl_surface_t(dpy, config, depthFormat) -{ - size_t size = w*h; - switch (f) { - case GGL_PIXEL_FORMAT_A_8: - size *= 1; - break; - case GGL_PIXEL_FORMAT_RGB_565: - size *= 2; - break; - case GGL_PIXEL_FORMAT_RGBA_8888: - size *= 4; - break; - case GGL_PIXEL_FORMAT_RGBX_8888: - size *= 4; - break; - default: - LOGE("incompatible pixel format for pbuffer (format=%d)", f); - pbuffer.data = 0; - break; - } - pbuffer.version = sizeof(GGLSurface); - pbuffer.width = w; - pbuffer.height = h; - pbuffer.stride = w; - pbuffer.data = (GGLubyte*)malloc(size); - pbuffer.format = (GGLPixelFormat)f; - - if (depthFormat) { - depth.width = pbuffer.width; - depth.height = pbuffer.height; - depth.stride = depth.width; // use the width here - depth.data = (GGLubyte*)malloc(depth.stride*depth.height*2); - if (depth.data == 0) { - setError(EGL_BAD_ALLOC, EGL_NO_SURFACE); - return; - } - } -} -egl_pbuffer_surface_t::~egl_pbuffer_surface_t() -{ - free(pbuffer.data); -} -EGLBoolean egl_pbuffer_surface_t::bindDrawSurface(GLES2Context* gl) -{ - gl->rasterizer.interface.SetBuffer(&gl->rasterizer.interface, GL_COLOR_BUFFER_BIT, &pbuffer); - if (depth.data != gl->rasterizer.depthSurface.data) - gl->rasterizer.interface.SetBuffer(&gl->rasterizer.interface, GL_DEPTH_BUFFER_BIT, &depth); - return EGL_TRUE; -} -EGLBoolean egl_pbuffer_surface_t::bindReadSurface(GLES2Context* gl) -{ - puts("agl2: readBuffer not implemented"); - //gl->rasterizer.interface.readBuffer(gl, &pbuffer); - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- - -struct config_pair_t { - GLint key; - GLint value; -}; - -struct configs_t { - const config_pair_t* array; - int size; -}; - -struct config_management_t { - GLint key; - bool (*match)(GLint reqValue, GLint confValue); - static bool atLeast(GLint reqValue, GLint confValue) { - return (reqValue == EGL_DONT_CARE) || (confValue >= reqValue); - } - static bool exact(GLint reqValue, GLint confValue) { - return (reqValue == EGL_DONT_CARE) || (confValue == reqValue); - } - static bool mask(GLint reqValue, GLint confValue) { - return (confValue & reqValue) == reqValue; - } - static bool ignore(GLint reqValue, GLint confValue) { - return true; - } -}; - -// ---------------------------------------------------------------------------- - -#define VERSION_MAJOR 1 -#define VERSION_MINOR 2 -static char const * const gVendorString = "Google Inc."; -static char const * const gVersionString = "0.0 Android Driver 0.0.0"; -static char const * const gClientApiString = "OpenGL ES2"; -static char const * const gExtensionsString = - //"EGL_KHR_image_base " - // "KHR_image_pixmap " - //"EGL_ANDROID_image_native_buffer " - //"EGL_ANDROID_swap_rectangle " - ""; - -// ---------------------------------------------------------------------------- - -struct extention_map_t { - const char * const name; - __eglMustCastToProperFunctionPointerType address; -}; - -static const extention_map_t gExtentionMap[] = { - // { "glDrawTexsOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexsOES }, - // { "glDrawTexiOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexiOES }, - // { "glDrawTexfOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexfOES }, - // { "glDrawTexxOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexxOES }, - // { "glDrawTexsvOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexsvOES }, - // { "glDrawTexivOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexivOES }, - // { "glDrawTexfvOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexfvOES }, - // { "glDrawTexxvOES", - // (__eglMustCastToProperFunctionPointerType)&glDrawTexxvOES }, - // { "glQueryMatrixxOES", - // (__eglMustCastToProperFunctionPointerType)&glQueryMatrixxOES }, - // { "glEGLImageTargetTexture2DOES", - // (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetTexture2DOES }, - // { "glEGLImageTargetRenderbufferStorageOES", - // (__eglMustCastToProperFunctionPointerType)&glEGLImageTargetRenderbufferStorageOES }, - // { "glClipPlanef", - // (__eglMustCastToProperFunctionPointerType)&glClipPlanef }, - // { "glClipPlanex", - // (__eglMustCastToProperFunctionPointerType)&glClipPlanex }, - // { "glBindBuffer", - // (__eglMustCastToProperFunctionPointerType)&glBindBuffer }, - // { "glBufferData", - // (__eglMustCastToProperFunctionPointerType)&glBufferData }, - // { "glBufferSubData", - // (__eglMustCastToProperFunctionPointerType)&glBufferSubData }, - // { "glDeleteBuffers", - // (__eglMustCastToProperFunctionPointerType)&glDeleteBuffers }, - // { "glGenBuffers", - // (__eglMustCastToProperFunctionPointerType)&glGenBuffers }, - // { "eglCreateImageKHR", - // (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, - // { "eglDestroyImageKHR", - // (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, - // { "eglSetSwapRectangleANDROID", - // (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, -}; - -/* - * In the lists below, attributes names MUST be sorted. - * Additionally, all configs must be sorted according to - * the EGL specification. - */ - -static config_pair_t const config_base_attribute_list[] = { - { EGL_STENCIL_SIZE, 0 }, - { EGL_CONFIG_CAVEAT, EGL_SLOW_CONFIG }, - { EGL_LEVEL, 0 }, - { EGL_MAX_PBUFFER_HEIGHT, GGL_MAX_VIEWPORT_DIMS }, - { EGL_MAX_PBUFFER_PIXELS, - GGL_MAX_VIEWPORT_DIMS*GGL_MAX_VIEWPORT_DIMS }, - { EGL_MAX_PBUFFER_WIDTH, GGL_MAX_VIEWPORT_DIMS }, - { EGL_NATIVE_RENDERABLE, EGL_TRUE }, - { EGL_NATIVE_VISUAL_ID, 0 }, - { EGL_NATIVE_VISUAL_TYPE, GGL_PIXEL_FORMAT_RGBA_8888 }, - { EGL_SAMPLES, 0 }, - { EGL_SAMPLE_BUFFERS, 0 }, - { EGL_TRANSPARENT_TYPE, EGL_NONE }, - { EGL_TRANSPARENT_BLUE_VALUE, 0 }, - { EGL_TRANSPARENT_GREEN_VALUE, 0 }, - { EGL_TRANSPARENT_RED_VALUE, 0 }, - { EGL_BIND_TO_TEXTURE_RGBA, EGL_FALSE }, - { EGL_BIND_TO_TEXTURE_RGB, EGL_FALSE }, - { EGL_MIN_SWAP_INTERVAL, 1 }, - { EGL_MAX_SWAP_INTERVAL, 1 }, - { EGL_LUMINANCE_SIZE, 0 }, - { EGL_ALPHA_MASK_SIZE, 0 }, - { EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER }, - { EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT }, - { EGL_CONFORMANT, 0 } -}; - -// These configs can override the base attribute list -// NOTE: when adding a config here, don't forget to update eglCreate*Surface() - -// 565 configs -static config_pair_t const config_0_attribute_list[] = { - { EGL_BUFFER_SIZE, 16 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 5 }, - { EGL_GREEN_SIZE, 6 }, - { EGL_RED_SIZE, 5 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 0 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_1_attribute_list[] = { - { EGL_BUFFER_SIZE, 16 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 5 }, - { EGL_GREEN_SIZE, 6 }, - { EGL_RED_SIZE, 5 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 1 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGB_565 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// RGB 888 configs -static config_pair_t const config_2_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 6 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_3_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 0 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 7 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBX_8888 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// 8888 configs -static config_pair_t const config_4_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 2 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_5_attribute_list[] = { - { EGL_BUFFER_SIZE, 32 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 8 }, - { EGL_GREEN_SIZE, 8 }, - { EGL_RED_SIZE, 8 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 3 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_RGBA_8888 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -// A8 configs -static config_pair_t const config_6_attribute_list[] = { - { EGL_BUFFER_SIZE, 8 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 0 }, - { EGL_GREEN_SIZE, 0 }, - { EGL_RED_SIZE, 0 }, - { EGL_DEPTH_SIZE, 0 }, - { EGL_CONFIG_ID, 4 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static config_pair_t const config_7_attribute_list[] = { - { EGL_BUFFER_SIZE, 8 }, - { EGL_ALPHA_SIZE, 8 }, - { EGL_BLUE_SIZE, 0 }, - { EGL_GREEN_SIZE, 0 }, - { EGL_RED_SIZE, 0 }, - { EGL_DEPTH_SIZE, 16 }, - { EGL_CONFIG_ID, 5 }, - { EGL_NATIVE_VISUAL_ID, GGL_PIXEL_FORMAT_A_8 }, - { EGL_SURFACE_TYPE, EGL_SWAP_BEHAVIOR_PRESERVED_BIT|EGL_WINDOW_BIT|EGL_PBUFFER_BIT|EGL_PIXMAP_BIT }, -}; - -static configs_t const gConfigs[] = { - { config_0_attribute_list, NELEM(config_0_attribute_list) }, - { config_1_attribute_list, NELEM(config_1_attribute_list) }, - { config_2_attribute_list, NELEM(config_2_attribute_list) }, - { config_3_attribute_list, NELEM(config_3_attribute_list) }, - { config_4_attribute_list, NELEM(config_4_attribute_list) }, - { config_5_attribute_list, NELEM(config_5_attribute_list) }, - // { config_6_attribute_list, NELEM(config_6_attribute_list) }, - // { config_7_attribute_list, NELEM(config_7_attribute_list) }, -}; - -static config_management_t const gConfigManagement[] = { - { EGL_BUFFER_SIZE, config_management_t::atLeast }, - { EGL_ALPHA_SIZE, config_management_t::atLeast }, - { EGL_BLUE_SIZE, config_management_t::atLeast }, - { EGL_GREEN_SIZE, config_management_t::atLeast }, - { EGL_RED_SIZE, config_management_t::atLeast }, - { EGL_DEPTH_SIZE, config_management_t::atLeast }, - { EGL_STENCIL_SIZE, config_management_t::atLeast }, - { EGL_CONFIG_CAVEAT, config_management_t::exact }, - { EGL_CONFIG_ID, config_management_t::exact }, - { EGL_LEVEL, config_management_t::exact }, - { EGL_MAX_PBUFFER_HEIGHT, config_management_t::ignore }, - { EGL_MAX_PBUFFER_PIXELS, config_management_t::ignore }, - { EGL_MAX_PBUFFER_WIDTH, config_management_t::ignore }, - { EGL_NATIVE_RENDERABLE, config_management_t::exact }, - { EGL_NATIVE_VISUAL_ID, config_management_t::ignore }, - { EGL_NATIVE_VISUAL_TYPE, config_management_t::exact }, - { EGL_SAMPLES, config_management_t::exact }, - { EGL_SAMPLE_BUFFERS, config_management_t::exact }, - { EGL_SURFACE_TYPE, config_management_t::mask }, - { EGL_TRANSPARENT_TYPE, config_management_t::exact }, - { EGL_TRANSPARENT_BLUE_VALUE, config_management_t::exact }, - { EGL_TRANSPARENT_GREEN_VALUE, config_management_t::exact }, - { EGL_TRANSPARENT_RED_VALUE, config_management_t::exact }, - { EGL_BIND_TO_TEXTURE_RGBA, config_management_t::exact }, - { EGL_BIND_TO_TEXTURE_RGB, config_management_t::exact }, - { EGL_MIN_SWAP_INTERVAL, config_management_t::exact }, - { EGL_MAX_SWAP_INTERVAL, config_management_t::exact }, - { EGL_LUMINANCE_SIZE, config_management_t::atLeast }, - { EGL_ALPHA_MASK_SIZE, config_management_t::atLeast }, - { EGL_COLOR_BUFFER_TYPE, config_management_t::exact }, - { EGL_RENDERABLE_TYPE, config_management_t::mask }, - { EGL_CONFORMANT, config_management_t::mask } -}; - - -static config_pair_t const config_defaults[] = { - // attributes that are not specified are simply ignored, if a particular - // one needs not be ignored, it must be specified here, eg: - // { EGL_SURFACE_TYPE, EGL_WINDOW_BIT }, -}; - -// ---------------------------------------------------------------------------- - -static status_t getConfigFormatInfo(EGLint configID, - int32_t& pixelFormat, int32_t& depthFormat) -{ - switch (configID) { - case 0: - pixelFormat = GGL_PIXEL_FORMAT_RGB_565; - depthFormat = 0; - break; - case 1: - pixelFormat = GGL_PIXEL_FORMAT_RGB_565; - depthFormat = GGL_PIXEL_FORMAT_Z_32; - break; - case 2: - pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; - depthFormat = 0; - break; - case 3: - pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; - depthFormat = GGL_PIXEL_FORMAT_Z_32; - break; - case 4: - pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; - depthFormat = 0; - break; - case 5: - pixelFormat = GGL_PIXEL_FORMAT_RGBA_8888; - depthFormat = GGL_PIXEL_FORMAT_Z_32; - break; - case 6: - pixelFormat = GGL_PIXEL_FORMAT_A_8; - depthFormat = 0; - break; - case 7: - pixelFormat = GGL_PIXEL_FORMAT_A_8; - depthFormat = GGL_PIXEL_FORMAT_Z_32; - break; - default: - return NAME_NOT_FOUND; - } - return NO_ERROR; -} - -// ---------------------------------------------------------------------------- - -template<typename T> -static int binarySearch(T const sortedArray[], int first, int last, EGLint key) -{ - while (first <= last) { - int mid = (first + last) / 2; - if (key > sortedArray[mid].key) { - first = mid + 1; - } else if (key < sortedArray[mid].key) { - last = mid - 1; - } else { - return mid; - } - } - return -1; -} - -static int isAttributeMatching(int i, EGLint attr, EGLint val) -{ - // look for the attribute in all of our configs - config_pair_t const* configFound = gConfigs[i].array; - int index = binarySearch<config_pair_t>( - gConfigs[i].array, - 0, gConfigs[i].size-1, - attr); - if (index < 0) { - configFound = config_base_attribute_list; - index = binarySearch<config_pair_t>( - config_base_attribute_list, - 0, NELEM(config_base_attribute_list)-1, - attr); - } - if (index >= 0) { - // attribute found, check if this config could match - int cfgMgtIndex = binarySearch<config_management_t>( - gConfigManagement, - 0, NELEM(gConfigManagement)-1, - attr); - if (cfgMgtIndex >= 0) { - bool match = gConfigManagement[cfgMgtIndex].match( - val, configFound[index].value); - if (match) { - // this config matches - return 1; - } - } else { - // attribute not found. this should NEVER happen. - } - } else { - // error, this attribute doesn't exist - } - return 0; -} - -static int makeCurrent(GLES2Context* gl) -{ - GLES2Context* current = (GLES2Context*)getGlThreadSpecific(); - if (gl) { - egl_context_t* c = egl_context_t::context(gl); - if (c->flags & egl_context_t::IS_CURRENT) { - if (current != gl) { - // it is an error to set a context current, if it's already - // current to another thread - return -1; - } - } else { - if (current) { - // mark the current context as not current, and flush - glFlush(); - egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; - } - } - if (!(c->flags & egl_context_t::IS_CURRENT)) { - // The context is not current, make it current! - setGlThreadSpecific(gl); - c->flags |= egl_context_t::IS_CURRENT; - } - } else { - if (current) { - // mark the current context as not current, and flush - glFlush(); - egl_context_t::context(current)->flags &= ~egl_context_t::IS_CURRENT; - } - // this thread has no context attached to it - setGlThreadSpecific(0); - } - return 0; -} - -static EGLBoolean getConfigAttrib(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ - size_t numConfigs = NELEM(gConfigs); - int index = (int)config; - if (uint32_t(index) >= numConfigs) - return setError(EGL_BAD_CONFIG, EGL_FALSE); - - int attrIndex; - attrIndex = binarySearch<config_pair_t>( - gConfigs[index].array, - 0, gConfigs[index].size-1, - attribute); - if (attrIndex>=0) { - *value = gConfigs[index].array[attrIndex].value; - return EGL_TRUE; - } - - attrIndex = binarySearch<config_pair_t>( - config_base_attribute_list, - 0, NELEM(config_base_attribute_list)-1, - attribute); - if (attrIndex>=0) { - *value = config_base_attribute_list[attrIndex].value; - return EGL_TRUE; - } - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); -} - -static EGLSurface createWindowSurface(EGLDisplay dpy, EGLConfig config, - NativeWindowType window, const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - if (window == 0) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - EGLint surfaceType; - if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) - return EGL_FALSE; - - if (!(surfaceType & EGL_WINDOW_BIT)) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - if (reinterpret_cast<ANativeWindow*>(window)->common.magic != - ANDROID_NATIVE_WINDOW_MAGIC) { - return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); - } - - EGLint configID; - if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) - return EGL_FALSE; - - int32_t depthFormat; - int32_t pixelFormat; - if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) { - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - } - - // FIXME: we don't have access to the pixelFormat here just yet. - // (it's possible that the surface is not fully initialized) - // maybe this should be done after the page-flip - //if (EGLint(info.format) != pixelFormat) - // return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - egl_surface_t* surface; - surface = new egl_window_surface_v2_t(dpy, config, depthFormat, - reinterpret_cast<ANativeWindow*>(window)); - - if (!surface->initCheck()) { - // there was a problem in the ctor, the error - // flag has been set. - delete surface; - surface = 0; - } - return surface; -} - -static EGLSurface createPixmapSurface(EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - if (pixmap == 0) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - EGLint surfaceType; - if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) - return EGL_FALSE; - - if (!(surfaceType & EGL_PIXMAP_BIT)) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - if (reinterpret_cast<egl_native_pixmap_t*>(pixmap)->version != - sizeof(egl_native_pixmap_t)) { - return setError(EGL_BAD_NATIVE_PIXMAP, EGL_NO_SURFACE); - } - - EGLint configID; - if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) - return EGL_FALSE; - - int32_t depthFormat; - int32_t pixelFormat; - if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) { - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - } - - if (reinterpret_cast<egl_native_pixmap_t *>(pixmap)->format != pixelFormat) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - egl_surface_t* surface = - new egl_pixmap_surface_t(dpy, config, depthFormat, - reinterpret_cast<egl_native_pixmap_t*>(pixmap)); - - if (!surface->initCheck()) { - // there was a problem in the ctor, the error - // flag has been set. - delete surface; - surface = 0; - } - return surface; -} - -static EGLSurface createPbufferSurface(EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - - EGLint surfaceType; - if (getConfigAttrib(dpy, config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) - return EGL_FALSE; - - if (!(surfaceType & EGL_PBUFFER_BIT)) - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - - EGLint configID; - if (getConfigAttrib(dpy, config, EGL_CONFIG_ID, &configID) == EGL_FALSE) - return EGL_FALSE; - - int32_t depthFormat; - int32_t pixelFormat; - if (getConfigFormatInfo(configID, pixelFormat, depthFormat) != NO_ERROR) { - return setError(EGL_BAD_MATCH, EGL_NO_SURFACE); - } - - int32_t w = 0; - int32_t h = 0; - while (attrib_list[0]) { - if (attrib_list[0] == EGL_WIDTH) w = attrib_list[1]; - if (attrib_list[0] == EGL_HEIGHT) h = attrib_list[1]; - attrib_list+=2; - } - - egl_surface_t* surface = - new egl_pbuffer_surface_t(dpy, config, depthFormat, w, h, pixelFormat); - - if (!surface->initCheck()) { - // there was a problem in the ctor, the error - // flag has been set. - delete surface; - surface = 0; - } - return surface; -} - -// ---------------------------------------------------------------------------- -}; // namespace android -// ---------------------------------------------------------------------------- - -using namespace android; - -// ---------------------------------------------------------------------------- -// Initialization -// ---------------------------------------------------------------------------- - -EGLDisplay eglGetDisplay(NativeDisplayType display) -{ - puts("agl2:eglGetDisplay"); -#ifndef HAVE_ANDROID_OS - // this just needs to be done once - if (gGLKey == -1) { - pthread_mutex_lock(&gInitMutex); - if (gGLKey == -1) - pthread_key_create(&gGLKey, NULL); - pthread_mutex_unlock(&gInitMutex); - } -#endif - if (display == EGL_DEFAULT_DISPLAY) { - EGLDisplay dpy = (EGLDisplay)1; - egl_display_t& d = egl_display_t::get_display(dpy); - d.type = display; - return dpy; - } - return EGL_NO_DISPLAY; -} - -EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) -{ - puts("agl2:eglInitialize"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - EGLBoolean res = EGL_TRUE; - egl_display_t& d = egl_display_t::get_display(dpy); - - if (android_atomic_inc(&d.initialized) == 0) { - // initialize stuff here if needed - //pthread_mutex_lock(&gInitMutex); - //pthread_mutex_unlock(&gInitMutex); - } - - if (res == EGL_TRUE) { - if (major != NULL) *major = VERSION_MAJOR; - if (minor != NULL) *minor = VERSION_MINOR; - } - return res; -} - -EGLBoolean eglTerminate(EGLDisplay dpy) -{ - puts("agl2:eglTerminate"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - EGLBoolean res = EGL_TRUE; - egl_display_t& d = egl_display_t::get_display(dpy); - if (android_atomic_dec(&d.initialized) == 1) { - // TODO: destroy all resources (surfaces, contexts, etc...) - //pthread_mutex_lock(&gInitMutex); - //pthread_mutex_unlock(&gInitMutex); - } - return res; -} - -// ---------------------------------------------------------------------------- -// configuration -// ---------------------------------------------------------------------------- - -EGLBoolean eglGetConfigs( EGLDisplay dpy, - EGLConfig *configs, - EGLint config_size, EGLint *num_config) -{ - puts("agl2:eglGetConfigs"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - GLint numConfigs = NELEM(gConfigs); - if (!configs) { - *num_config = numConfigs; - return EGL_TRUE; - } - GLint i; - for (i=0 ; i<numConfigs && i<config_size ; i++) { - *configs++ = (EGLConfig)i; - } - *num_config = i; - return EGL_TRUE; -} - -static const char * ATTRIBUTE_NAMES [] = { - "EGL_BUFFER_SIZE", - "EGL_ALPHA_SIZE", - "EGL_BLUE_SIZE", - "EGL_GREEN_SIZE", - "EGL_RED_SIZE", - "EGL_DEPTH_SIZE", - "EGL_STENCIL_SIZE", - "EGL_CONFIG_CAVEAT", - "EGL_CONFIG_ID", - "EGL_LEVEL", - "EGL_MAX_PBUFFER_HEIGHT", - "EGL_MAX_PBUFFER_PIXELS", - "EGL_MAX_PBUFFER_WIDTH", - "EGL_NATIVE_RENDERABLE", - "EGL_NATIVE_VISUAL_ID", - "EGL_NATIVE_VISUAL_TYPE", - "EGL_PRESERVED_RESOURCES", - "EGL_SAMPLES", - "EGL_SAMPLE_BUFFERS", - "EGL_SURFACE_TYPE", - "EGL_TRANSPARENT_TYPE", - "EGL_TRANSPARENT_BLUE_VALUE", - "EGL_TRANSPARENT_GREEN_VALUE", - "EGL_TRANSPARENT_RED_VALUE", - "EGL_NONE", /* Attrib list terminator */ - "EGL_BIND_TO_TEXTURE_RGB", - "EGL_BIND_TO_TEXTURE_RGBA", - "EGL_MIN_SWAP_INTERVAL", - "EGL_MAX_SWAP_INTERVAL", - "EGL_LUMINANCE_SIZE", - "EGL_ALPHA_MASK_SIZE", - "EGL_COLOR_BUFFER_TYPE", - "EGL_RENDERABLE_TYPE", - "EGL_MATCH_NATIVE_PIXMAP", /* Pseudo-attribute (not queryable) */ - "EGL_CONFORMANT", -}; - -EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list, - EGLConfig *configs, EGLint config_size, - EGLint *num_config) -{ - puts("agl2:eglChooseConfig"); - LOGD("\n***\n***\n agl2:LOGD eglChooseConfig \n***\n***\n"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - if (ggl_unlikely(num_config==0)) { - LOGD("\n***\n***\n num_config==0 \n***\n***\n"); - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - } - - if (ggl_unlikely(attrib_list==0)) { - /* - * A NULL attrib_list should be treated as though it was an empty - * one (terminated with EGL_NONE) as defined in - * section 3.4.1 "Querying Configurations" in the EGL specification. - */ - LOGD("\n***\n***\n attrib_list==0 \n***\n***\n"); - static const EGLint dummy = EGL_NONE; - attrib_list = &dummy; - } - - for (const EGLint * attrib = attrib_list; *attrib != EGL_NONE; attrib += 2) { - LOGD("eglChooseConfig %s(%.4X): %d \n", ATTRIBUTE_NAMES[attrib[0] - EGL_BUFFER_SIZE], attrib[0], attrib[1]); - if (EGL_BUFFER_SIZE > attrib[0] || EGL_CONFORMANT < attrib[0]) - LOGD("eglChooseConfig invalid config attrib: 0x%.4X=%d \n", attrib[0], attrib[1]); - } - - int numAttributes = 0; - int numConfigs = NELEM(gConfigs); - uint32_t possibleMatch = (1<<numConfigs)-1; - while (possibleMatch && *attrib_list != EGL_NONE) { - numAttributes++; - EGLint attr = *attrib_list++; - EGLint val = *attrib_list++; - for (int i=0 ; possibleMatch && i<numConfigs ; i++) { - if (!(possibleMatch & (1<<i))) - continue; - if (isAttributeMatching(i, attr, val) == 0) { - LOGD("!isAttributeMatching config(%d) %s=%d \n", i, ATTRIBUTE_NAMES[attr - EGL_BUFFER_SIZE], val); - possibleMatch &= ~(1<<i); - } - } - } - - LOGD("eglChooseConfig possibleMatch=%.4X \n", possibleMatch); - - // now, handle the attributes which have a useful default value - for (size_t j=0 ; possibleMatch && j<NELEM(config_defaults) ; j++) { - // see if this attribute was specified, if not, apply its - // default value - if (binarySearch<config_pair_t>( - (config_pair_t const*)attrib_list, - 0, numAttributes-1, - config_defaults[j].key) < 0) { - for (int i=0 ; possibleMatch && i<numConfigs ; i++) { - if (!(possibleMatch & (1<<i))) - continue; - if (isAttributeMatching(i, - config_defaults[j].key, - config_defaults[j].value) == 0) { - possibleMatch &= ~(1<<i); - } - } - } - } - - // return the configurations found - int n=0; - if (possibleMatch) { - if (configs) { - for (int i=0 ; config_size && i<numConfigs ; i++) { - if (possibleMatch & (1<<i)) { - *configs++ = (EGLConfig)i; - config_size--; - n++; - } - } - } else { - for (int i=0 ; i<numConfigs ; i++) { - if (possibleMatch & (1<<i)) { - n++; - } - } - } - } - *num_config = n; - LOGD("\n***\n***\n num_config==%d \n***\n***\n", *num_config); - return EGL_TRUE; -} - -EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, - EGLint attribute, EGLint *value) -{ - puts("agl2:eglGetConfigAttrib"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - return getConfigAttrib(dpy, config, attribute, value); -} - -// ---------------------------------------------------------------------------- -// surfaces -// ---------------------------------------------------------------------------- - -EGLSurface eglCreateWindowSurface( EGLDisplay dpy, EGLConfig config, - NativeWindowType window, - const EGLint *attrib_list) -{ - puts("agl2:eglCreateWindowSurface"); - return createWindowSurface(dpy, config, window, attrib_list); -} - -EGLSurface eglCreatePixmapSurface( EGLDisplay dpy, EGLConfig config, - NativePixmapType pixmap, - const EGLint *attrib_list) -{ - puts("agl2:eglCreatePixmapSurface"); - return createPixmapSurface(dpy, config, pixmap, attrib_list); -} - -EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config, - const EGLint *attrib_list) -{ - puts("agl2:eglCreatePbufferSurface"); - return createPbufferSurface(dpy, config, attrib_list); -} - -EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface) -{ - puts("agl2:eglDestroySurface"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - if (eglSurface != EGL_NO_SURFACE) { - egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) ); - if (!surface->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (surface->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - if (surface->ctx) { - // FIXME: this surface is current check what the spec says - surface->disconnect(); - surface->ctx = 0; - } - delete surface; - } - return EGL_TRUE; -} - -EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface eglSurface, - EGLint attribute, EGLint *value) -{ - puts("agl2:eglQuerySurface"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_surface_t* surface = static_cast<egl_surface_t*>(eglSurface); - if (!surface->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (surface->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - EGLBoolean ret = EGL_TRUE; - switch (attribute) { - case EGL_CONFIG_ID: - ret = getConfigAttrib(dpy, surface->config, EGL_CONFIG_ID, value); - break; - case EGL_WIDTH: - *value = surface->getWidth(); - break; - case EGL_HEIGHT: - *value = surface->getHeight(); - break; - case EGL_LARGEST_PBUFFER: - // not modified for a window or pixmap surface - break; - case EGL_TEXTURE_FORMAT: - *value = EGL_NO_TEXTURE; - break; - case EGL_TEXTURE_TARGET: - *value = EGL_NO_TEXTURE; - break; - case EGL_MIPMAP_TEXTURE: - *value = EGL_FALSE; - break; - case EGL_MIPMAP_LEVEL: - *value = 0; - break; - case EGL_RENDER_BUFFER: - // TODO: return the real RENDER_BUFFER here - *value = EGL_BACK_BUFFER; - break; - case EGL_HORIZONTAL_RESOLUTION: - // pixel/mm * EGL_DISPLAY_SCALING - *value = surface->getHorizontalResolution(); - break; - case EGL_VERTICAL_RESOLUTION: - // pixel/mm * EGL_DISPLAY_SCALING - *value = surface->getVerticalResolution(); - break; - case EGL_PIXEL_ASPECT_RATIO: { - // w/h * EGL_DISPLAY_SCALING - int wr = surface->getHorizontalResolution(); - int hr = surface->getVerticalResolution(); - *value = (wr * EGL_DISPLAY_SCALING) / hr; - } - break; - case EGL_SWAP_BEHAVIOR: - *value = surface->getSwapBehavior(); - break; - default: - ret = setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); - } - return ret; -} - -EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, - EGLContext share_list, const EGLint *attrib_list) -{ - puts("agl2:eglCreateContext"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - - GLES2Context* gl = new GLES2Context();//ogles_init(sizeof(egl_context_t)); - if (!gl) return setError(EGL_BAD_ALLOC, EGL_NO_CONTEXT); - - //egl_context_t* c = static_cast<egl_context_t*>(gl->rasterizer.base); - egl_context_t * c = &gl->egl; - c->flags = egl_context_t::NEVER_CURRENT; - c->dpy = dpy; - c->config = config; - c->read = 0; - c->draw = 0; - - c->frame = 0; - c->lastSwapTime = clock(); - c->accumulateSeconds = 0; - return (EGLContext)gl; -} - -EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) -{ - puts("agl2:eglDestroyContext"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_context_t* c = egl_context_t::context(ctx); - if (c->flags & egl_context_t::IS_CURRENT) - setGlThreadSpecific(0); - //ogles_uninit((GLES2Context*)ctx); - delete (GLES2Context*)ctx; - return EGL_TRUE; -} - -EGLBoolean eglMakeCurrent( EGLDisplay dpy, EGLSurface draw, - EGLSurface read, EGLContext ctx) -{ - puts("agl2:eglMakeCurrent"); - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - if (draw) { - egl_surface_t* s = (egl_surface_t*)draw; - if (!s->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (s->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: check that draw is compatible with the context - } - if (read && read!=draw) { - egl_surface_t* s = (egl_surface_t*)read; - if (!s->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (s->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: check that read is compatible with the context - } - - EGLContext current_ctx = EGL_NO_CONTEXT; - - if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT)) - return setError(EGL_BAD_MATCH, EGL_FALSE); - - if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT)) - return setError(EGL_BAD_MATCH, EGL_FALSE); - - if (ctx == EGL_NO_CONTEXT) { - // if we're detaching, we need the current context - current_ctx = (EGLContext)getGlThreadSpecific(); - } else { - egl_context_t* c = egl_context_t::context(ctx); - egl_surface_t* d = (egl_surface_t*)draw; - egl_surface_t* r = (egl_surface_t*)read; - if ((d && d->ctx && d->ctx != ctx) || - (r && r->ctx && r->ctx != ctx)) { - // one of the surface is bound to a context in another thread - return setError(EGL_BAD_ACCESS, EGL_FALSE); - } - } - - GLES2Context* gl = (GLES2Context*)ctx; - if (makeCurrent(gl) == 0) { - if (ctx) { - egl_context_t* c = egl_context_t::context(ctx); - egl_surface_t* d = (egl_surface_t*)draw; - egl_surface_t* r = (egl_surface_t*)read; - - if (c->draw) { - egl_surface_t* s = reinterpret_cast<egl_surface_t*>(c->draw); - s->disconnect(); - } - if (c->read) { - // FIXME: unlock/disconnect the read surface too - } - - c->draw = draw; - c->read = read; - - if (c->flags & egl_context_t::NEVER_CURRENT) { - c->flags &= ~egl_context_t::NEVER_CURRENT; - GLint w = 0; - GLint h = 0; - if (draw) { - w = d->getWidth(); - h = d->getHeight(); - } - gl->rasterizer.interface.Viewport(&gl->rasterizer.interface, 0, 0, w, h); - //ogles_surfaceport(gl, 0, 0); - //ogles_viewport(gl, 0, 0, w, h); - //ogles_scissor(gl, 0, 0, w, h); - } - if (d) { - if (d->connect() == EGL_FALSE) { - return EGL_FALSE; - } - d->ctx = ctx; - d->bindDrawSurface(gl); - } - if (r) { - // FIXME: lock/connect the read surface too - r->ctx = ctx; - r->bindReadSurface(gl); - } - } else { - // if surfaces were bound to the context bound to this thread - // mark then as unbound. - if (current_ctx) { - egl_context_t* c = egl_context_t::context(current_ctx); - egl_surface_t* d = (egl_surface_t*)c->draw; - egl_surface_t* r = (egl_surface_t*)c->read; - if (d) { - c->draw = 0; - d->ctx = EGL_NO_CONTEXT; - d->disconnect(); - } - if (r) { - c->read = 0; - r->ctx = EGL_NO_CONTEXT; - // FIXME: unlock/disconnect the read surface too - } - } - } - return EGL_TRUE; - } - return setError(EGL_BAD_ACCESS, EGL_FALSE); -} - -EGLContext eglGetCurrentContext(void) -{ - // eglGetCurrentContext returns the current EGL rendering context, - // as specified by eglMakeCurrent. If no context is current, - // EGL_NO_CONTEXT is returned. - return (EGLContext)getGlThreadSpecific(); -} - -EGLSurface eglGetCurrentSurface(EGLint readdraw) -{ - // eglGetCurrentSurface returns the read or draw surface attached - // to the current EGL rendering context, as specified by eglMakeCurrent. - // If no context is current, EGL_NO_SURFACE is returned. - EGLContext ctx = (EGLContext)getGlThreadSpecific(); - if (ctx == EGL_NO_CONTEXT) return EGL_NO_SURFACE; - egl_context_t* c = egl_context_t::context(ctx); - if (readdraw == EGL_READ) { - return c->read; - } else if (readdraw == EGL_DRAW) { - return c->draw; - } - return setError(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE); -} - -EGLDisplay eglGetCurrentDisplay(void) -{ - // eglGetCurrentDisplay returns the current EGL display connection - // for the current EGL rendering context, as specified by eglMakeCurrent. - // If no context is current, EGL_NO_DISPLAY is returned. - EGLContext ctx = (EGLContext)getGlThreadSpecific(); - if (ctx == EGL_NO_CONTEXT) return EGL_NO_DISPLAY; - egl_context_t* c = egl_context_t::context(ctx); - return c->dpy; -} - -EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx, - EGLint attribute, EGLint *value) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - egl_context_t* c = egl_context_t::context(ctx); - switch (attribute) { - case EGL_CONFIG_ID: - // Returns the ID of the EGL frame buffer configuration with - // respect to which the context was created - return getConfigAttrib(dpy, c->config, EGL_CONFIG_ID, value); - } - return setError(EGL_BAD_ATTRIBUTE, EGL_FALSE); -} - -EGLBoolean eglWaitGL(void) -{ - return EGL_TRUE; -} - -EGLBoolean eglWaitNative(EGLint engine) -{ - return EGL_TRUE; -} - -EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - egl_surface_t* d = static_cast<egl_surface_t*>(draw); - if (!d->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (d->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - // post the surface - d->swapBuffers(); - - // if it's bound to a context, update the buffer - if (d->ctx != EGL_NO_CONTEXT) { - d->bindDrawSurface((GLES2Context*)d->ctx); - // if this surface is also the read surface of the context - // it is bound to, make sure to update the read buffer as well. - // The EGL spec is a little unclear about this. - egl_context_t* c = egl_context_t::context(d->ctx); - if (c->read == draw) { - d->bindReadSurface((GLES2Context*)d->ctx); - } - clock_t time = clock(); - float elapsed = (float)(time - c->lastSwapTime) / CLOCKS_PER_SEC; - c->accumulateSeconds += elapsed; - c->frame++; - // LOGD("agl2: eglSwapBuffers elapsed=%.2fms \n*", elapsed * 1000); - if (20 == c->frame) { - float avg = c->accumulateSeconds / c->frame; - LOGD("\n*\n* agl2: eglSwapBuffers %u frame avg fps=%.1f elapsed=%.2fms \n*", - c->frame, 1 / avg, avg * 1000); - c->frame = 0; - c->accumulateSeconds = 0; - } - c->lastSwapTime = time; - } - - return EGL_TRUE; -} - -EGLBoolean eglCopyBuffers( EGLDisplay dpy, EGLSurface surface, - NativePixmapType target) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglCopyBuffers() - return EGL_FALSE; -} - -EGLint eglGetError(void) -{ - return getError(); -} - -const char* eglQueryString(EGLDisplay dpy, EGLint name) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, (const char*)0); - - switch (name) { - case EGL_VENDOR: - return gVendorString; - case EGL_VERSION: - return gVersionString; - case EGL_EXTENSIONS: - return gExtensionsString; - case EGL_CLIENT_APIS: - return gClientApiString; - } - return setError(EGL_BAD_PARAMETER, (const char *)0); -} - -// ---------------------------------------------------------------------------- -// EGL 1.1 -// ---------------------------------------------------------------------------- - -EGLBoolean eglSurfaceAttrib( - EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglSurfaceAttrib() - return setError(EGL_BAD_PARAMETER, EGL_FALSE); -} - -EGLBoolean eglBindTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglBindTexImage() - return setError(EGL_BAD_PARAMETER, EGL_FALSE); -} - -EGLBoolean eglReleaseTexImage( - EGLDisplay dpy, EGLSurface surface, EGLint buffer) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglReleaseTexImage() - return setError(EGL_BAD_PARAMETER, EGL_FALSE); -} - -EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - // TODO: eglSwapInterval() - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- -// EGL 1.2 -// ---------------------------------------------------------------------------- - -EGLBoolean eglBindAPI(EGLenum api) -{ - if (api != EGL_OPENGL_ES_API) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - return EGL_TRUE; -} - -EGLenum eglQueryAPI(void) -{ - return EGL_OPENGL_ES_API; -} - -EGLBoolean eglWaitClient(void) -{ - glFinish(); - return EGL_TRUE; -} - -EGLBoolean eglReleaseThread(void) -{ - // TODO: eglReleaseThread() - return EGL_TRUE; -} - -EGLSurface eglCreatePbufferFromClientBuffer( - EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, - EGLConfig config, const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_NO_SURFACE); - // TODO: eglCreatePbufferFromClientBuffer() - return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE); -} - -// ---------------------------------------------------------------------------- -// EGL_EGLEXT_VERSION 3 -// ---------------------------------------------------------------------------- - -void (*eglGetProcAddress (const char *procname))() - { - extention_map_t const * const map = gExtentionMap; - for (uint32_t i=0 ; i<NELEM(gExtentionMap) ; i++) { - if (!strcmp(procname, map[i].name)) { - return map[i].address; - } - } - return NULL; - } - -EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface, - const EGLint *attrib_list) -{ - EGLBoolean result = EGL_FALSE; - return result; -} - -EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface) -{ - EGLBoolean result = EGL_FALSE; - return result; -} - -EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, - EGLClientBuffer buffer, const EGLint *attrib_list) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) { - return setError(EGL_BAD_DISPLAY, EGL_NO_IMAGE_KHR); - } - if (ctx != EGL_NO_CONTEXT) { - return setError(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR); - } - if (target != EGL_NATIVE_BUFFER_ANDROID) { - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - } - - ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)buffer; - - if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - - if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - - switch (native_buffer->format) { - case HAL_PIXEL_FORMAT_RGBA_8888: - case HAL_PIXEL_FORMAT_RGBX_8888: - case HAL_PIXEL_FORMAT_RGB_888: - case HAL_PIXEL_FORMAT_RGB_565: - case HAL_PIXEL_FORMAT_BGRA_8888: - case HAL_PIXEL_FORMAT_RGBA_5551: - case HAL_PIXEL_FORMAT_RGBA_4444: - break; - default: - return setError(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); - } - - native_buffer->common.incRef(&native_buffer->common); - return (EGLImageKHR)native_buffer; -} - -EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) { - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - } - - ANativeWindowBuffer* native_buffer = (ANativeWindowBuffer*)img; - - if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - - if (native_buffer->common.version != sizeof(ANativeWindowBuffer)) - return setError(EGL_BAD_PARAMETER, EGL_FALSE); - - native_buffer->common.decRef(&native_buffer->common); - - return EGL_TRUE; -} - -// ---------------------------------------------------------------------------- -// ANDROID extensions -// ---------------------------------------------------------------------------- - -EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, - EGLint left, EGLint top, EGLint width, EGLint height) -{ - if (egl_display_t::is_valid(dpy) == EGL_FALSE) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - egl_surface_t* d = static_cast<egl_surface_t*>(draw); - if (!d->isValid()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - if (d->dpy != dpy) - return setError(EGL_BAD_DISPLAY, EGL_FALSE); - - // post the surface - d->setSwapRectangle(left, top, width, height); - - return EGL_TRUE; -} diff --git a/opengl/libagl2/src/get.cpp b/opengl/libagl2/src/get.cpp deleted file mode 100644 index 13c28ce012dc..000000000000 --- a/opengl/libagl2/src/get.cpp +++ /dev/null @@ -1,79 +0,0 @@ -#include "gles2context.h" - -static char const * const gVendorString = "Android"; -static char const * const gRendererString = "Android PixelFlinger2 0.0"; -static char const * const gVersionString = "OpenGL ES 2.0"; -static char const * const gExtensionsString = -// "GL_OES_byte_coordinates " // OK -// "GL_OES_fixed_point " // OK -// "GL_OES_single_precision " // OK -// "GL_OES_read_format " // OK -// "GL_OES_compressed_paletted_texture " // OK -// "GL_OES_draw_texture " // OK -// "GL_OES_matrix_get " // OK -// "GL_OES_query_matrix " // OK -// // "GL_OES_point_size_array " // TODO -// // "GL_OES_point_sprite " // TODO -// "GL_OES_EGL_image " // OK -//#ifdef GL_OES_compressed_ETC1_RGB8_texture -// "GL_OES_compressed_ETC1_RGB8_texture " // OK -//#endif -// "GL_ARB_texture_compression " // OK -// "GL_ARB_texture_non_power_of_two " // OK -// "GL_ANDROID_user_clip_plane " // OK -// "GL_ANDROID_vertex_buffer_object " // OK -// "GL_ANDROID_generate_mipmap " // OK - "" - ; - -void glGetIntegerv(GLenum pname, GLint* params) -{ - switch (pname) { - case GL_MAX_TEXTURE_SIZE : - *params = 4096; // limit is in precision of texcoord calculation, which uses 16.16 - break; - case GL_MAX_VERTEX_ATTRIBS: - *params = GGL_MAXVERTEXATTRIBS; - break; - case GL_MAX_VERTEX_UNIFORM_VECTORS: - *params = GGL_MAXVERTEXUNIFORMVECTORS; - break; - case GL_MAX_VARYING_VECTORS: - *params = GGL_MAXVARYINGVECTORS; - break; - case GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS: - *params = GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; - break; - case GL_MAX_VERTEX_TEXTURE_IMAGE_UNITS: - *params = GGL_MAXVERTEXTEXTUREIMAGEUNITS; - break; - case GL_MAX_TEXTURE_IMAGE_UNITS: - *params = GGL_MAXTEXTUREIMAGEUNITS; - break; - case GL_MAX_FRAGMENT_UNIFORM_VECTORS: - *params = GGL_MAXFRAGMENTUNIFORMVECTORS; - break; - case GL_ALIASED_LINE_WIDTH_RANGE: - *params = 1; // TODO: not implemented - break; - default: - LOGD("agl2: glGetIntegerv 0x%.4X", pname); - assert(0); - } -} - -const GLubyte* glGetString(GLenum name) -{ - switch (name) { - case GL_VENDOR: - return (const GLubyte*)gVendorString; - case GL_RENDERER: - return (const GLubyte*)gRendererString; - case GL_VERSION: - return (const GLubyte*)gVersionString; - case GL_EXTENSIONS: - return (const GLubyte*)gExtensionsString; - } - assert(0); //(c, GL_INVALID_ENUM); - return 0; -} diff --git a/opengl/libagl2/src/gles2context.h b/opengl/libagl2/src/gles2context.h deleted file mode 100644 index cec0340303f3..000000000000 --- a/opengl/libagl2/src/gles2context.h +++ /dev/null @@ -1,166 +0,0 @@ -#define _SIZE_T_DEFINED_ -typedef unsigned int size_t; - -#include <stdio.h> -#include <stdlib.h> - -#include <EGL/egl.h> -#include <EGL/eglext.h> -#include <GLES/gl.h> -#include <GLES/glext.h> - -#include <utils/threads.h> -#include <pthread.h> - -#include <cutils/log.h> - -#include <assert.h> - -#ifdef __arm__ -#ifndef __location__ -#define __HIERALLOC_STRING_0__(s) #s -#define __HIERALLOC_STRING_1__(s) __HIERALLOC_STRING_0__(s) -#define __HIERALLOC_STRING_2__ __HIERALLOC_STRING_1__(__LINE__) -#define __location__ __FILE__ ":" __HIERALLOC_STRING_2__ -#endif -#undef assert -#define assert(EXPR) { do { if (!(EXPR)) {LOGD("\n*\n*\n*\n* assert fail: '"#EXPR"' at "__location__"\n*\n*\n*\n*"); exit(EXIT_FAILURE); } } while (false); } -//#define printf LOGD -#else // #ifdef __arm__ -//#define LOGD printf -#endif // #ifdef __arm__ - - -#include <pixelflinger2/pixelflinger2_format.h> -#include <pixelflinger2/pixelflinger2.h> - -#include <map> - -typedef uint8_t GGLubyte; // ub - -#define ggl_likely(x) __builtin_expect(!!(x), 1) -#define ggl_unlikely(x) __builtin_expect(!!(x), 0) - -#undef NELEM -#define NELEM(x) (sizeof(x)/sizeof(*(x))) - -template<typename T> -inline T max(T a, T b) -{ - return a<b ? b : a; -} - -template<typename T> -inline T min(T a, T b) -{ - return a<b ? a : b; -} - -struct egl_context_t { - enum { - IS_CURRENT = 0x00010000, - NEVER_CURRENT = 0x00020000 - }; - uint32_t flags; - EGLDisplay dpy; - EGLConfig config; - EGLSurface read; - EGLSurface draw; - - unsigned frame; - clock_t lastSwapTime; - float accumulateSeconds; - - static inline egl_context_t* context(EGLContext ctx); -}; - -struct GLES2Context; - -#ifdef HAVE_ANDROID_OS -#include <bionic_tls.h> -// We have a dedicated TLS slot in bionic -inline void setGlThreadSpecific(GLES2Context *value) -{ - ((uint32_t *)__get_tls())[TLS_SLOT_OPENGL] = (uint32_t)value; -} -inline GLES2Context* getGlThreadSpecific() -{ - return (GLES2Context *)(((unsigned *)__get_tls())[TLS_SLOT_OPENGL]); -} -#else -extern pthread_key_t gGLKey; -inline void setGlThreadSpecific(GLES2Context *value) -{ - pthread_setspecific(gGLKey, value); -} -inline GLES2Context* getGlThreadSpecific() -{ - return static_cast<GLES2Context*>(pthread_getspecific(gGLKey)); -} -#endif - -struct VBO { - unsigned size; - GLenum usage; - void * data; -}; - -struct GLES2Context { - GGLContext rasterizer; - egl_context_t egl; - GGLInterface * iface; // shortcut to &rasterizer.interface - - struct VertexState { - struct VertAttribPointer { - unsigned size; // number of values per vertex - GLenum type; // data type - unsigned stride; // bytes - const void * ptr; -bool normalized : - 1; -bool enabled : - 1; - } attribs [GGL_MAXVERTEXATTRIBS]; - - VBO * vbo, * indices; - std::map<GLuint, VBO *> vbos; - GLuint free; - - Vector4 defaultAttribs [GGL_MAXVERTEXATTRIBS]; - } vert; - - struct TextureState { - GGLTexture * tmus[GGL_MAXCOMBINEDTEXTUREIMAGEUNITS]; - int sampler2tmu[GGL_MAXCOMBINEDTEXTUREIMAGEUNITS]; // sampler2tmu[sampler] is index of tmu, -1 means not used - unsigned active; - std::map<GLuint, GGLTexture *> textures; - GLuint free; // first possible free name - GGLTexture * tex2D, * texCube; // default textures - unsigned unpack; - - void UpdateSampler(GGLInterface * iface, unsigned tmu); - } tex; - - GLES2Context(); - void InitializeTextures(); - void InitializeVertices(); - - ~GLES2Context(); - void UninitializeTextures(); - void UninitializeVertices(); - - static inline GLES2Context* get() { - return getGlThreadSpecific(); - } -}; - -inline egl_context_t* egl_context_t::context(EGLContext ctx) -{ - GLES2Context* const gl = static_cast<GLES2Context*>(ctx); - return static_cast<egl_context_t*>(&gl->egl); -} - -#define GLES2_GET_CONTEXT(ctx) GLES2Context * ctx = GLES2Context::get(); \ - /*puts(__FUNCTION__);*/ -#define GLES2_GET_CONST_CONTEXT(ctx) GLES2Context * ctx = GLES2Context::get(); \ - /*puts(__FUNCTION__);*/ diff --git a/opengl/libagl2/src/shader.cpp b/opengl/libagl2/src/shader.cpp deleted file mode 100644 index 076e3881711d..000000000000 --- a/opengl/libagl2/src/shader.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "gles2context.h" - -//#undef LOGD -//#define LOGD(...) - -static inline GLuint s2n(gl_shader * s) -{ - return (GLuint)s ^ 0xaf3c532d; -} - -static inline gl_shader * n2s(GLuint n) -{ - return (gl_shader *)(n ^ 0xaf3c532d); -} - -static inline GLuint p2n(gl_shader_program * p) -{ - return (GLuint)p ^ 0x04dc18f9; -} - -static inline gl_shader_program * n2p(GLuint n) -{ - return (gl_shader_program *)(n ^ 0x04dc18f9); -} - -void glAttachShader(GLuint program, GLuint shader) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderAttach(ctx->iface, n2p(program), n2s(shader)); -} - -void glBindAttribLocation(GLuint program, GLuint index, const GLchar* name) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderAttributeBind(n2p(program), index, name); -// assert(0); -} - -GLuint glCreateShader(GLenum type) -{ - GLES2_GET_CONST_CONTEXT(ctx); - return s2n(ctx->iface->ShaderCreate(ctx->iface, type)); -} - -GLuint glCreateProgram(void) -{ - GLES2_GET_CONST_CONTEXT(ctx); - return p2n(ctx->iface->ShaderProgramCreate(ctx->iface)); -} - -void glCompileShader(GLuint shader) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderCompile(ctx->iface, n2s(shader), NULL, NULL); -} - -void glDeleteProgram(GLuint program) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderProgramDelete(ctx->iface, n2p(program)); -} - -void glDeleteShader(GLuint shader) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderDelete(ctx->iface, n2s(shader)); -} - -void glDetachShader(GLuint program, GLuint shader) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderDetach(ctx->iface, n2p(program), n2s(shader)); -} - -GLint glGetAttribLocation(GLuint program, const GLchar* name) -{ - GLES2_GET_CONST_CONTEXT(ctx); - GLint location = ctx->iface->ShaderAttributeLocation(n2p(program), name); -// LOGD("\n*\n*\n* agl2: glGetAttribLocation program=%u name=%s location=%d \n*\n*", -// program, name, location); - return location; -} - -void glGetProgramiv(GLuint program, GLenum pname, GLint* params) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderProgramGetiv(n2p(program), pname, params); - LOGD("agl2: glGetProgramiv 0x%.4X=%d \n", pname, *params); -} - -void glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* length, GLchar* infolog) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderProgramGetInfoLog(n2p(program), bufsize, length, infolog); -} - -void glGetShaderiv(GLuint shader, GLenum pname, GLint* params) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderGetiv(n2s(shader), pname, params); - LOGD("agl2: glGetShaderiv 0x%.4X=%d \n", pname, *params); -} - -void glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, GLchar* infolog) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderGetInfoLog(n2s(shader), bufsize, length, infolog); -} - -int glGetUniformLocation(GLuint program, const GLchar* name) -{ - GLES2_GET_CONST_CONTEXT(ctx); - return ctx->iface->ShaderUniformLocation(n2p(program), name); -} - -void glLinkProgram(GLuint program) -{ - GLES2_GET_CONST_CONTEXT(ctx); - GLboolean linked = ctx->iface->ShaderProgramLink(n2p(program), NULL); - assert(linked); -} - -void glShaderSource(GLuint shader, GLsizei count, const GLchar** string, const GLint* length) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ShaderSource(n2s(shader), count, string, length); -} - -void glUniform1f(GLint location, GLfloat x) -{ - GLES2_GET_CONST_CONTEXT(ctx); - int sampler = ctx->iface->ShaderUniform(ctx->rasterizer.CurrentProgram, location, 1, &x, GL_FLOAT); - assert(0 > sampler); // should be assigning to sampler -} - -void glUniform1i(GLint location, GLint x) -{ - GLES2_GET_CONST_CONTEXT(ctx); - const float params[1] = {x}; - int sampler = ctx->iface->ShaderUniform(ctx->rasterizer.CurrentProgram, location, 1, params, GL_INT); - if (0 <= sampler) { -// LOGD("\n*\n* agl2: glUniform1i updated sampler=%d tmu=%d location=%d\n*", sampler, x, location); - assert(0 <= x && GGL_MAXCOMBINEDTEXTUREIMAGEUNITS > x); -// LOGD("tmu%u: format=0x%.2X w=%u h=%u levels=%p", x, ctx->tex.tmus[x]->format, -// ctx->tex.tmus[x]->width, ctx->tex.tmus[x]->height, ctx->tex.tmus[x]->format); - ctx->tex.sampler2tmu[sampler] = x; - ctx->tex.UpdateSampler(ctx->iface, x); - } -} - -void glUniform2f(GLint location, GLfloat x, GLfloat y) -{ - GLES2_GET_CONST_CONTEXT(ctx); - const float params[4] = {x, y}; - ctx->iface->ShaderUniform(ctx->rasterizer.CurrentProgram, location, 1, params, GL_FLOAT_VEC2); -} - -void glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - GLES2_GET_CONST_CONTEXT(ctx); - const float params[4] = {x, y, z, w}; -// LOGD("agl2: glUniform4f location=%d %f,%f,%f,%f", location, x, y, z, w); - ctx->iface->ShaderUniform(ctx->rasterizer.CurrentProgram, location, 1, params, GL_FLOAT_VEC4); -} - -void glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpose, const GLfloat* value) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// const gl_shader_program * program = ctx->rasterizer.CurrentProgram; -// if (strstr(program->Shaders[MESA_SHADER_FRAGMENT]->Source, ").a;")) { -// LOGD("agl2: glUniformMatrix4fv location=%d count=%d transpose=%d", location, count, transpose); -// for (unsigned i = 0; i < 4; i++) -// LOGD("agl2: glUniformMatrix4fv %.2f \t %.2f \t %.2f \t %.2f", value[i * 4 + 0], -// value[i * 4 + 1], value[i * 4 + 2], value[i * 4 + 3]); -// } - ctx->iface->ShaderUniformMatrix(ctx->rasterizer.CurrentProgram, 4, 4, location, count, transpose, value); -// while (true) -// ; -// assert(0); -} - -void glUseProgram(GLuint program) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("\n*\n*\n* agl2: glUseProgram %d \n*\n*\n*", program); - ctx->iface->ShaderUse(ctx->iface, n2p(program)); - ctx->iface->ShaderUniformGetSamplers(n2p(program), ctx->tex.sampler2tmu); - for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++) - if (0 <= ctx->tex.sampler2tmu[i]) - ctx->iface->SetSampler(ctx->iface, i, ctx->tex.tmus[ctx->tex.sampler2tmu[i]]); -} diff --git a/opengl/libagl2/src/state.cpp b/opengl/libagl2/src/state.cpp deleted file mode 100644 index 22e73fadd56a..000000000000 --- a/opengl/libagl2/src/state.cpp +++ /dev/null @@ -1,129 +0,0 @@ -#include "gles2context.h" - -GLES2Context::GLES2Context() -{ - memset(this, 0, sizeof *this); - - assert((void *)&rasterizer == &rasterizer.interface); - InitializeGGLState(&rasterizer.interface); - iface = &rasterizer.interface; - printf("gl->rasterizer.PickScanLine(%p) = %p \n", &rasterizer.PickScanLine, rasterizer.PickScanLine); - assert(rasterizer.PickRaster); - assert(rasterizer.PickScanLine); - - InitializeTextures(); - InitializeVertices(); -} - -GLES2Context::~GLES2Context() -{ - UninitializeTextures(); - UninitializeVertices(); - UninitializeGGLState(&rasterizer.interface); -} - -void glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->BlendColor(ctx->iface, red, green, blue, alpha); -} - -void glBlendEquation( GLenum mode ) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->BlendEquationSeparate(ctx->iface, mode, mode); -} - -void glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->BlendEquationSeparate(ctx->iface, modeRGB, modeAlpha); -} - -void glBlendFunc(GLenum sfactor, GLenum dfactor) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->BlendFuncSeparate(ctx->iface, sfactor, dfactor, sfactor, dfactor); -} - -void glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->BlendFuncSeparate(ctx->iface, srcRGB, dstRGB, srcAlpha, dstAlpha); -} - -void glClear(GLbitfield mask) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->Clear(ctx->iface, mask); -} - -void glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ClearColor(ctx->iface, red, green, blue, alpha); -} - -void glClearDepthf(GLclampf depth) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ClearDepthf(ctx->iface, depth); -} - -void glClearStencil(GLint s) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->ClearStencil(ctx->iface, s); -} - -void glCullFace(GLenum mode) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->CullFace(ctx->iface, mode); -} - -void glDisable(GLenum cap) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->EnableDisable(ctx->iface, cap, false); -} - -void glEnable(GLenum cap) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->EnableDisable(ctx->iface, cap, true); -} - -void glFinish(void) -{ - // do nothing -} - -void glFrontFace(GLenum mode) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->iface->FrontFace(ctx->iface, mode); -} - -void glFlush(void) -{ - // do nothing -} - -void glHint(GLenum target, GLenum mode) -{ - // do nothing -} - -void glScissor(GLint x, GLint y, GLsizei width, GLsizei height) -{ -// LOGD("agl2: glScissor not implemented x=%d y=%d width=%d height=%d", x, y, width, height); - //CALL_GL_API(glScissor, x, y, width, height); -} - -void glViewport(GLint x, GLint y, GLsizei width, GLsizei height) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glViewport x=%d y=%d width=%d height=%d", x, y, width, height); - ctx->iface->Viewport(ctx->iface, x, y, width, height); -} diff --git a/opengl/libagl2/src/texture.cpp b/opengl/libagl2/src/texture.cpp deleted file mode 100644 index 4de1f1664e81..000000000000 --- a/opengl/libagl2/src/texture.cpp +++ /dev/null @@ -1,534 +0,0 @@ -#include "gles2context.h" - -//#undef LOGD -//#define LOGD(...) - -#define API_ENTRY -#define CALL_GL_API(NAME,...) LOGD("?"#NAME); assert(0); -#define CALL_GL_API_RETURN(NAME,...) LOGD("?"#NAME); assert(0); return 0; - -static inline GGLTexture * AllocTexture() -{ - GGLTexture * tex = (GGLTexture *)calloc(1, sizeof(GGLTexture)); - tex->minFilter = GGLTexture::GGL_LINEAR; // should be NEAREST_ MIPMAP_LINEAR - tex->magFilter = GGLTexture::GGL_LINEAR; - return tex; -} - -void GLES2Context::InitializeTextures() -{ - tex.textures = std::map<GLuint, GGLTexture *>(); // the entire struct has been zeroed in constructor - tex.tex2D = AllocTexture(); - tex.textures[GL_TEXTURE_2D] = tex.tex2D; - tex.texCube = AllocTexture(); - tex.textures[GL_TEXTURE_CUBE_MAP] = tex.texCube; - for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++) { - tex.tmus[i] = NULL; - tex.sampler2tmu[i] = NULL; - } - - tex.active = 0; - - tex.free = max(GL_TEXTURE_2D, GL_TEXTURE_CUBE_MAP) + 1; - - tex.tex2D->format = GGL_PIXEL_FORMAT_RGBA_8888; - tex.tex2D->type = GL_TEXTURE_2D; - tex.tex2D->levelCount = 1; - tex.tex2D->wrapS = tex.tex2D->wrapT = GGLTexture::GGL_REPEAT; - tex.tex2D->minFilter = tex.tex2D->magFilter = GGLTexture::GGL_NEAREST; - tex.tex2D->width = tex.tex2D->height = 1; - tex.tex2D->levels = malloc(4); - *(unsigned *)tex.tex2D->levels = 0xff000000; - - - tex.texCube->format = GGL_PIXEL_FORMAT_RGBA_8888; - tex.texCube->type = GL_TEXTURE_CUBE_MAP; - tex.texCube->levelCount = 1; - tex.texCube->wrapS = tex.texCube->wrapT = GGLTexture::GGL_REPEAT; - tex.texCube->minFilter = tex.texCube->magFilter = GGLTexture::GGL_NEAREST; - tex.texCube->width = tex.texCube->height = 1; - tex.texCube->levels = malloc(4 * 6); - static unsigned texels [6] = {0xff0000ff, 0xff00ff00, 0xffff0000, - 0xff00ffff, 0xffffff00, 0xffff00ff - }; - memcpy(tex.texCube->levels, texels, sizeof texels); - - //texture.levelCount = GenerateMipmaps(texture.levels, texture.width, texture.height); - - // static unsigned texels [6] = {0xff0000ff, 0xff00ff00, 0xffff0000, - // 0xff00ffff, 0xffffff00, 0xffff00ff}; - // memcpy(texture.levels[0], texels, sizeof texels); - // texture.format = GGL_PIXEL_FORMAT_RGBA_8888; - // texture.width = texture.height = 1; - //texture.height /= 6; - //texture.type = GL_TEXTURE_CUBE_MAP; - - tex.unpack = 4; -} - -void GLES2Context::TextureState::UpdateSampler(GGLInterface * iface, unsigned tmu) -{ - for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++) - if (tmu == sampler2tmu[i]) - iface->SetSampler(iface, i, tmus[tmu]); -} - -void GLES2Context::UninitializeTextures() -{ - for (std::map<GLuint, GGLTexture *>::iterator it = tex.textures.begin(); it != tex.textures.end(); it++) { - if (!it->second) - continue; - free(it->second->levels); - free(it->second); - } -} - -static inline void GetFormatAndBytesPerPixel(const GLenum format, unsigned * bytesPerPixel, - GGLPixelFormat * texFormat) -{ - switch (format) { - case GL_ALPHA: - *texFormat = GGL_PIXEL_FORMAT_A_8; - *bytesPerPixel = 1; - break; - case GL_LUMINANCE: - *texFormat = GGL_PIXEL_FORMAT_L_8; - *bytesPerPixel = 1; - break; - case GL_LUMINANCE_ALPHA: - *texFormat = GGL_PIXEL_FORMAT_LA_88; - *bytesPerPixel = 2; - break; - case GL_RGB: - *texFormat = GGL_PIXEL_FORMAT_RGB_888; - *bytesPerPixel = 3; - break; - case GL_RGBA: - *texFormat = GGL_PIXEL_FORMAT_RGBA_8888; - *bytesPerPixel = 4; - break; - - // internal formats to avoid conversion - case GL_UNSIGNED_SHORT_5_6_5: - *texFormat = GGL_PIXEL_FORMAT_RGB_565; - *bytesPerPixel = 2; - break; - - default: - assert(0); - return; - } -} - -static inline void CopyTexture(char * dst, const char * src, const unsigned bytesPerPixel, - const unsigned sx, const unsigned sy, const unsigned sw, - const unsigned dx, const unsigned dy, const unsigned dw, - const unsigned w, const unsigned h) -{ - const unsigned bpp = bytesPerPixel; - if (dw == sw && dw == w && sx == 0 && dx == 0) - memcpy(dst + dy * dw * bpp, src + sy * sw * bpp, w * h * bpp); - else - for (unsigned y = 0; y < h; y++) - memcpy(dst + ((dy + y) * dw + dx) * bpp, src + ((sy + y) * sw + sx) * bpp, w * bpp); -} - -void glActiveTexture(GLenum texture) -{ - GLES2_GET_CONST_CONTEXT(ctx); - unsigned index = texture - GL_TEXTURE0; - assert(NELEM(ctx->tex.tmus) > index); -// LOGD("agl2: glActiveTexture %u", index); - ctx->tex.active = index; -} - -void glBindTexture(GLenum target, GLuint texture) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glBindTexture target=0x%.4X texture=%u active=%u", target, texture, ctx->tex.active); - std::map<GLuint, GGLTexture *>::iterator it = ctx->tex.textures.find(texture); - GGLTexture * tex = NULL; - if (it != ctx->tex.textures.end()) { - tex = it->second; - if (!tex) { - tex = AllocTexture(); - tex->type = target; - it->second = tex; -// LOGD("agl2: glBindTexture allocTexture"); - } -// else -// LOGD("agl2: glBindTexture bind existing texture"); - assert(target == tex->type); - } else if (0 == texture) { - if (GL_TEXTURE_2D == target) - { - tex = ctx->tex.tex2D; -// LOGD("agl2: glBindTexture bind default tex2D"); - } - else if (GL_TEXTURE_CUBE_MAP == target) - { - tex = ctx->tex.texCube; -// LOGD("agl2: glBindTexture bind default texCube"); - } - else - assert(0); - } else { - if (texture <= ctx->tex.free) - ctx->tex.free = texture + 1; - tex = AllocTexture(); - tex->type = target; - ctx->tex.textures[texture] = tex; -// LOGD("agl2: glBindTexture new texture=%u", texture); - } - ctx->tex.tmus[ctx->tex.active] = tex; -// LOGD("agl2: glBindTexture format=0x%.2X w=%u h=%u levels=%p", tex->format, -// tex->width, tex->height, tex->levels); - ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active); -} - -void API_ENTRY(glCompressedTexImage2D)(GLenum target, GLint level, GLenum internalformat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, const GLvoid* data) -{ - CALL_GL_API(glCompressedTexImage2D, target, level, internalformat, width, height, border, imageSize, data); -} - -void API_ENTRY(glCompressedTexSubImage2D)(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLsizei imageSize, const GLvoid* data) -{ - CALL_GL_API(glCompressedTexSubImage2D, target, level, xoffset, yoffset, width, height, format, imageSize, data); -} - -void glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, - GLint x, GLint y, GLsizei width, GLsizei height, GLint border) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glCopyTexImage2D target=0x%.4X internalformat=0x%.4X", target, internalformat); -// LOGD("x=%d y=%d width=%d height=%d border=%d level=%d ", x, y, width, height, border, level); - assert(0 == border); - assert(0 == level); - unsigned bytesPerPixel = 0; - GGLPixelFormat texFormat = GGL_PIXEL_FORMAT_UNKNOWN; - GetFormatAndBytesPerPixel(internalformat, &bytesPerPixel, &texFormat); - - assert(texFormat == ctx->rasterizer.frameSurface.format); -// LOGD("texFormat=0x%.2X bytesPerPixel=%d \n", texFormat, bytesPerPixel); - unsigned offset = 0, size = width * height * bytesPerPixel, totalSize = size; - - assert(ctx->tex.tmus[ctx->tex.active]); - assert(y + height <= ctx->rasterizer.frameSurface.height); - assert(x + width <= ctx->rasterizer.frameSurface.width); - GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active]; - tex.width = width; - tex.height = height; - tex.levelCount = 1; - tex.format = texFormat; - switch (target) { - case GL_TEXTURE_2D: - tex.levels = realloc(tex.levels, totalSize); - CopyTexture((char *)tex.levels, (const char *)ctx->rasterizer.frameSurface.data, bytesPerPixel, - x, y, ctx->rasterizer.frameSurface.width, 0, 0, width, width, height); - break; - default: - assert(0); - return; - } - ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active); -} - -void glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height) -{ - // x, y are src offset - // xoffset and yoffset are dst offset - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glCopyTexSubImage2D target=0x%.4X level=%d", target, level); -// LOGD("xoffset=%d yoffset=%d x=%d y=%d width=%d height=%d", xoffset, yoffset, x, y, width, height); - assert(0 == level); - - unsigned bytesPerPixel = 4; - unsigned offset = 0, size = width * height * bytesPerPixel, totalSize = size; - - assert(ctx->tex.tmus[ctx->tex.active]); - GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active]; - - assert(tex.format == ctx->rasterizer.frameSurface.format); - assert(GGL_PIXEL_FORMAT_RGBA_8888 == tex.format); - - const unsigned srcWidth = ctx->rasterizer.frameSurface.width; - const unsigned srcHeight = ctx->rasterizer.frameSurface.height; - - assert(x >= 0 && y >= 0); - assert(xoffset >= 0 && yoffset >= 0); - assert(x + width <= srcWidth); - assert(y + height <= srcHeight); - assert(xoffset + width <= tex.width); - assert(yoffset + height <= tex.height); - - switch (target) { - case GL_TEXTURE_2D: - CopyTexture((char *)tex.levels, (const char *)ctx->rasterizer.frameSurface.data, bytesPerPixel, - x, y, srcWidth, xoffset, yoffset, tex.width, width, height); - break; - default: - assert(0); - return; - } - ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active); -} - -void glDeleteTextures(GLsizei n, const GLuint* textures) -{ - GLES2_GET_CONST_CONTEXT(ctx); - for (unsigned i = 0; i < n; i++) { - std::map<GLuint, GGLTexture *>::iterator it = ctx->tex.textures.find(textures[i]); - if (it == ctx->tex.textures.end()) - continue; - ctx->tex.free = min(ctx->tex.free, textures[i]); - for (unsigned i = 0; i < GGL_MAXCOMBINEDTEXTUREIMAGEUNITS; i++) - if (ctx->tex.tmus[i] == it->second) { - if (GL_TEXTURE_2D == it->second->type) - ctx->tex.tmus[i] = ctx->tex.tex2D; - else if (GL_TEXTURE_CUBE_MAP == it->second->type) - ctx->tex.tmus[i] = ctx->tex.texCube; - else - assert(0); - ctx->tex.UpdateSampler(ctx->iface, i); - } - if (it->second) { - free(it->second->levels); - free(it->second); - } - ctx->tex.textures.erase(it); - } -} - -void glGenTextures(GLsizei n, GLuint* textures) -{ - GLES2_GET_CONST_CONTEXT(ctx); - for (unsigned i = 0; i < n; i++) { - textures[i] = 0; - for (ctx->tex.free; ctx->tex.free < 0xffffffffu; ctx->tex.free++) - if (ctx->tex.textures.find(ctx->tex.free) == ctx->tex.textures.end()) { - ctx->tex.textures[ctx->tex.free] = NULL; - textures[i] = ctx->tex.free; - ctx->tex.free++; - break; - } - assert(textures[i]); - } -} - -void API_ENTRY(glGetTexParameterfv)(GLenum target, GLenum pname, GLfloat* params) -{ - CALL_GL_API(glGetTexParameterfv, target, pname, params); -} -void API_ENTRY(glGetTexParameteriv)(GLenum target, GLenum pname, GLint* params) -{ - CALL_GL_API(glGetTexParameteriv, target, pname, params); -} - -GLboolean glIsTexture(GLuint texture) -{ - GLES2_GET_CONST_CONTEXT(ctx); - if (ctx->tex.textures.find(texture) == ctx->tex.textures.end()) - return GL_FALSE; - else - return GL_TRUE; -} - -void glPixelStorei(GLenum pname, GLint param) -{ - GLES2_GET_CONST_CONTEXT(ctx); - assert(GL_UNPACK_ALIGNMENT == pname); - assert(1 == param || 2 == param || 4 == param || 8 == param); -// LOGD("\n*\n* agl2: glPixelStorei not implemented pname=0x%.4X param=%d \n*", pname, param); - ctx->tex.unpack = param; -// CALL_GL_API(glPixelStorei, pname, param); -} -void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, - GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glTexImage2D internalformat=0x%.4X format=0x%.4X type=0x%.4X \n", internalformat, format, type); -// LOGD("width=%d height=%d border=%d level=%d pixels=%p \n", width, height, border, level, pixels); - switch (type) { - case GL_UNSIGNED_BYTE: - break; - case GL_UNSIGNED_SHORT_5_6_5: - internalformat = format = GL_UNSIGNED_SHORT_5_6_5; - assert(4 == ctx->tex.unpack); - break; - default: - assert(0); - } - assert(internalformat == format); - assert(0 == border); - if (0 != level) { - LOGD("agl2: glTexImage2D level=%d", level); - return; - } - unsigned bytesPerPixel = 0; - GGLPixelFormat texFormat = GGL_PIXEL_FORMAT_UNKNOWN; - GetFormatAndBytesPerPixel(format, &bytesPerPixel, &texFormat); - - assert(texFormat && bytesPerPixel); -// LOGD("texFormat=0x%.2X bytesPerPixel=%d active=%u", texFormat, bytesPerPixel, ctx->tex.active); - unsigned offset = 0, size = width * height * bytesPerPixel, totalSize = size; - - assert(ctx->tex.tmus[ctx->tex.active]); - - GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active]; - tex.width = width; - tex.height = height; - tex.levelCount = 1; - tex.format = texFormat; - - switch (target) { - case GL_TEXTURE_2D: - assert(GL_TEXTURE_2D == ctx->tex.tmus[ctx->tex.active]->type); - offset = 0; - break; - break; - case GL_TEXTURE_CUBE_MAP_POSITIVE_X: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_X: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Y: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Y: - case GL_TEXTURE_CUBE_MAP_POSITIVE_Z: - case GL_TEXTURE_CUBE_MAP_NEGATIVE_Z: - assert(GL_TEXTURE_CUBE_MAP == ctx->tex.tmus[ctx->tex.active]->type); - assert(width == height); - offset = (target - GL_TEXTURE_CUBE_MAP_POSITIVE_X) * size; - totalSize = 6 * size; - break; - default: - assert(0); - return; - } - - tex.levels = realloc(tex.levels, totalSize); - if (pixels) - CopyTexture((char *)tex.levels, (const char *)pixels, bytesPerPixel, 0, 0, width, 0, 0, width, width, height); - ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active); -} - -void glTexParameterf(GLenum target, GLenum pname, GLfloat param) -{ -// LOGD("agl2: glTexParameterf target=0x%.4X pname=0x%.4X param=%f", target, pname, param); - glTexParameteri(target, pname, param); -} -void API_ENTRY(glTexParameterfv)(GLenum target, GLenum pname, const GLfloat* params) -{ - CALL_GL_API(glTexParameterfv, target, pname, params); -} -void glTexParameteri(GLenum target, GLenum pname, GLint param) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("alg2: glTexParameteri target=0x%.0X pname=0x%.4X param=0x%.4X", -// target, pname, param); - assert(ctx->tex.tmus[ctx->tex.active]); - assert(target == ctx->tex.tmus[ctx->tex.active]->type); - GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active]; - switch (pname) { - case GL_TEXTURE_WRAP_S: - case GL_TEXTURE_WRAP_T: - GGLTexture::GGLTextureWrap wrap; - switch (param) { - case GL_REPEAT: - wrap = GGLTexture::GGL_REPEAT; - break; - case GL_CLAMP_TO_EDGE: - wrap = GGLTexture::GGL_CLAMP_TO_EDGE; - break; - case GL_MIRRORED_REPEAT: - wrap = GGLTexture::GGL_MIRRORED_REPEAT; - break; - default: - assert(0); - return; - } - if (GL_TEXTURE_WRAP_S == pname) - tex.wrapS = wrap; - else - tex.wrapT = wrap; - break; - case GL_TEXTURE_MIN_FILTER: - switch (param) { - case GL_NEAREST: - tex.minFilter = GGLTexture::GGL_NEAREST; - break; - case GL_LINEAR: - tex.minFilter = GGLTexture::GGL_LINEAR; - break; - case GL_NEAREST_MIPMAP_NEAREST: -// tex.minFilter = GGLTexture::GGL_NEAREST_MIPMAP_NEAREST; - break; - case GL_NEAREST_MIPMAP_LINEAR: -// tex.minFilter = GGLTexture::GGL_NEAREST_MIPMAP_LINEAR; - break; - case GL_LINEAR_MIPMAP_NEAREST: -// tex.minFilter = GGLTexture::GGL_LINEAR_MIPMAP_NEAREST; - break; - case GL_LINEAR_MIPMAP_LINEAR: -// tex.minFilter = GGLTexture::GGL_LINEAR_MIPMAP_LINEAR; - break; - default: - assert(0); - return; - } - break; - case GL_TEXTURE_MAG_FILTER: - switch (param) { - case GL_NEAREST: - tex.minFilter = GGLTexture::GGL_NEAREST; - break; - case GL_LINEAR: - tex.minFilter = GGLTexture::GGL_LINEAR; - break; - default: - assert(0); - return; - } - break; - default: - assert(0); - return; - } - // implementation restriction - if (tex.magFilter != tex.minFilter) - tex.magFilter = tex.minFilter = GGLTexture::GGL_LINEAR; - ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active); -} -void API_ENTRY(glTexParameteriv)(GLenum target, GLenum pname, const GLint* params) -{ - CALL_GL_API(glTexParameteriv, target, pname, params); -} -void glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glTexSubImage2D target=0x%.4X level=%d xoffset=%d yoffset=%d width=%d height=%d format=0x%.4X type=0x%.4X pixels=%p", -// target, level, xoffset, yoffset, width, height, format, type, pixels); - assert(0 == level); - assert(target == ctx->tex.tmus[ctx->tex.active]->type); - switch (type) { - case GL_UNSIGNED_BYTE: - break; - case GL_UNSIGNED_SHORT_5_6_5: - format = GL_UNSIGNED_SHORT_5_6_5; - assert(4 == ctx->tex.unpack); - break; - default: - assert(0); - } - GGLTexture & tex = *ctx->tex.tmus[ctx->tex.active]; - GGLPixelFormat texFormat = GGL_PIXEL_FORMAT_UNKNOWN; - unsigned bytesPerPixel = 0; - GetFormatAndBytesPerPixel(format, &bytesPerPixel, &texFormat); - assert(texFormat == tex.format); - assert(GL_UNSIGNED_BYTE == type); - switch (target) { - case GL_TEXTURE_2D: - CopyTexture((char *)tex.levels, (const char *)pixels, bytesPerPixel, 0, 0, width, xoffset, - yoffset, tex.width, width, height); - break; - default: - assert(0); - } - ctx->tex.UpdateSampler(ctx->iface, ctx->tex.active); -} diff --git a/opengl/libagl2/src/vertex.cpp b/opengl/libagl2/src/vertex.cpp deleted file mode 100644 index 021b82b76371..000000000000 --- a/opengl/libagl2/src/vertex.cpp +++ /dev/null @@ -1,373 +0,0 @@ -#include "gles2context.h" - -//#undef LOGD -//#define LOGD(...) - -void GLES2Context::InitializeVertices() -{ - vert.vbos = std::map<GLuint, VBO *>(); // the entire struct has been zeroed in constructor - vert.free = 1; - vert.vbo = NULL; - vert.indices = NULL; - for (unsigned i = 0; i < GGL_MAXVERTEXATTRIBS; i++) - vert.defaultAttribs[i] = Vector4(0,0,0,1); -} - -void GLES2Context::UninitializeVertices() -{ - for (std::map<GLuint, VBO *>::iterator it = vert.vbos.begin(); it != vert.vbos.end(); it++) { - if (!it->second) - continue; - free(it->second->data); - free(it->second); - } -} - -static inline void FetchElement(const GLES2Context * ctx, const unsigned index, - const unsigned maxAttrib, VertexInput * elem) -{ - for (unsigned i = 0; i < maxAttrib; i++) { - { - unsigned size = 0; - if (ctx->vert.attribs[i].enabled) { - const char * ptr = (const char *)ctx->vert.attribs[i].ptr; - ptr += ctx->vert.attribs[i].stride * index; - memcpy(elem->attributes + i, ptr, ctx->vert.attribs[i].size * sizeof(float)); - size = ctx->vert.attribs[i].size; -// LOGD("agl2: FetchElement %d attribs size=%d %.2f,%.2f,%.2f,%.2f", i, size, elem->attributes[i].x, -// elem->attributes[i].y, elem->attributes[i].z, elem->attributes[i].w); - } else { -// LOGD("agl2: FetchElement %d default %.2f,%.2f,%.2f,%.2f", i, ctx->vert.defaultAttribs[i].x, -// ctx->vert.defaultAttribs[i].y, ctx->vert.defaultAttribs[i].z, ctx->vert.defaultAttribs[i].w); - } - - switch (size) { - case 0: // fall through - elem->attributes[i].x = ctx->vert.defaultAttribs[i].x; - case 1: // fall through - elem->attributes[i].y = ctx->vert.defaultAttribs[i].y; - case 2: // fall through - elem->attributes[i].z = ctx->vert.defaultAttribs[i].z; - case 3: // fall through - elem->attributes[i].w = ctx->vert.defaultAttribs[i].w; - case 4: - break; - default: - assert(0); - break; - } -// LOGD("agl2: FetchElement %d size=%d %.2f,%.2f,%.2f,%.2f", i, size, elem->attributes[i].x, -// elem->attributes[i].y, elem->attributes[i].z, elem->attributes[i].w); - } - } -} - -template<typename IndexT> static void DrawElementsTriangles(const GLES2Context * ctx, - const unsigned count, const IndexT * indices, const unsigned maxAttrib) -{ - VertexInput v[3]; - if (ctx->vert.indices) - indices = (IndexT *)((char *)ctx->vert.indices->data + (long)indices); - for (unsigned i = 0; i < count; i += 3) { - for (unsigned j = 0; j < 3; j++) - FetchElement(ctx, indices[i + j], maxAttrib, v + j); - ctx->iface->DrawTriangle(ctx->iface, v, v + 1, v + 2); - } -} - -static void DrawArraysTriangles(const GLES2Context * ctx, const unsigned first, - const unsigned count, const unsigned maxAttrib) -{ -// LOGD("agl: DrawArraysTriangles=%p", DrawArraysTriangles); - VertexInput v[3]; - for (unsigned i = 2; i < count; i+=3) { - // TODO: fix order - FetchElement(ctx, first + i - 2, maxAttrib, v + 0); - FetchElement(ctx, first + i - 1, maxAttrib, v + 1); - FetchElement(ctx, first + i - 0, maxAttrib, v + 2); - ctx->iface->DrawTriangle(ctx->iface, v + 0, v + 1, v + 2); - } -// LOGD("agl: DrawArraysTriangles end"); -} - -template<typename IndexT> static void DrawElementsTriangleStrip(const GLES2Context * ctx, - const unsigned count, const IndexT * indices, const unsigned maxAttrib) -{ - VertexInput v[3]; - if (ctx->vert.indices) - indices = (IndexT *)((char *)ctx->vert.indices->data + (long)indices); - -// LOGD("agl2: DrawElementsTriangleStrip"); -// for (unsigned i = 0; i < count; i++) -// LOGD("indices[%d] = %d", i, indices[i]); - - FetchElement(ctx, indices[0], maxAttrib, v + 0); - FetchElement(ctx, indices[1], maxAttrib, v + 1); - for (unsigned i = 2; i < count; i ++) { - FetchElement(ctx, indices[i], maxAttrib, v + i % 3); - ctx->iface->DrawTriangle(ctx->iface, v + (i - 2) % 3, v + (i - 1) % 3 , v + (i + 0) % 3); - } - -// for (unsigned i = 2; i < count; i++) { -// FetchElement(ctx, indices[i - 2], maxAttrib, v + 0); -// FetchElement(ctx, indices[i - 1], maxAttrib, v + 1); -// FetchElement(ctx, indices[i - 0], maxAttrib, v + 2); -// ctx->iface->DrawTriangle(ctx->iface, v + 0, v + 1, v + 2); -// } -} - -static void DrawArraysTriangleStrip(const GLES2Context * ctx, const unsigned first, - const unsigned count, const unsigned maxAttrib) -{ - VertexInput v[3]; - FetchElement(ctx, first, maxAttrib, v + 0); - FetchElement(ctx, first + 1, maxAttrib, v + 1); - for (unsigned i = 2; i < count; i++) { - // TODO: fix order - FetchElement(ctx, first + i, maxAttrib, v + i % 3); - ctx->iface->DrawTriangle(ctx->iface, v + (i - 2) % 3, v + (i - 1) % 3 , v + (i + 0) % 3); - } -} - -void glBindBuffer(GLenum target, GLuint buffer) -{ - GLES2_GET_CONST_CONTEXT(ctx); - VBO * vbo = NULL; - if (0 != buffer) { - std::map<GLuint, VBO *>::iterator it = ctx->vert.vbos.find(buffer); - if (it != ctx->vert.vbos.end()) { - vbo = it->second; - if (!vbo) - vbo = (VBO *)calloc(1, sizeof(VBO)); - it->second = vbo; - } else - assert(0); - } - if (GL_ARRAY_BUFFER == target) - ctx->vert.vbo = vbo; - else if (GL_ELEMENT_ARRAY_BUFFER == target) - ctx->vert.indices = vbo; - else - assert(0); - assert(vbo || buffer == 0); -// LOGD("\n*\n glBindBuffer 0x%.4X=%d ", target, buffer); -} - -void glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GLenum usage) -{ - GLES2_GET_CONST_CONTEXT(ctx); - if (GL_ARRAY_BUFFER == target) { - assert(ctx->vert.vbo); - ctx->vert.vbo->data = realloc(ctx->vert.vbo->data, size); - ctx->vert.vbo->size = size; - ctx->vert.vbo->usage = usage; - if (data) - memcpy(ctx->vert.vbo->data, data, size); - } else if (GL_ELEMENT_ARRAY_BUFFER == target) { - assert(ctx->vert.indices); - ctx->vert.indices->data = realloc(ctx->vert.indices->data, size); - ctx->vert.indices->size = size; - ctx->vert.indices->usage = usage; - if (data) - memcpy(ctx->vert.indices->data, data, size); - } else - assert(0); -// LOGD("\n*\n glBufferData target=0x%.4X size=%u data=%p usage=0x%.4X \n", -// target, size, data, usage); -} - -void glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid* data) -{ - GLES2_GET_CONST_CONTEXT(ctx); - if (GL_ARRAY_BUFFER == target) - { - assert(ctx->vert.vbo); - assert(0 <= offset); - assert(0 <= size); - assert(offset + size <= ctx->vert.vbo->size); - memcpy((char *)ctx->vert.vbo->data + offset, data, size); - } - else - assert(0); -} - -void glDeleteBuffers(GLsizei n, const GLuint* buffers) -{ - GLES2_GET_CONST_CONTEXT(ctx); - for (unsigned i = 0; i < n; i++) { - std::map<GLuint, VBO*>::iterator it = ctx->vert.vbos.find(buffers[i]); - if (it == ctx->vert.vbos.end()) - continue; - ctx->vert.free = min(ctx->vert.free, buffers[i]); - if (it->second == ctx->vert.vbo) - ctx->vert.vbo = NULL; - else if (it->second == ctx->vert.indices) - ctx->vert.indices = NULL; - if (it->second) { - free(it->second->data); - free(it->second); - } - } -} - -void glDisableVertexAttribArray(GLuint index) -{ - GLES2_GET_CONST_CONTEXT(ctx); - assert(GGL_MAXVERTEXATTRIBS > index); - ctx->vert.attribs[index].enabled = false; -} - -void glDrawArrays(GLenum mode, GLint first, GLsizei count) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glDrawArrays=%p", glDrawArrays); - assert(ctx->rasterizer.CurrentProgram); - assert(0 <= first); - int maxAttrib = -1; - ctx->iface->ShaderProgramGetiv(ctx->rasterizer.CurrentProgram, GL_ACTIVE_ATTRIBUTES, &maxAttrib); - assert(0 <= maxAttrib && GGL_MAXVERTEXATTRIBS >= maxAttrib); - switch (mode) { - case GL_TRIANGLE_STRIP: - DrawArraysTriangleStrip(ctx, first, count, maxAttrib); - break; - case GL_TRIANGLES: - DrawArraysTriangles(ctx, first, count, maxAttrib); - break; - default: - LOGE("agl2: glDrawArrays unsupported mode: 0x%.4X \n", mode); - assert(0); - break; - } -// LOGD("agl2: glDrawArrays end"); -} - -void glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoid* indices) -{ - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("agl2: glDrawElements=%p mode=0x%.4X count=%d type=0x%.4X indices=%p", -// glDrawElements, mode, count, type, indices); - if (!ctx->rasterizer.CurrentProgram) - return; - - int maxAttrib = -1; - ctx->iface->ShaderProgramGetiv(ctx->rasterizer.CurrentProgram, GL_ACTIVE_ATTRIBUTES, &maxAttrib); - assert(0 <= maxAttrib && GGL_MAXVERTEXATTRIBS >= maxAttrib); -// LOGD("agl2: glDrawElements mode=0x%.4X type=0x%.4X count=%d program=%p indices=%p \n", -// mode, type, count, ctx->rasterizer.CurrentProgram, indices); - switch (mode) { - case GL_TRIANGLES: - if (GL_UNSIGNED_SHORT == type) - DrawElementsTriangles<unsigned short>(ctx, count, (unsigned short *)indices, maxAttrib); - else - assert(0); - break; - case GL_TRIANGLE_STRIP: - if (GL_UNSIGNED_SHORT == type) - DrawElementsTriangleStrip<unsigned short>(ctx, count, (unsigned short *)indices, maxAttrib); - else - assert(0); - break; - default: - assert(0); - } -// LOGD("agl2: glDrawElements end"); -} - -void glEnableVertexAttribArray(GLuint index) -{ - GLES2_GET_CONST_CONTEXT(ctx); - ctx->vert.attribs[index].enabled = true; -// LOGD("agl2: glEnableVertexAttribArray %d \n", index); -} - -void glGenBuffers(GLsizei n, GLuint* buffers) -{ - GLES2_GET_CONST_CONTEXT(ctx); - for (unsigned i = 0; i < n; i++) { - buffers[i] = 0; - for (ctx->vert.free; ctx->vert.free < 0xffffffffu; ctx->vert.free++) { - if (ctx->vert.vbos.find(ctx->vert.free) == ctx->vert.vbos.end()) { - ctx->vert.vbos[ctx->vert.free] = NULL; - buffers[i] = ctx->vert.free; -// LOGD("glGenBuffers %d \n", buffers[i]); - ctx->vert.free++; - break; - } - } - assert(buffers[i]); - } -} - -void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, - GLsizei stride, const GLvoid* ptr) -{ - GLES2_GET_CONST_CONTEXT(ctx); - assert(GL_FLOAT == type); - assert(0 < size && 4 >= size); - ctx->vert.attribs[index].size = size; - ctx->vert.attribs[index].type = type; - ctx->vert.attribs[index].normalized = normalized; - if (0 == stride) - ctx->vert.attribs[index].stride = size * sizeof(float); - else if (stride > 0) - ctx->vert.attribs[index].stride = stride; - else - assert(0); -// LOGD("\n*\n*\n* agl2: glVertexAttribPointer program=%u index=%d size=%d stride=%d ptr=%p \n*\n*", -// unsigned(ctx->rasterizer.CurrentProgram) ^ 0x04dc18f9, index, size, stride, ptr); - if (ctx->vert.vbo) - ctx->vert.attribs[index].ptr = (char *)ctx->vert.vbo->data + (long)ptr; - else - ctx->vert.attribs[index].ptr = ptr; -// const float * attrib = (const float *)ctx->vert.attribs[index].ptr; -// for (unsigned i = 0; i < 3; i++) -// if (3 == size) -// LOGD("%.2f %.2f %.2f", attrib[i * 3 + 0], attrib[i * 3 + 1], attrib[i * 3 + 2]); -// else if (2 == size) -// LOGD("%.2f %.2f", attrib[i * 3 + 0], attrib[i * 3 + 1]); - -} - -void glVertexAttrib1f(GLuint indx, GLfloat x) -{ - glVertexAttrib4f(indx, x,0,0,1); -} - -void glVertexAttrib1fv(GLuint indx, const GLfloat* values) -{ - glVertexAttrib4f(indx, values[0],0,0,1); -} - -void glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) -{ - glVertexAttrib4f(indx, x,y,0,1); -} - -void glVertexAttrib2fv(GLuint indx, const GLfloat* values) -{ - glVertexAttrib4f(indx, values[0],values[1],0,1); -} - -void glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) -{ - glVertexAttrib4f(indx, x,y,z,1); -} - -void glVertexAttrib3fv(GLuint indx, const GLfloat* values) -{ - glVertexAttrib4f(indx, values[0],values[1],values[2],1); -} - -void glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfloat w) -{ - assert(GGL_MAXVERTEXATTRIBS > indx); - GLES2_GET_CONST_CONTEXT(ctx); -// LOGD("\n*\n*\n agl2: glVertexAttrib4f %d %.2f,%.2f,%.2f,%.2f \n*\n*", indx, x, y, z, w); - ctx->vert.defaultAttribs[indx] = Vector4(x,y,z,w); - assert(0); -} - -void glVertexAttrib4fv(GLuint indx, const GLfloat* values) -{ - glVertexAttrib4f(indx, values[0], values[1], values[2], values[3]); -} 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 095f10c80b77..a63d5b054d00 100644 --- a/opengl/libs/EGL/eglApi.cpp +++ b/opengl/libs/EGL/eglApi.cpp @@ -49,22 +49,6 @@ using namespace android; // ---------------------------------------------------------------------------- -static char const * const sVendorString = "Android"; -static char const * const sVersionString = "1.4 Android META-EGL"; -static char const * const sClientApiString = "OpenGL ES"; -static char const * const sExtensionString = - "EGL_KHR_image " - "EGL_KHR_image_base " - "EGL_KHR_image_pixmap " - "EGL_KHR_gl_texture_2D_image " - "EGL_KHR_gl_texture_cubemap_image " - "EGL_KHR_gl_renderbuffer_image " - "EGL_KHR_fence_sync " - "EGL_ANDROID_image_native_buffer " - "EGL_ANDROID_swap_rectangle " - "EGL_NV_system_time " - ; - struct extention_map_t { const char* name; __eglMustCastToProperFunctionPointerType address; @@ -79,8 +63,6 @@ static const extention_map_t sExtentionMap[] = { (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR }, { "eglDestroyImageKHR", (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR }, - { "eglSetSwapRectangleANDROID", - (__eglMustCastToProperFunctionPointerType)&eglSetSwapRectangleANDROID }, { "eglGetSystemTimeFrequencyNV", (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV }, { "eglGetSystemTimeNV", @@ -388,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) { @@ -978,13 +965,13 @@ const char* eglQueryString(EGLDisplay dpy, EGLint name) switch (name) { case EGL_VENDOR: - return sVendorString; + return dp->getVendorString(); case EGL_VERSION: - return sVersionString; + return dp->getVersionString(); case EGL_EXTENSIONS: - return sExtensionString; + return dp->getExtensionString(); case EGL_CLIENT_APIS: - return sClientApiString; + return dp->getClientApiString(); } return setError(EGL_BAD_PARAMETER, (const char *)0); } @@ -1447,25 +1434,7 @@ EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute // ANDROID extensions // ---------------------------------------------------------------------------- -EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, - EGLint left, EGLint top, EGLint width, EGLint height) -{ - clearError(); - - egl_display_t const * const dp = validate_display(dpy); - if (!dp) return EGL_FALSE; - - SurfaceRef _s(dp, draw); - if (!_s.get()) - return setError(EGL_BAD_SURFACE, EGL_FALSE); - - egl_surface_t const * const s = get_surface(draw); - if (s->cnx->egl.eglSetSwapRectangleANDROID) { - return s->cnx->egl.eglSetSwapRectangleANDROID( - dp->disp[s->impl].dpy, s->surface, left, top, width, height); - } - return setError(EGL_BAD_DISPLAY, NULL); -} +/* ANDROID extensions entry-point go here */ // ---------------------------------------------------------------------------- // NVIDIA extensions diff --git a/opengl/libs/EGL/egl_cache.cpp b/opengl/libs/EGL/egl_cache.cpp index 13a492917d2c..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$"; @@ -85,7 +93,7 @@ void egl_cache_t::initialize(egl_display_t *display) { bool atStart = !strncmp(BC_EXT_STR " ", exts, bcExtLen+1); bool atEnd = (bcExtLen+1) < extsLen && !strcmp(" " BC_EXT_STR, exts + extsLen - (bcExtLen+1)); - bool inMiddle = strstr(" " BC_EXT_STR " ", exts); + bool inMiddle = strstr(exts, " " BC_EXT_STR " "); if (equal || atStart || atEnd || inMiddle) { PFNEGLSETBLOBCACHEFUNCSANDROIDPROC eglSetBlobCacheFuncsANDROID; eglSetBlobCacheFuncsANDROID = diff --git a/opengl/libs/EGL/egl_display.cpp b/opengl/libs/EGL/egl_display.cpp index 2935832ad8a7..31119f9608d5 100644 --- a/opengl/libs/EGL/egl_display.cpp +++ b/opengl/libs/EGL/egl_display.cpp @@ -14,6 +14,8 @@ ** limitations under the License. */ +#include <string.h> + #include "egl_cache.h" #include "egl_display.h" #include "egl_object.h" @@ -25,6 +27,36 @@ namespace android { // ---------------------------------------------------------------------------- +static char const * const sVendorString = "Android"; +static char const * const sVersionString = "1.4 Android META-EGL"; +static char const * const sClientApiString = "OpenGL ES"; + +// this is the list of EGL extensions that are exposed to applications +// some of them are mandatory because used by the ANDROID system. +// +// mandatory extensions are required per the CDD and not explicitly +// checked during EGL initialization. the system *assumes* these extensions +// are present. the system may not function properly if some mandatory +// extensions are missing. +// +// NOTE: sExtensionString MUST be have a single space as the last character. +// +static char const * const sExtensionString = + "EGL_KHR_image " // mandatory + "EGL_KHR_image_base " // mandatory + "EGL_KHR_image_pixmap " + "EGL_KHR_gl_texture_2D_image " + "EGL_KHR_gl_texture_cubemap_image " + "EGL_KHR_gl_renderbuffer_image " + "EGL_KHR_fence_sync " + "EGL_NV_system_time " + "EGL_ANDROID_image_native_buffer " // mandatory + ; + +// extensions not exposed to applications but used by the ANDROID system +// "EGL_ANDROID_recordable " // mandatory +// "EGL_ANDROID_blob_cache " // strongly recommended + extern void initEglTraceLevel(); extern void setGLHooksThreadSpecific(gl_hooks_t const *value); @@ -174,6 +206,40 @@ EGLBoolean egl_display_t::initialize(EGLint *major, EGLint *minor) { } } + // the query strings are per-display + mVendorString.setTo(sVendorString); + mVersionString.setTo(sVersionString); + mClientApiString.setTo(sClientApiString); + + // we only add extensions that exist in at least one implementation + char const* start = sExtensionString; + char const* end; + do { + // find the space separating this extension for the next one + end = strchr(start, ' '); + if (end) { + // length of the extension string + const size_t len = end - start; + if (len) { + // NOTE: we could avoid the copy if we had strnstr. + const String8 ext(start, len); + // now go through all implementations and look for this extension + for (int i = 0; i < IMPL_NUM_IMPLEMENTATIONS; i++) { + if (disp[i].queryString.extensions) { + // if we find it, add this extension string to our list + // (and don't forget the space) + const char* match = strstr(disp[i].queryString.extensions, ext.string()); + if (match && (match[len] == ' ' || match[len] == 0)) { + mExtensionString.append(start, len+1); + } + } + } + } + // process the next extension string, and skip the space. + start = end + 1; + } + } while (end); + egl_cache_t::get()->initialize(this); EGLBoolean res = EGL_FALSE; diff --git a/opengl/libs/EGL/egl_display.h b/opengl/libs/EGL/egl_display.h index 94077bea1d99..042ae07a91f6 100644 --- a/opengl/libs/EGL/egl_display.h +++ b/opengl/libs/EGL/egl_display.h @@ -29,6 +29,7 @@ #include <utils/SortedVector.h> #include <utils/threads.h> +#include <utils/String8.h> #include "egldefs.h" #include "hooks.h" @@ -91,6 +92,11 @@ public: inline bool isValid() const { return magic == '_dpy'; } inline bool isAlive() const { return isValid(); } + char const * getVendorString() const { return mVendorString.string(); } + char const * getVersionString() const { return mVersionString.string(); } + char const * getClientApiString() const { return mClientApiString.string(); } + char const * getExtensionString() const { return mExtensionString.string(); } + inline uint32_t getRefsCount() const { return refs; } struct strings_t { @@ -122,6 +128,10 @@ private: uint32_t refs; mutable Mutex lock; SortedVector<egl_object_t*> objects; + String8 mVendorString; + String8 mVersionString; + String8 mClientApiString; + String8 mExtensionString; }; // ---------------------------------------------------------------------------- diff --git a/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if b/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if index c5e34cd1b9aa..0c5fa04f134f 100644 --- a/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if +++ b/opengl/tools/glgen/stubs/gles11/GLES11ExtHeader.java-if @@ -124,6 +124,10 @@ public class GLES11Ext { public static final int GL_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FE; public static final int GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = 0x84FF; public static final int GL_BGRA = 0x80E1; + public static final int GL_TEXTURE_EXTERNAL_OES = 0x8D65; + public static final int GL_SAMPLER_EXTERNAL_OES = 0x8D66; + public static final int GL_TEXTURE_BINDING_EXTERNAL_OES = 0x8D67; + public static final int GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES = 0x8D68; native private static void _nativeClassInit(); static { @@ -135,4 +139,4 @@ public class GLES11Ext { private static final int GL_FLOAT = GLES10.GL_FLOAT; private static final int GL_SHORT = GLES10.GL_SHORT; - private static Buffer _matrixIndexPointerOES;
\ No newline at end of file + private static Buffer _matrixIndexPointerOES; diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 5495d0840477..aa08e64353d8 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -723,6 +723,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 57; } + /************* The following are Honeycomb changes ************/ + if (upgradeVersion == 57) { /* * New settings to: @@ -751,13 +753,13 @@ public class DatabaseHelper extends SQLiteOpenHelper { if (upgradeVersion == 58) { /* Add default for new Auto Time Zone */ + int autoTimeValue = getIntValueFromSystem(db, Settings.System.AUTO_TIME, 0); db.beginTransaction(); SQLiteStatement stmt = null; try { - stmt = db.compileStatement("INSERT INTO secure(name,value)" - + " VALUES(?,?);"); - loadBooleanSetting(stmt, Settings.System.AUTO_TIME_ZONE, - R.bool.def_auto_time_zone); // Sync timezone to NITZ + stmt = db.compileStatement("INSERT INTO system(name,value)" + " VALUES(?,?);"); + loadSetting(stmt, Settings.System.AUTO_TIME_ZONE, + autoTimeValue); // Sync timezone to NITZ if auto_time was enabled db.setTransactionSuccessful(); } finally { db.endTransaction(); @@ -784,18 +786,24 @@ public class DatabaseHelper extends SQLiteOpenHelper { } if (upgradeVersion == 60) { - upgradeScreenTimeout(db); + // Don't do this for upgrades from Gingerbread + // Were only required for intra-Honeycomb upgrades for testing + // upgradeScreenTimeout(db); upgradeVersion = 61; } if (upgradeVersion == 61) { - upgradeScreenTimeout(db); + // Don't do this for upgrades from Gingerbread + // Were only required for intra-Honeycomb upgrades for testing + // upgradeScreenTimeout(db); upgradeVersion = 62; } // Change the default for screen auto-brightness mode if (upgradeVersion == 62) { - upgradeAutoBrightness(db); + // Don't do this for upgrades from Gingerbread + // Were only required for intra-Honeycomb upgrades for testing + // upgradeAutoBrightness(db); upgradeVersion = 63; } @@ -839,6 +847,8 @@ public class DatabaseHelper extends SQLiteOpenHelper { upgradeVersion = 65; } + /************* The following are Ice Cream Sandwich changes ************/ + if (upgradeVersion == 65) { /* * Animations are removed from Settings. Turned on by default @@ -1232,12 +1242,13 @@ public class DatabaseHelper extends SQLiteOpenHelper { stmt = db.compileStatement("INSERT OR IGNORE INTO system(name,value)" + " VALUES(?,?);"); - // Vibrate off by default for ringer, on for notification + // Vibrate on by default for ringer, on for notification int vibrate = 0; vibrate = AudioService.getValueForVibrateSetting(vibrate, - AudioManager.VIBRATE_TYPE_NOTIFICATION, AudioManager.VIBRATE_SETTING_ON); + AudioManager.VIBRATE_TYPE_NOTIFICATION, + AudioManager.VIBRATE_SETTING_ONLY_SILENT); vibrate |= AudioService.getValueForVibrateSetting(vibrate, - AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_OFF); + AudioManager.VIBRATE_TYPE_RINGER, AudioManager.VIBRATE_SETTING_ONLY_SILENT); loadSetting(stmt, Settings.System.VIBRATE_ON, vibrate); } finally { if (stmt != null) stmt.close(); @@ -1509,4 +1520,20 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadSetting(stmt, key, Float.toString(mContext.getResources().getFraction(resid, base, base))); } + + private int getIntValueFromSystem(SQLiteDatabase db, String name, int defaultValue) { + int value = defaultValue; + Cursor c = null; + try { + c = db.query("system", new String[] { Settings.System.VALUE }, "name='" + name + "'", + null, null, null, null); + if (c != null && c.moveToFirst()) { + String val = c.getString(0); + value = val == null ? defaultValue : Integer.parseInt(val); + } + } finally { + if (c != null) c.close(); + } + return value; + } } diff --git a/packages/SystemUI/AndroidManifest.xml b/packages/SystemUI/AndroidManifest.xml index eefb9fe69687..e937587c62eb 100644 --- a/packages/SystemUI/AndroidManifest.xml +++ b/packages/SystemUI/AndroidManifest.xml @@ -46,18 +46,29 @@ </intent-filter> </receiver> - <!-- handle dock insertion, launch screensaver instead --> + <!-- should you need to launch the screensaver, this is a good way to do it --> <activity android:name=".DreamsDockLauncher" + android:theme="@android:style/Theme.Dialog" android:label="@string/dreams_dock_launcher"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.DESK_DOCK" /> </intent-filter> </activity> + <!-- launch screensaver on (desk) dock event --> + <receiver android:name=".DreamsDockLauncher$DockEventReceiver" + android:exported="true" + > + <intent-filter> + <action android:name="android.intent.action.DOCK_EVENT" /> + </intent-filter> + </receiver> + + <activity android:name=".usb.UsbStorageActivity" - android:excludeFromRecents="true"> + android:label="@*android:string/usb_storage_activity_title" + android:excludeFromRecents="true"> </activity> <activity android:name="com.android.internal.app.ExternalMediaFormatActivity" android:theme="@*android:style/Theme.Dialog.Alert" @@ -124,7 +135,7 @@ <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.DEFAULT" /> - <category android:name="android.intent.category.DREAM" /> +<!-- <category android:name="android.intent.category.DREAM" />--> </intent-filter> </activity> </application> diff --git a/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png b/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png Binary files differindex cbd72fb7cd8b..c02bd424f51d 100644 --- a/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png +++ b/packages/SystemUI/res/drawable-hdpi/ic_launcher_settings.png diff --git a/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png b/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png Binary files differindex 765133110704..05cdd9aa94e8 100644 --- a/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png +++ b/packages/SystemUI/res/drawable-mdpi/ic_launcher_settings.png diff --git a/packages/SystemUI/res/drawable-xhdpi/ic_launcher_settings.png b/packages/SystemUI/res/drawable-xhdpi/ic_launcher_settings.png Binary files differindex b6317797d946..2b2907b60843 100644 --- a/packages/SystemUI/res/drawable-xhdpi/ic_launcher_settings.png +++ b/packages/SystemUI/res/drawable-xhdpi/ic_launcher_settings.png diff --git a/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml b/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml new file mode 100644 index 000000000000..ce3372ee0f9b --- /dev/null +++ b/packages/SystemUI/res/drawable/notification_row_legacy_bg.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- 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. +--> + +<selector xmlns:android="http://schemas.android.com/apk/res/android" + android:exitFadeDuration="@android:integer/config_mediumAnimTime"> + + <item android:state_pressed="true" android:drawable="@drawable/notification_item_background_color_pressed" /> + <item android:state_pressed="false" android:drawable="@drawable/notification_item_background_legacy_color" /> +</selector> diff --git a/packages/SystemUI/res/values-sw600dp/donottranslate.xml b/packages/SystemUI/res/values-sw600dp/donottranslate.xml new file mode 100644 index 000000000000..09960673a53e --- /dev/null +++ b/packages/SystemUI/res/values-sw600dp/donottranslate.xml @@ -0,0 +1,27 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- +/** + * 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. + */ +--> +<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> + <!-- For formatting day of week and date in DateView. %1$s is DOW, %2$s is date. + We show both (DOW on one line, then the date) but this can be overridden for locales as + necessary. + --> + <string name="status_bar_date_formatter">%1$s\n%2$s</string> + +</resources> + diff --git a/packages/SystemUI/res/values/colors.xml b/packages/SystemUI/res/values/colors.xml index ff67a7eac6c1..3a2ea65937ee 100644 --- a/packages/SystemUI/res/values/colors.xml +++ b/packages/SystemUI/res/values/colors.xml @@ -30,4 +30,5 @@ <drawable name="notification_tracking_bg">#d8000000</drawable> <color name="notification_list_shadow_top">#80000000</color> <drawable name="recents_callout_line">#99ffffff</drawable> + <drawable name="notification_item_background_legacy_color">#ffaaaaaa</drawable> </resources> diff --git a/packages/SystemUI/res/values/donottranslate.xml b/packages/SystemUI/res/values/donottranslate.xml index 089a54db5e86..4ca211665e05 100644 --- a/packages/SystemUI/res/values/donottranslate.xml +++ b/packages/SystemUI/res/values/donottranslate.xml @@ -21,6 +21,6 @@ We show both (DOW on one line, then the date) but this can be overridden for locales as necessary. --> - <string name="status_bar_date_formatter">%1$s\n%2$s</string> + <string name="status_bar_date_formatter">%2$s</string> </resources> diff --git a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java index b8cdd73aa1d9..1db2a7feca00 100644 --- a/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java +++ b/packages/SystemUI/src/com/android/systemui/DreamsDockLauncher.java @@ -12,21 +12,38 @@ import android.util.Slog; public class DreamsDockLauncher extends Activity { private static final String TAG = "DreamsDockLauncher"; + + // Launch the screen saver if started as an activity. @Override protected void onCreate (Bundle icicle) { super.onCreate(icicle); + launchDream(this); + finish(); + } + + private static void launchDream(Context context) { try { String component = Settings.Secure.getString( - getContentResolver(), Settings.Secure.DREAM_COMPONENT); + context.getContentResolver(), Settings.Secure.SCREENSAVER_COMPONENT); + if (component == null) { + component = context.getResources().getString( + 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 ); - startActivity(zzz); + Slog.v(TAG, "Starting screen saver on dock event: " + component); + context.startActivity(zzz); } else { Slog.e(TAG, "Couldn't start screen saver: none selected"); } @@ -34,6 +51,28 @@ public class DreamsDockLauncher extends Activity { // no screensaver? give up Slog.e(TAG, "Couldn't start screen saver: none installed"); } - finish(); + } + + // Trap low-level dock events and launch the screensaver. + public static class DockEventReceiver extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + final boolean activateOnDock = 0 != Settings.Secure.getInt( + context.getContentResolver(), + Settings.Secure.SCREENSAVER_ACTIVATE_ON_DOCK, 1); + + if (!activateOnDock) return; + + if (Intent.ACTION_DOCK_EVENT.equals(intent.getAction())) { + Bundle extras = intent.getExtras(); + int state = extras + .getInt(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); + if (state == Intent.EXTRA_DOCK_STATE_DESK + || state == Intent.EXTRA_DOCK_STATE_LE_DESK + || state == Intent.EXTRA_DOCK_STATE_HE_DESK) { + launchDream(context); + } + } + } } } diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java index b0554d0278bc..f0093d33040a 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java @@ -28,11 +28,14 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Resources; import android.content.res.Configuration; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.IBinder; import android.os.RemoteException; import android.os.Handler; @@ -775,6 +778,8 @@ public class PhoneStatusBar extends StatusBar { row.setDrawingCacheEnabled(true); } + applyLegacyRowBackground(notification, content); + return new View[] { row, content, expanded }; } @@ -948,6 +953,8 @@ public class PhoneStatusBar extends StatusBar { row.setDrawingCacheEnabled(true); } + applyLegacyRowBackground(sbn, content); + entry.row = row; entry.content = content; entry.expanded = expanded; @@ -956,6 +963,24 @@ public class PhoneStatusBar extends StatusBar { return true; } + void applyLegacyRowBackground(StatusBarNotification sbn, View content) { + if (sbn.notification.contentView.getLayoutId() != + com.android.internal.R.layout.status_bar_latest_event_content) { + int version = 0; + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0); + version = info.targetSdkVersion; + } catch (NameNotFoundException ex) { + Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex); + } + if (version > 0 && version < Build.VERSION_CODES.HONEYCOMB) { + content.setBackgroundResource(R.drawable.notification_row_legacy_bg); + } else { + content.setBackgroundResource(R.drawable.notification_row_bg); + } + } + } + StatusBarNotification removeNotificationViews(IBinder key) { NotificationData.Entry entry = mNotificationData.remove(key); if (entry == null) { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java index 5c691aacf9e5..01406bc2bd3e 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/tablet/TabletStatusBar.java @@ -32,13 +32,17 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; +import android.content.pm.ApplicationInfo; +import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.content.res.Resources; import android.inputmethodservice.InputMethodService; import android.graphics.PixelFormat; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.drawable.Drawable; import android.graphics.drawable.LayerDrawable; +import android.os.Build; import android.os.Handler; import android.os.IBinder; import android.os.Message; @@ -1742,8 +1746,10 @@ public class TabletStatusBar extends StatusBar implements } void workAroundBadLayerDrawableOpacity(View v) { - LayerDrawable d = (LayerDrawable)v.getBackground(); - if (d == null) return; + Drawable bgd = v.getBackground(); + if (!(bgd instanceof LayerDrawable)) return; + + LayerDrawable d = (LayerDrawable) bgd; v.setBackgroundDrawable(null); d.setOpacity(PixelFormat.TRANSLUCENT); v.setBackgroundDrawable(d); @@ -1809,6 +1815,8 @@ public class TabletStatusBar extends StatusBar implements row.setDrawingCacheEnabled(true); } + applyLegacyRowBackground(sbn, content); + entry.row = row; entry.content = content; entry.expanded = expanded; @@ -1817,6 +1825,24 @@ public class TabletStatusBar extends StatusBar implements return true; } + void applyLegacyRowBackground(StatusBarNotification sbn, View content) { + if (sbn.notification.contentView.getLayoutId() != + com.android.internal.R.layout.status_bar_latest_event_content) { + int version = 0; + try { + ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(sbn.pkg, 0); + version = info.targetSdkVersion; + } catch (NameNotFoundException ex) { + Slog.e(TAG, "Failed looking up ApplicationInfo for " + sbn.pkg, ex); + } + if (version > 0 && version < Build.VERSION_CODES.HONEYCOMB) { + content.setBackgroundResource(R.drawable.notification_row_legacy_bg); + } else { + content.setBackgroundResource(R.drawable.notification_row_bg); + } + } + } + public void clearAll() { try { mBarService.onClearAllNotifications(); diff --git a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java index 43dfb96b7cea..e61ef8ada9ed 100644 --- a/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java +++ b/packages/SystemUI/src/com/android/systemui/usb/UsbStorageActivity.java @@ -114,16 +114,11 @@ public class UsbStorageActivity extends Activity thr.start(); mAsyncStorageHandler = new Handler(thr.getLooper()); - requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); - setProgressBarIndeterminateVisibility(true); - getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); if (Environment.isExternalStorageRemovable()) { getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED); } - setTitle(getString(com.android.internal.R.string.usb_storage_activity_title)); - setContentView(com.android.internal.R.layout.usb_storage_activity); mIcon = (ImageView) findViewById(com.android.internal.R.id.icon); diff --git a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java index a7da96e4d5ea..ee54de1300e3 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardStatusViewManager.java @@ -168,7 +168,7 @@ class KeyguardStatusViewManager implements OnClickListener { boolean emergencyButtonEnabledInScreen) { if (DEBUG) Log.v(TAG, "KeyguardStatusViewManager()"); mContainer = view; - mDateFormatString = getContext().getString(R.string.full_wday_month_day_no_year); + mDateFormatString = getContext().getString(R.string.abbrev_wday_month_day_no_year); mLockPatternUtils = lockPatternUtils; mUpdateMonitor = updateMonitor; mCallback = callback; @@ -481,7 +481,14 @@ class KeyguardStatusViewManager implements OnClickListener { break; case SimMissing: + // Shows "No SIM card | Emergency calls only" on devices that are voice-capable. + // This depends on mPlmn containing the text "Emergency calls only" when the radio + // has some connectivity. Otherwise, it should be null or empty and just show + // "No SIM card" carrierText = getContext().getText(R.string.lockscreen_missing_sim_message_short); + if (mLockPatternUtils.isEmergencyCallCapable()) { + carrierText = makeCarierString(carrierText, mPlmn); + } carrierHelpTextId = R.string.lockscreen_missing_sim_instructions_long; break; diff --git a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java index 84540a150b6c..b4b82aa6fc88 100644 --- a/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java +++ b/policy/src/com/android/internal/policy/impl/KeyguardUpdateMonitor.java @@ -110,10 +110,14 @@ public class KeyguardUpdateMonitor { * the intent and provide a {@link SimCard.State} result. */ private static class SimArgs { - public final IccCard.State simState; - private SimArgs(Intent intent) { + SimArgs(IccCard.State state) { + simState = state; + } + + static SimArgs fromIntent(Intent intent) { + IccCard.State state; if (!TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(intent.getAction())) { throw new IllegalArgumentException("only handles intent ACTION_SIM_STATE_CHANGED"); } @@ -124,27 +128,28 @@ public class KeyguardUpdateMonitor { if (IccCard.INTENT_VALUE_ABSENT_ON_PERM_DISABLED.equals( absentReason)) { - this.simState = IccCard.State.PERM_DISABLED; + state = IccCard.State.PERM_DISABLED; } else { - this.simState = IccCard.State.ABSENT; + state = IccCard.State.ABSENT; } } else if (IccCard.INTENT_VALUE_ICC_READY.equals(stateExtra)) { - this.simState = IccCard.State.READY; + state = IccCard.State.READY; } else if (IccCard.INTENT_VALUE_ICC_LOCKED.equals(stateExtra)) { final String lockedReason = intent .getStringExtra(IccCard.INTENT_KEY_LOCKED_REASON); if (IccCard.INTENT_VALUE_LOCKED_ON_PIN.equals(lockedReason)) { - this.simState = IccCard.State.PIN_REQUIRED; + state = IccCard.State.PIN_REQUIRED; } else if (IccCard.INTENT_VALUE_LOCKED_ON_PUK.equals(lockedReason)) { - this.simState = IccCard.State.PUK_REQUIRED; + state = IccCard.State.PUK_REQUIRED; } else { - this.simState = IccCard.State.UNKNOWN; + state = IccCard.State.UNKNOWN; } } else if (IccCard.INTENT_VALUE_LOCKED_NETWORK.equals(stateExtra)) { - this.simState = IccCard.State.NETWORK_LOCKED; + state = IccCard.State.NETWORK_LOCKED; } else { - this.simState = IccCard.State.UNKNOWN; + state = IccCard.State.UNKNOWN; } + return new SimArgs(state); } public String toString() { @@ -279,8 +284,7 @@ public class KeyguardUpdateMonitor { mHandler.sendMessage(msg); } else if (TelephonyIntents.ACTION_SIM_STATE_CHANGED.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage( - MSG_SIM_STATE_CHANGE, - new SimArgs(intent))); + MSG_SIM_STATE_CHANGE, SimArgs.fromIntent(intent))); } else if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) { mHandler.sendMessage(mHandler.obtainMessage(MSG_RINGER_MODE_CHANGED, intent.getIntExtra(AudioManager.EXTRA_RINGER_MODE, -1), 0)); @@ -571,12 +575,16 @@ public class KeyguardUpdateMonitor { } /** - * Report that the user succesfully entered the sim pin or puk so we + * Report that the user successfully entered the SIM PIN or PUK/SIM PIN so we * have the information earlier than waiting for the intent * broadcast from the telephony code. + * + * NOTE: Because handleSimStateChange() invokes callbacks immediately without going + * through mHandler, this *must* be called from the UI thread. */ public void reportSimUnlocked() { mSimState = IccCard.State.READY; + handleSimStateChange(new SimArgs(mSimState)); } public boolean isKeyguardBypassEnabled() { diff --git a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java index 98675c6e297e..11da17c56f1d 100644 --- a/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java +++ b/policy/src/com/android/internal/policy/impl/LockPatternKeyguardView.java @@ -147,6 +147,11 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler //Also true if we've activated a phone call, either emergency dialing or incoming //This resets when the phone is turned off with no current call private boolean mHasOverlay; + //True if a dialog is currently displaying on top of this window + //Unlike other overlays, this does not close with a power button cycle + private boolean mHasDialog = false; + //True if this device is currently plugged in + private boolean mPluggedIn; /** @@ -309,6 +314,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler mLockPatternUtils = lockPatternUtils; mWindowController = controller; mHasOverlay = false; + mPluggedIn = mUpdateMonitor.isDevicePluggedIn(); mUpdateMonitor.registerInfoCallback(this); @@ -556,10 +562,14 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler if (DEBUG) Log.d(TAG, "screen off"); mScreenOn = false; mForgotPattern = false; - mHasOverlay = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE; - if (mMode == Mode.LockScreen) { + mHasOverlay = mUpdateMonitor.getPhoneState() != TelephonyManager.CALL_STATE_IDLE || + mHasDialog; + + // Emulate activity life-cycle for both lock and unlock screen. + if (mLockScreen != null) { ((KeyguardScreen) mLockScreen).onPause(); - } else { + } + if (mUnlockScreen != null) { ((KeyguardScreen) mUnlockScreen).onPause(); } @@ -643,16 +653,19 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler mHasOverlay = true; stopAndUnbindFromFaceLock(); hideFaceLockArea(); - } else if (runFaceLock) { - activateFaceLockIfAble(); + } else { + mHasDialog = false; + if (runFaceLock) activateFaceLockIfAble(); } } @Override public void show() { - if (mMode == Mode.LockScreen) { + // Emulate activity life-cycle for both lock and unlock screen. + if (mLockScreen != null) { ((KeyguardScreen) mLockScreen).onResume(); - } else { + } + if (mUnlockScreen != null) { ((KeyguardScreen) mUnlockScreen).onResume(); } @@ -712,10 +725,19 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler post(mRecreateRunnable); } - //Ignore these events; they are implemented only because they come from the same interface + /** When somebody plugs in or unplugs the device, we don't want to display faceunlock */ @Override - public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) - {} + public void onRefreshBatteryInfo(boolean showBatteryInfo, boolean pluggedIn, int batteryLevel) { + mHasOverlay |= mPluggedIn != pluggedIn; + mPluggedIn = pluggedIn; + //If it's already running, don't close it down: the unplug didn't start it + if (!mFaceLockServiceRunning) { + stopAndUnbindFromFaceLock(); + hideFaceLockArea(); + } + } + + //Ignore these events; they are implemented only because they come from the same interface @Override public void onTimeChanged() {} @Override @@ -1043,6 +1065,7 @@ public class LockPatternKeyguardView extends KeyguardViewBase implements Handler } private void showDialog(String title, String message) { + mHasDialog = true; final AlertDialog dialog = new AlertDialog.Builder(mContext) .setTitle(title) .setMessage(message) diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java index a42a433f1fe0..72856b8eda24 100755 --- a/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindowManager.java @@ -39,6 +39,7 @@ import android.database.ContentObserver; import android.graphics.PixelFormat; import android.graphics.Rect; import android.graphics.RectF; +import android.os.BatteryManager; import android.os.Binder; import android.os.Bundle; import android.os.Handler; @@ -157,6 +158,14 @@ public class PhoneWindowManager implements WindowManagerPolicy { static final boolean SHOW_STARTING_ANIMATIONS = true; static final boolean SHOW_PROCESSES_ON_ALT_MENU = false; + // Whether to allow dock apps with METADATA_DOCK_HOME to temporarily take over the Home key. + // No longer recommended for desk docks; still useful in car docks. + static final boolean ENABLE_CAR_DOCK_HOME_CAPTURE = true; + static final boolean ENABLE_DESK_DOCK_HOME_CAPTURE = false; + + // Should screen savers use their own timeout, or the SCREEN_OFF_TIMEOUT? + static final boolean SEPARATE_TIMEOUT_FOR_SCREEN_SAVER = false; + static final int LONG_PRESS_POWER_NOTHING = 0; static final int LONG_PRESS_POWER_GLOBAL_ACTIONS = 1; static final int LONG_PRESS_POWER_SHUT_OFF = 2; @@ -391,7 +400,9 @@ public class PhoneWindowManager implements WindowManagerPolicy { // visual screen saver support int mScreenSaverTimeout = 0; - boolean mScreenSaverEnabled = true; + boolean mScreenSaverEnabledByUser = false; + boolean mScreenSaverMayRun = true; // false if a wakelock is held + boolean mPluggedIn; // Behavior of ENDCALL Button. (See Settings.System.END_BUTTON_BEHAVIOR.) int mEndcallBehavior; @@ -456,7 +467,11 @@ public class PhoneWindowManager implements WindowManagerPolicy { resolver.registerContentObserver(Settings.System.getUriFor( "fancy_rotation_anim"), false, this); resolver.registerContentObserver(Settings.Secure.getUriFor( - Settings.Secure.DREAM_TIMEOUT), false, this); + Settings.Secure.SCREENSAVER_ENABLED), false, this); + if (SEPARATE_TIMEOUT_FOR_SCREEN_SAVER) { + resolver.registerContentObserver(Settings.Secure.getUriFor( + "screensaver_timeout"), false, this); + } // otherwise SCREEN_OFF_TIMEOUT will do nicely updateSettings(); } @@ -763,6 +778,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { mDockMode = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, Intent.EXTRA_DOCK_STATE_UNDOCKED); } + + // watch the plug to know whether to trigger the screen saver + filter = new IntentFilter(); + filter.addAction(Intent.ACTION_BATTERY_CHANGED); + intent = context.registerReceiver(mPowerReceiver, filter); + if (intent != null) { + mPluggedIn = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)); + } + mVibrator = new Vibrator(); mLongPressVibePattern = getLongIntArray(mContext.getResources(), com.android.internal.R.array.config_longPressVibePattern); @@ -912,8 +936,21 @@ public class PhoneWindowManager implements WindowManagerPolicy { updateRotation = true; } - mScreenSaverTimeout = Settings.Secure.getInt(resolver, - Settings.Secure.DREAM_TIMEOUT, 0); + mScreenSaverEnabledByUser = 0 != Settings.Secure.getInt(resolver, + Settings.Secure.SCREENSAVER_ENABLED, 1); + + if (SEPARATE_TIMEOUT_FOR_SCREEN_SAVER) { + mScreenSaverTimeout = Settings.Secure.getInt(resolver, + "screensaver_timeout", 0); + } else { + mScreenSaverTimeout = Settings.System.getInt(resolver, + Settings.System.SCREEN_OFF_TIMEOUT, 0); + if (mScreenSaverTimeout > 0) { + // We actually want to activate the screensaver just before the + // power manager's screen timeout + mScreenSaverTimeout -= 5000; + } + } updateScreenSaverTimeoutLocked(); } if (updateRotation) { @@ -2952,6 +2989,15 @@ public class PhoneWindowManager implements WindowManagerPolicy { } }; + BroadcastReceiver mPowerReceiver = new BroadcastReceiver() { + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_BATTERY_CHANGED.equals(intent.getAction())) { + mPluggedIn = (0 != intent.getIntExtra(BatteryManager.EXTRA_PLUGGED, 0)); + if (localLOGV) Log.v(TAG, "BATTERY_CHANGED: " + intent + " plugged=" + mPluggedIn); + } + } + }; + /** {@inheritDoc} */ public void screenTurnedOff(int why) { EventLog.writeEvent(70000, 0); @@ -3100,7 +3146,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { sensorRotation = lastRotation; } - int preferredRotation = -1; + final int preferredRotation; if (mHdmiPlugged) { // Ignore sensor when plugged into HDMI. preferredRotation = mHdmiRotation; @@ -3149,28 +3195,39 @@ public class PhoneWindowManager implements WindowManagerPolicy { } else if (mUserRotationMode == WindowManagerPolicy.USER_ROTATION_LOCKED) { // Apply rotation lock. preferredRotation = mUserRotation; + } else { + // No overriding preference. + // We will do exactly what the application asked us to do. + preferredRotation = -1; } - // TODO: Sometimes, we might want to override the application-requested - // orientation, such as when HDMI is plugged in or when docked. - // We can do that by modifying the appropriate cases above to return - // the preferred orientation directly instead of continuing on down here. - switch (orientation) { case ActivityInfo.SCREEN_ORIENTATION_PORTRAIT: - // Always return portrait if orientation set to portrait. + // Return portrait unless overridden. + if (isAnyPortrait(preferredRotation)) { + return preferredRotation; + } return mPortraitRotation; case ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE: - // Always return landscape if orientation set to landscape. + // Return landscape unless overridden. + if (isLandscapeOrSeascape(preferredRotation)) { + return preferredRotation; + } return mLandscapeRotation; case ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT: - // Always return portrait if orientation set to portrait. + // Return reverse portrait unless overridden. + if (isAnyPortrait(preferredRotation)) { + return preferredRotation; + } return mUpsideDownRotation; case ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE: - // Always return seascape if orientation set to reverse landscape. + // Return seascape unless overridden. + if (isLandscapeOrSeascape(preferredRotation)) { + return preferredRotation; + } return mSeascapeRotation; case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE: @@ -3403,22 +3460,35 @@ public class PhoneWindowManager implements WindowManagerPolicy { Runnable mScreenSaverActivator = new Runnable() { public void run() { - if (!(mScreenSaverEnabled && mScreenOnEarly)) { + if (!(mScreenSaverMayRun && mScreenOnEarly)) { Log.w(TAG, "mScreenSaverActivator ran, but the screensaver should not be showing. Who's driving this thing?"); return; } + if (!mPluggedIn) { + if (localLOGV) Log.v(TAG, "mScreenSaverActivator: not running screen saver when not plugged in"); + return; + } if (localLOGV) Log.v(TAG, "mScreenSaverActivator entering dreamland"); + try { String component = Settings.Secure.getString( - mContext.getContentResolver(), Settings.Secure.DREAM_COMPONENT); + mContext.getContentResolver(), Settings.Secure.SCREENSAVER_COMPONENT); + if (component == null) { + 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 { @@ -3436,13 +3506,13 @@ public class PhoneWindowManager implements WindowManagerPolicy { if (mScreenSaverActivator == null) return; mHandler.removeCallbacks(mScreenSaverActivator); - if (mScreenSaverEnabled && mScreenOnEarly && mScreenSaverTimeout > 0) { + if (mScreenSaverEnabledByUser && mScreenSaverMayRun && mScreenOnEarly && mScreenSaverTimeout > 0) { if (localLOGV) Log.v(TAG, "scheduling screensaver for " + mScreenSaverTimeout + "ms from now"); mHandler.postDelayed(mScreenSaverActivator, mScreenSaverTimeout); } else { if (localLOGV) { - if (mScreenSaverTimeout == 0) + if (!mScreenSaverEnabledByUser || mScreenSaverTimeout == 0) Log.v(TAG, "screen saver disabled by user"); else if (!mScreenOnEarly) Log.v(TAG, "screen saver disabled while screen off"); @@ -3500,21 +3570,35 @@ public class PhoneWindowManager implements WindowManagerPolicy { } /** - * Return an Intent to launch the currently active dock as home. Returns - * null if the standard home should be launched. + * Return an Intent to launch the currently active dock app as home. Returns + * null if the standard home should be launched, which is the case if any of the following is + * true: + * <ul> + * <li>The device is not in either car mode or desk mode + * <li>The device is in car mode but ENABLE_CAR_DOCK_HOME_CAPTURE is false + * <li>The device is in desk mode but ENABLE_DESK_DOCK_HOME_CAPTURE is false + * <li>The device is in car mode but there's no CAR_DOCK app with METADATA_DOCK_HOME + * <li>The device is in desk mode but there's no DESK_DOCK app with METADATA_DOCK_HOME + * </ul> * @return */ Intent createHomeDockIntent() { - Intent intent; + Intent intent = null; // What home does is based on the mode, not the dock state. That // is, when in car mode you should be taken to car home regardless // of whether we are actually in a car dock. if (mUiMode == Configuration.UI_MODE_TYPE_CAR) { - intent = mCarDockIntent; + if (ENABLE_CAR_DOCK_HOME_CAPTURE) { + intent = mCarDockIntent; + } } else if (mUiMode == Configuration.UI_MODE_TYPE_DESK) { - intent = mDeskDockIntent; - } else { + if (ENABLE_DESK_DOCK_HOME_CAPTURE) { + intent = mDeskDockIntent; + } + } + + if (intent == null) { return null; } @@ -3644,7 +3728,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { // The window manager has just grabbed a wake lock. This is our cue to disable the screen // saver. synchronized (mLock) { - mScreenSaverEnabled = false; + mScreenSaverMayRun = false; } } @@ -3658,7 +3742,7 @@ public class PhoneWindowManager implements WindowManagerPolicy { synchronized (mLock) { // even if the keyguard is up, now that all the wakelocks have been released, we // should re-enable the screen saver - mScreenSaverEnabled = true; + mScreenSaverMayRun = true; updateScreenSaverTimeoutLocked(); } } diff --git a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java index 47a715767c4e..ba06996064fe 100644 --- a/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/SimPukUnlockScreen.java @@ -242,20 +242,24 @@ public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen, new CheckSimPuk(mPukText.getText().toString(), mPinText.getText().toString()) { - void onSimLockChangedResponse(boolean success) { - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - if (success) { - // before closing the keyguard, report back that - // the sim is unlocked so it knows right away - mUpdateMonitor.reportSimUnlocked(); - mCallback.goToUnlockScreen(); - } else { - mHeaderText.setText(R.string.badPuk); - mPukText.setText(""); - mPinText.setText(""); - } + void onSimLockChangedResponse(final boolean success) { + mPinText.post(new Runnable() { + public void run() { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + if (success) { + // before closing the keyguard, report back that + // the sim is unlocked so it knows right away + mUpdateMonitor.reportSimUnlocked(); + mCallback.goToUnlockScreen(); + } else { + mHeaderText.setText(R.string.badPuk); + mPukText.setText(""); + mPinText.setText(""); + } + } + }); } }.start(); } @@ -379,6 +383,9 @@ public class SimPukUnlockScreen extends LinearLayout implements KeyguardScreen, public void onClick(View v) { if (v == mCancelButton) { + // clear the PIN/PUK entry fields if the user cancels + mPinText.setText(""); + mPukText.setText(""); mCallback.goToLockScreen(); return; } diff --git a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java index 99e1ce10cfb6..9604cdc6e7b0 100644 --- a/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java +++ b/policy/src/com/android/internal/policy/impl/SimUnlockScreen.java @@ -214,21 +214,25 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie getSimUnlockProgressDialog().show(); new CheckSimPin(mPinText.getText().toString()) { - void onSimLockChangedResponse(boolean success) { - if (mSimUnlockProgressDialog != null) { - mSimUnlockProgressDialog.hide(); - } - if (success) { - // before closing the keyguard, report back that - // the sim is unlocked so it knows right away - mUpdateMonitor.reportSimUnlocked(); - mCallback.goToUnlockScreen(); - } else { - mHeaderText.setText(R.string.keyguard_password_wrong_pin_code); - mPinText.setText(""); - mEnteredDigits = 0; - } - mCallback.pokeWakelock(); + void onSimLockChangedResponse(final boolean success) { + mPinText.post(new Runnable() { + public void run() { + if (mSimUnlockProgressDialog != null) { + mSimUnlockProgressDialog.hide(); + } + if (success) { + // before closing the keyguard, report back that + // the sim is unlocked so it knows right away + mUpdateMonitor.reportSimUnlocked(); + mCallback.goToUnlockScreen(); + } else { + mHeaderText.setText(R.string.keyguard_password_wrong_pin_code); + mPinText.setText(""); + mEnteredDigits = 0; + } + mCallback.pokeWakelock(); + } + }); } }.start(); } @@ -355,6 +359,7 @@ public class SimUnlockScreen extends LinearLayout implements KeyguardScreen, Vie public void onClick(View v) { if (v == mCancelButton) { + mPinText.setText(""); // clear the PIN entry field if the user cancels mCallback.goToLockScreen(); return; } diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 66537dfebe82..66eb63ac8649 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -89,6 +89,12 @@ static const int kRecordThreadSleepUs = 5000; static const nsecs_t kSetParametersTimeout = seconds(2); +// minimum sleep time for the mixer thread loop when tracks are active but in underrun +static const uint32_t kMinThreadSleepTimeUs = 5000; +// maximum divider applied to the active sleep time in the mixer thread loop +static const uint32_t kMaxThreadSleepTimeShift = 2; + + // ---------------------------------------------------------------------------- static bool recordingAllowed() { @@ -1846,6 +1852,7 @@ bool AudioFlinger::MixerThread::threadLoop() uint32_t activeSleepTime = activeSleepTimeUs(); uint32_t idleSleepTime = idleSleepTimeUs(); uint32_t sleepTime = idleSleepTime; + uint32_t sleepTimeShift = 0; Vector< sp<EffectChain> > effectChains; #ifdef DEBUG_CPU_USAGE ThreadCpuUsage cpu; @@ -1937,6 +1944,7 @@ bool AudioFlinger::MixerThread::threadLoop() standbyTime = systemTime() + kStandbyTimeInNsecs; sleepTime = idleSleepTime; + sleepTimeShift = 0; continue; } } @@ -1953,6 +1961,10 @@ bool AudioFlinger::MixerThread::threadLoop() // mix buffers... mAudioMixer->process(); sleepTime = 0; + // increase sleep time progressively when application underrun condition clears + if (sleepTimeShift > 0) { + sleepTimeShift--; + } standbyTime = systemTime() + kStandbyTimeInNsecs; //TODO: delay standby when effects have a tail } else { @@ -1960,7 +1972,17 @@ bool AudioFlinger::MixerThread::threadLoop() // buffer size, then write 0s to the output if (sleepTime == 0) { if (mixerStatus == MIXER_TRACKS_ENABLED) { - sleepTime = activeSleepTime; + sleepTime = activeSleepTime >> sleepTimeShift; + if (sleepTime < kMinThreadSleepTimeUs) { + sleepTime = kMinThreadSleepTimeUs; + } + // reduce sleep time in case of consecutive application underruns to avoid + // starving the audio HAL. As activeSleepTimeUs() is larger than a buffer + // duration we would end up writing less data than needed by the audio HAL if + // the condition persists. + if (sleepTimeShift < kMaxThreadSleepTimeShift) { + sleepTimeShift++; + } } else { sleepTime = idleSleepTime; } diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index 906794b0256b..e945ce0d21d7 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -33,7 +33,7 @@ namespace android { #ifdef __ARM_HAVE_HALFWORD_MULTIPLY // optimized asm option - //#define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 + #define ASM_ARM_RESAMP1 // enable asm optimisation for ResamplerOrder1 #endif // __ARM_HAVE_HALFWORD_MULTIPLY // ---------------------------------------------------------------------------- @@ -390,6 +390,7 @@ resampleMono16_exit: * phaseFraction : phase fraction for next interpolation * *******************************************************************/ +__attribute__((noinline)) void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, uint32_t &phaseFraction, uint32_t phaseIncrement) @@ -500,6 +501,7 @@ void AudioResamplerOrder1::AsmMono16Loop(int16_t *in, int32_t* maxOutPt, int32_t * phaseFraction : phase fraction for next interpolation * *******************************************************************/ +__attribute__((noinline)) void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32_t maxInIdx, size_t &outputIndex, int32_t* out, size_t &inputIndex, int32_t vl, int32_t vr, uint32_t &phaseFraction, uint32_t phaseIncrement) @@ -600,6 +602,5 @@ void AudioResamplerOrder1::AsmStereo16Loop(int16_t *in, int32_t* maxOutPt, int32 // ---------------------------------------------------------------------------- -} -; // namespace android +} // namespace android diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 39f8efe24034..838a03f52f18 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -507,6 +507,15 @@ void EventHub::getVirtualKeyDefinitions(int32_t deviceId, } } +String8 EventHub::getKeyCharacterMapFile(int32_t deviceId) const { + AutoMutex _l(mLock); + Device* device = getDeviceLocked(deviceId); + if (device) { + return device->keyMap.keyCharacterMapFile; + } + return String8(); +} + EventHub::Device* EventHub::getDeviceLocked(int32_t deviceId) const { if (deviceId == 0) { deviceId = mBuiltInKeyboardId; @@ -1013,16 +1022,12 @@ status_t EventHub::openDeviceLocked(const char *devicePath) { // Configure the keyboard, gamepad or virtual keyboard. if (device->classes & INPUT_DEVICE_CLASS_KEYBOARD) { - // Set system properties for the keyboard. - setKeyboardPropertiesLocked(device, false); - // Register the keyboard as a built-in keyboard if it is eligible. if (!keyMapStatus && mBuiltInKeyboardId == -1 && isEligibleBuiltInKeyboard(device->identifier, device->configuration, &device->keyMap)) { mBuiltInKeyboardId = device->id; - setKeyboardPropertiesLocked(device, true); } // 'Q' key support = cheap test of whether this is an alpha-capable kbd @@ -1120,17 +1125,6 @@ status_t EventHub::loadKeyMapLocked(Device* device) { return device->keyMap.load(device->identifier, device->configuration); } -void EventHub::setKeyboardPropertiesLocked(Device* device, bool builtInKeyboard) { - int32_t id = builtInKeyboard ? 0 : device->id; - android::setKeyboardProperties(id, device->identifier, - device->keyMap.keyLayoutFile, device->keyMap.keyCharacterMapFile); -} - -void EventHub::clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboard) { - int32_t id = builtInKeyboard ? 0 : device->id; - android::clearKeyboardProperties(id); -} - bool EventHub::isExternalDeviceLocked(Device* device) { if (device->configuration) { bool value; @@ -1184,9 +1178,7 @@ void EventHub::closeDeviceLocked(Device* device) { LOGW("built-in keyboard device %s (id=%d) is closing! the apps will not like this", device->path.string(), mBuiltInKeyboardId); mBuiltInKeyboardId = -1; - clearKeyboardPropertiesLocked(device, true); } - clearKeyboardPropertiesLocked(device, false); if (epoll_ctl(mEpollFd, EPOLL_CTL_DEL, device->fd, NULL)) { LOGW("Could not remove device fd from epoll instance. errno=%d", errno); diff --git a/services/input/EventHub.h b/services/input/EventHub.h index d37549a849e7..9d8252ed4763 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -208,6 +208,8 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector<VirtualKeyDefinition>& outVirtualKeys) const = 0; + virtual String8 getKeyCharacterMapFile(int32_t deviceId) const = 0; + /* Requests the EventHub to reopen all input devices on the next call to getEvents(). */ virtual void requestReopenDevices() = 0; @@ -264,6 +266,8 @@ public: virtual void getVirtualKeyDefinitions(int32_t deviceId, Vector<VirtualKeyDefinition>& outVirtualKeys) const; + virtual String8 getKeyCharacterMapFile(int32_t deviceId) const; + virtual void requestReopenDevices(); virtual void wake(); @@ -321,8 +325,6 @@ private: void loadConfigurationLocked(Device* device); status_t loadVirtualKeyMapLocked(Device* device); status_t loadKeyMapLocked(Device* device); - void setKeyboardPropertiesLocked(Device* device, bool builtInKeyboard); - void clearKeyboardPropertiesLocked(Device* device, bool builtInKeyboard); bool isExternalDeviceLocked(Device* device); diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 9a73527db9d3..382987b33238 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -1783,6 +1783,7 @@ void KeyboardInputMapper::populateDeviceInfo(InputDeviceInfo* info) { InputMapper::populateDeviceInfo(info); info->setKeyboardType(mKeyboardType); + info->setKeyCharacterMapFile(getEventHub()->getKeyCharacterMapFile(getDeviceId())); } void KeyboardInputMapper::dump(String8& dump) { diff --git a/services/input/tests/InputReader_test.cpp b/services/input/tests/InputReader_test.cpp index a0862082f208..08efe7dcdd92 100644 --- a/services/input/tests/InputReader_test.cpp +++ b/services/input/tests/InputReader_test.cpp @@ -609,6 +609,10 @@ private: } } + virtual String8 getKeyCharacterMapFile(int32_t deviceId) const { + return String8(); + } + virtual bool isExternal(int32_t deviceId) const { return false; } diff --git a/services/java/com/android/server/EntropyService.java b/services/java/com/android/server/EntropyMixer.java index 788a2f53bbdf..b63a70ecc548 100644 --- a/services/java/com/android/server/EntropyService.java +++ b/services/java/com/android/server/EntropyMixer.java @@ -47,8 +47,8 @@ import android.util.Slog; * <p>TODO: Investigate attempting to write entropy data at shutdown time * instead of periodically. */ -public class EntropyService extends Binder { - private static final String TAG = "EntropyService"; +public class EntropyMixer extends Binder { + private static final String TAG = "EntropyMixer"; private static final int ENTROPY_WHAT = 1; private static final int ENTROPY_WRITE_PERIOD = 3 * 60 * 60 * 1000; // 3 hrs private static final long START_TIME = System.currentTimeMillis(); @@ -72,12 +72,12 @@ public class EntropyService extends Binder { } }; - public EntropyService() { + public EntropyMixer() { this(getSystemDir() + "/entropy.dat", "/dev/urandom"); } /** Test only interface, not for public use */ - public EntropyService(String entropyFile, String randomDevice) { + public EntropyMixer(String entropyFile, String randomDevice) { if (randomDevice == null) { throw new NullPointerException("randomDevice"); } if (entropyFile == null) { throw new NullPointerException("entropyFile"); } diff --git a/services/java/com/android/server/EventLogTags.logtags b/services/java/com/android/server/EventLogTags.logtags index a7eff935c99c..54084361a28a 100644 --- a/services/java/com/android/server/EventLogTags.logtags +++ b/services/java/com/android/server/EventLogTags.logtags @@ -142,5 +142,5 @@ option java_package com.android.server # --------------------------- # NetworkStatsService.java # --------------------------- -51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3) -51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3) +51100 netstats_mobile_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3),(dev_history_start|2|3) +51101 netstats_wifi_sample (dev_rx_bytes|2|2),(dev_tx_bytes|2|2),(dev_rx_pkts|2|1),(dev_tx_pkts|2|1),(xt_rx_bytes|2|2),(xt_tx_bytes|2|2),(xt_rx_pkts|2|1),(xt_tx_pkts|2|1),(uid_rx_bytes|2|2),(uid_tx_bytes|2|2),(uid_rx_pkts|2|1),(uid_tx_pkts|2|1),(trusted_time|2|3),(dev_history_start|2|3) diff --git a/services/java/com/android/server/InputMethodManagerService.java b/services/java/com/android/server/InputMethodManagerService.java index ddac35c5d533..f5c4ed46c411 100644 --- a/services/java/com/android/server/InputMethodManagerService.java +++ b/services/java/com/android/server/InputMethodManagerService.java @@ -382,6 +382,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { mScreenOn = true; + refreshImeWindowVisibilityLocked(); } else if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { mScreenOn = false; setImeWindowVisibilityStatusHiddenLocked(); @@ -640,6 +641,21 @@ public class InputMethodManagerService extends IInputMethodManager.Stub updateImeWindowStatusLocked(); } + private void refreshImeWindowVisibilityLocked() { + final Configuration conf = mRes.getConfiguration(); + final boolean haveHardKeyboard = conf.keyboard + != Configuration.KEYBOARD_NOKEYS; + final boolean hardKeyShown = haveHardKeyboard + && conf.hardKeyboardHidden + != Configuration.HARDKEYBOARDHIDDEN_YES; + final boolean isScreenLocked = mKeyguardManager != null + && mKeyguardManager.isKeyguardLocked() + && mKeyguardManager.isKeyguardSecure(); + mImeWindowVis = (!isScreenLocked && (mInputShown || hardKeyShown)) ? + (InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) : 0; + updateImeWindowStatusLocked(); + } + private void updateImeWindowStatusLocked() { setImeWindowStatus(mCurToken, mImeWindowVis, mBackDisposition); } @@ -1285,16 +1301,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } if (mCurMethod != null) { try { - final Configuration conf = mRes.getConfiguration(); - final boolean haveHardKeyboard = conf.keyboard - != Configuration.KEYBOARD_NOKEYS; - final boolean hardKeyShown = haveHardKeyboard - && conf.hardKeyboardHidden - != Configuration.HARDKEYBOARDHIDDEN_YES; - mImeWindowVis = (mInputShown || hardKeyShown) ? ( - InputMethodService.IME_ACTIVE | InputMethodService.IME_VISIBLE) - : 0; - updateImeWindowStatusLocked(); + refreshImeWindowVisibilityLocked(); // If subtype is null, try to find the most applicable one from // getCurrentInputMethodSubtype. if (subtype == null) { diff --git a/services/java/com/android/server/NetworkManagementService.java b/services/java/com/android/server/NetworkManagementService.java index da960aeae5e3..9d808e16196d 100644 --- a/services/java/com/android/server/NetworkManagementService.java +++ b/services/java/com/android/server/NetworkManagementService.java @@ -18,8 +18,10 @@ package com.android.server; import static android.Manifest.permission.ACCESS_NETWORK_STATE; import static android.Manifest.permission.CHANGE_NETWORK_STATE; +import static android.Manifest.permission.CHANGE_WIFI_STATE; import static android.Manifest.permission.DUMP; import static android.Manifest.permission.MANAGE_NETWORK_POLICY; +import static android.Manifest.permission.SHUTDOWN; import static android.net.NetworkStats.SET_DEFAULT; import static android.net.NetworkStats.TAG_NONE; import static android.net.NetworkStats.UID_ALL; @@ -28,7 +30,6 @@ import static android.provider.Settings.Secure.NETSTATS_ENABLED; import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED; import android.content.Context; -import android.content.pm.PackageManager; import android.net.INetworkManagementEventObserver; import android.net.InterfaceConfiguration; import android.net.LinkAddress; @@ -193,11 +194,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub SystemProperties.set(PROP_QTAGUID_ENABLED, mBandwidthControlEnabled ? "1" : "0"); } + @Override public void registerObserver(INetworkManagementEventObserver obs) { Slog.d(TAG, "Registering observer"); mObservers.add(obs); } + @Override public void unregisterObserver(INetworkManagementEventObserver obs) { Slog.d(TAG, "Unregistering observer"); mObservers.remove(mObservers.indexOf(obs)); @@ -351,10 +354,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub // INetworkManagementService members // - public String[] listInterfaces() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); - + @Override + public String[] listInterfaces() { + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); try { return mConnector.doListCommand("interface list", NetdResponseCode.InterfaceListResult); } catch (NativeDaemonConnectorException e) { @@ -363,7 +365,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public InterfaceConfiguration getInterfaceConfig(String iface) throws IllegalStateException { + @Override + public InterfaceConfiguration getInterfaceConfig(String iface) { mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); String rsp; try { @@ -417,8 +420,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub return cfg; } - public void setInterfaceConfig( - String iface, InterfaceConfiguration cfg) throws IllegalStateException { + @Override + public void setInterfaceConfig(String iface, InterfaceConfiguration cfg) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); LinkAddress linkAddr = cfg.addr; if (linkAddr == null || linkAddr.getAddress() == null) { @@ -436,7 +439,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void setInterfaceDown(String iface) throws IllegalStateException { + @Override + public void setInterfaceDown(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { InterfaceConfiguration ifcg = getInterfaceConfig(iface); @@ -448,7 +452,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void setInterfaceUp(String iface) throws IllegalStateException { + @Override + public void setInterfaceUp(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { InterfaceConfiguration ifcg = getInterfaceConfig(iface); @@ -460,8 +465,8 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) - throws IllegalStateException { + @Override + public void setInterfaceIpv6PrivacyExtensions(String iface, boolean enable) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); String cmd = String.format("interface ipv6privacyextensions %s %s", iface, enable ? "enable" : "disable"); @@ -473,11 +478,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - - /* TODO: This is right now a IPv4 only function. Works for wifi which loses its IPv6 addresses on interface down, but we need to do full clean up here */ - public void clearInterfaceAddresses(String iface) throws IllegalStateException { + @Override + public void clearInterfaceAddresses(String iface) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); String cmd = String.format("interface clearaddrs %s", iface); try { @@ -488,9 +492,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void enableIpv6(String iface) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void enableIpv6(String iface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand(String.format("interface ipv6 %s enable", iface)); } catch (NativeDaemonConnectorException e) { @@ -499,9 +503,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void disableIpv6(String iface) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void disableIpv6(String iface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand(String.format("interface ipv6 %s disable", iface)); } catch (NativeDaemonConnectorException e) { @@ -510,21 +514,25 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + @Override public void addRoute(String interfaceName, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); modifyRoute(interfaceName, ADD, route, DEFAULT); } + @Override public void removeRoute(String interfaceName, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); modifyRoute(interfaceName, REMOVE, route, DEFAULT); } + @Override public void addSecondaryRoute(String interfaceName, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); modifyRoute(interfaceName, ADD, route, SECONDARY); } + @Override public void removeSecondaryRoute(String interfaceName, RouteInfo route) { mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); modifyRoute(interfaceName, REMOVE, route, SECONDARY); @@ -609,6 +617,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub return list; } + @Override public RouteInfo[] getRoutes(String interfaceName) { mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); ArrayList<RouteInfo> routes = new ArrayList<RouteInfo>(); @@ -683,19 +692,17 @@ public class NetworkManagementService extends INetworkManagementService.Stub return (RouteInfo[]) routes.toArray(new RouteInfo[0]); } + @Override public void shutdown() { - if (mContext.checkCallingOrSelfPermission( - android.Manifest.permission.SHUTDOWN) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Requires SHUTDOWN permission"); - } + // TODO: remove from aidl if nobody calls externally + mContext.enforceCallingOrSelfPermission(SHUTDOWN, TAG); Slog.d(TAG, "Shutting down"); } + @Override public boolean getIpForwardingEnabled() throws IllegalStateException{ - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); ArrayList<String> rsp; try { @@ -723,16 +730,15 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException("Got an empty response"); } - public void setIpForwardingEnabled(boolean enable) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void setIpForwardingEnabled(boolean enable) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); mConnector.doCommand(String.format("ipfwd %sable", (enable ? "en" : "dis"))); } - public void startTethering(String[] dhcpRange) - throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void startTethering(String[] dhcpRange) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); // cmd is "tether start first_start first_stop second_start second_stop ..." // an odd number of addrs will fail String cmd = "tether start"; @@ -747,9 +753,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void stopTethering() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void stopTethering() { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand("tether stop"); } catch (NativeDaemonConnectorException e) { @@ -757,9 +763,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public boolean isTetheringStarted() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + @Override + public boolean isTetheringStarted() { + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); ArrayList<String> rsp; try { @@ -785,9 +791,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub throw new IllegalStateException("Got an empty response"); } - public void tetherInterface(String iface) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void tetherInterface(String iface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand("tether interface add " + iface); } catch (NativeDaemonConnectorException e) { @@ -796,9 +802,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + @Override public void untetherInterface(String iface) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand("tether interface remove " + iface); } catch (NativeDaemonConnectorException e) { @@ -807,9 +813,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public String[] listTetheredInterfaces() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + @Override + public String[] listTetheredInterfaces() { + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); try { return mConnector.doListCommand( "tether interface list", NetdResponseCode.TetherInterfaceListResult); @@ -819,9 +825,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void setDnsForwarders(String[] dns) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void setDnsForwarders(String[] dns) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { String cmd = "tether dns set"; for (String s : dns) { @@ -838,9 +844,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public String[] getDnsForwarders() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + @Override + public String[] getDnsForwarders() { + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); try { return mConnector.doListCommand( "tether dns list", NetdResponseCode.TetherDnsFwdTgtListResult); @@ -856,22 +862,25 @@ public class NetworkManagementService extends INetworkManagementService.Stub NetworkInterface internalNetworkInterface = NetworkInterface.getByName(internalInterface); - Collection<InterfaceAddress>interfaceAddresses = - internalNetworkInterface.getInterfaceAddresses(); - cmd += " " + interfaceAddresses.size(); - for (InterfaceAddress ia : interfaceAddresses) { - InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(), - ia.getNetworkPrefixLength()); - cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength(); + if (internalNetworkInterface == null) { + cmd += " 0"; + } else { + Collection<InterfaceAddress>interfaceAddresses = + internalNetworkInterface.getInterfaceAddresses(); + cmd += " " + interfaceAddresses.size(); + for (InterfaceAddress ia : interfaceAddresses) { + InetAddress addr = NetworkUtils.getNetworkPart(ia.getAddress(), + ia.getNetworkPrefixLength()); + cmd = cmd + " " + addr.getHostAddress() + "/" + ia.getNetworkPrefixLength(); + } } mConnector.doCommand(cmd); } - public void enableNat(String internalInterface, String externalInterface) - throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void enableNat(String internalInterface, String externalInterface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); if (DBG) Log.d(TAG, "enableNat(" + internalInterface + ", " + externalInterface + ")"); try { modifyNat("enable", internalInterface, externalInterface); @@ -882,10 +891,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void disableNat(String internalInterface, String externalInterface) - throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void disableNat(String internalInterface, String externalInterface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); if (DBG) Log.d(TAG, "disableNat(" + internalInterface + ", " + externalInterface + ")"); try { modifyNat("disable", internalInterface, externalInterface); @@ -896,9 +904,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public String[] listTtys() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + @Override + public String[] listTtys() { + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); try { return mConnector.doListCommand("list_ttys", NetdResponseCode.TtyListResult); } catch (NativeDaemonConnectorException e) { @@ -907,11 +915,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void attachPppd(String tty, String localAddr, String remoteAddr, String dns1Addr, - String dns2Addr) throws IllegalStateException { + @Override + public void attachPppd( + String tty, String localAddr, String remoteAddr, String dns1Addr, String dns2Addr) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); mConnector.doCommand(String.format("pppd attach %s %s %s %s %s", tty, NetworkUtils.numericToInetAddress(localAddr).getHostAddress(), NetworkUtils.numericToInetAddress(remoteAddr).getHostAddress(), @@ -924,9 +932,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void detachPppd(String tty) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void detachPppd(String tty) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand(String.format("pppd detach %s", tty)); } catch (NativeDaemonConnectorException e) { @@ -934,12 +942,11 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void startAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) - throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + @Override + public void startAccessPoint( + WifiConfiguration wifiConfig, String wlanIface, String softapIface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); + mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG); try { wifiFirmwareReload(wlanIface, "AP"); mConnector.doCommand(String.format("softap start " + wlanIface)); @@ -989,12 +996,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } /* @param mode can be "AP", "STA" or "P2P" */ - public void wifiFirmwareReload(String wlanIface, String mode) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); - + @Override + public void wifiFirmwareReload(String wlanIface, String mode) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); + mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG); try { mConnector.doCommand(String.format("softap fwreload " + wlanIface + " " + mode)); } catch (NativeDaemonConnectorException e) { @@ -1002,11 +1007,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void stopAccessPoint(String wlanIface) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + @Override + public void stopAccessPoint(String wlanIface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); + mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG); try { mConnector.doCommand("softap stopap"); mConnector.doCommand("softap stop " + wlanIface); @@ -1017,12 +1021,10 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) - throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_WIFI_STATE, "NetworkManagementService"); + @Override + public void setAccessPoint(WifiConfiguration wifiConfig, String wlanIface, String softapIface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); + mContext.enforceCallingOrSelfPermission(CHANGE_WIFI_STATE, TAG); try { if (wifiConfig == null) { mConnector.doCommand(String.format("softap set " + wlanIface + " " + softapIface)); @@ -1040,8 +1042,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub } private long getInterfaceCounter(String iface, boolean rx) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); try { String rsp; try { @@ -1081,15 +1082,13 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public NetworkStats getNetworkStatsSummary() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); return mStatsFactory.readNetworkStatsSummary(); } @Override public NetworkStats getNetworkStatsDetail() { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); return mStatsFactory.readNetworkStatsDetail(UID_ALL); } @@ -1272,16 +1271,14 @@ public class NetworkManagementService extends INetworkManagementService.Stub @Override public NetworkStats getNetworkStatsUidDetail(int uid) { if (Binder.getCallingUid() != uid) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); } return mStatsFactory.readNetworkStatsDetail(uid); } @Override public NetworkStats getNetworkStatsTethering(String[] ifacePairs) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); if (ifacePairs.length % 2 != 0) { throw new IllegalArgumentException( @@ -1345,9 +1342,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } + @Override public void setInterfaceThrottle(String iface, int rxKbps, int txKbps) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { mConnector.doCommand(String.format( "interface setthrottle %s %d %d", iface, rxKbps, txKbps)); @@ -1357,8 +1354,7 @@ public class NetworkManagementService extends INetworkManagementService.Stub } private int getInterfaceThrottle(String iface, boolean rx) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.ACCESS_NETWORK_STATE, "NetworkManagementService"); + mContext.enforceCallingOrSelfPermission(ACCESS_NETWORK_STATE, TAG); try { String rsp; try { @@ -1396,17 +1392,19 @@ public class NetworkManagementService extends INetworkManagementService.Stub return -1; } + @Override public int getInterfaceRxThrottle(String iface) { return getInterfaceThrottle(iface, true); } + @Override public int getInterfaceTxThrottle(String iface) { return getInterfaceThrottle(iface, false); } - public void setDefaultInterfaceForDns(String iface) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void setDefaultInterfaceForDns(String iface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { String cmd = "resolver setdefaultif " + iface; @@ -1417,10 +1415,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void setDnsServersForInterface(String iface, String[] servers) - throws IllegalStateException { - mContext.enforceCallingOrSelfPermission(android.Manifest.permission.CHANGE_NETWORK_STATE, - "NetworkManagementService"); + @Override + public void setDnsServersForInterface(String iface, String[] servers) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { String cmd = "resolver setifdns " + iface; for (String s : servers) { @@ -1438,9 +1435,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void flushDefaultDnsCache() throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void flushDefaultDnsCache() { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { String cmd = "resolver flushdefaultif"; @@ -1451,9 +1448,9 @@ public class NetworkManagementService extends INetworkManagementService.Stub } } - public void flushInterfaceDnsCache(String iface) throws IllegalStateException { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.CHANGE_NETWORK_STATE, "NetworkManagementService"); + @Override + public void flushInterfaceDnsCache(String iface) { + mContext.enforceCallingOrSelfPermission(CHANGE_NETWORK_STATE, TAG); try { String cmd = "resolver flushif " + iface; diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 3ae62add369f..762acbbc3de8 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -133,8 +133,8 @@ class ServerThread extends Thread { // Critical services... try { - Slog.i(TAG, "Entropy Service"); - ServiceManager.addService("entropy", new EntropyService()); + Slog.i(TAG, "Entropy Mixer"); + ServiceManager.addService("entropy", new EntropyMixer()); Slog.i(TAG, "Power Manager"); power = new PowerManagerService(); diff --git a/services/java/com/android/server/TextServicesManagerService.java b/services/java/com/android/server/TextServicesManagerService.java index af9152df84e5..d04b4404ae40 100644 --- a/services/java/com/android/server/TextServicesManagerService.java +++ b/services/java/com/android/server/TextServicesManagerService.java @@ -360,7 +360,7 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } final String sciId = info.getId(); final InternalServiceConnection connection = new InternalServiceConnection( - sciId, locale, scListener, bundle); + sciId, locale, bundle); final Intent serviceIntent = new Intent(SpellCheckerService.SERVICE_INTERFACE); serviceIntent.setComponent(info.getComponent()); if (DBG) { @@ -704,15 +704,13 @@ public class TextServicesManagerService extends ITextServicesManager.Stub { } private class InternalServiceConnection implements ServiceConnection { - private final ISpellCheckerSessionListener mListener; private final String mSciId; private final String mLocale; private final Bundle mBundle; public InternalServiceConnection( - String id, String locale, ISpellCheckerSessionListener listener, Bundle bundle) { + String id, String locale, Bundle bundle) { mSciId = id; mLocale = locale; - mListener = listener; mBundle = bundle; } diff --git a/services/java/com/android/server/UiModeManagerService.java b/services/java/com/android/server/UiModeManagerService.java index 280b32941571..e6392d746daa 100644 --- a/services/java/com/android/server/UiModeManagerService.java +++ b/services/java/com/android/server/UiModeManagerService.java @@ -63,6 +63,10 @@ class UiModeManagerService extends IUiModeManager.Stub { private static final String KEY_LAST_UPDATE_INTERVAL = "LAST_UPDATE_INTERVAL"; + // Enable launching of applications when entering the dock. + private static final boolean ENABLE_LAUNCH_CAR_DOCK_APP = true; + private static final boolean ENABLE_LAUNCH_DESK_DOCK_APP = false; + private static final int MSG_UPDATE_TWILIGHT = 0; private static final int MSG_ENABLE_LOCATION_UPDATES = 1; private static final int MSG_GET_NEW_LOCATION_UPDATE = 2; @@ -139,14 +143,16 @@ class UiModeManagerService extends IUiModeManager.Stub { if (UiModeManager.ACTION_ENTER_CAR_MODE.equals(intent.getAction())) { // Only launch car home when car mode is enabled and the caller // has asked us to switch to it. - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_CAR_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { category = Intent.CATEGORY_CAR_DOCK; } } else if (UiModeManager.ACTION_ENTER_DESK_MODE.equals(intent.getAction())) { // Only launch car home when desk mode is enabled and the caller // has asked us to switch to it. Currently re-using the car // mode flag since we don't have a formal API for "desk mode". - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_DESK_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { category = Intent.CATEGORY_DESK_DOCK; } } else { @@ -550,11 +556,13 @@ class UiModeManagerService extends IUiModeManager.Stub { } else { Intent homeIntent = null; if (mCarModeEnabled) { - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_CAR_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { homeIntent = buildHomeIntent(Intent.CATEGORY_CAR_DOCK); } } else if (isDeskDockState(mDockState)) { - if ((enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { + if (ENABLE_LAUNCH_DESK_DOCK_APP + && (enableFlags&UiModeManager.ENABLE_CAR_MODE_GO_CAR_HOME) != 0) { homeIntent = buildHomeIntent(Intent.CATEGORY_DESK_DOCK); } } else { diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 39e8c726b6ed..80234771b466 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -1211,8 +1211,8 @@ public final class ActivityManagerService extends ActivityManagerNative } synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); - if (now < (mLastMemUsageReportTime+10000)) { - // Don't report more than every 10 seconds to somewhat + if (now < (mLastMemUsageReportTime+5*60*1000)) { + // Don't report more than every 5 minutes to somewhat // avoid spamming. return; } @@ -1249,21 +1249,24 @@ public final class ActivityManagerService extends ActivityManagerNative StringWriter catSw = new StringWriter(); PrintWriter catPw = new PrintWriter(catSw); String[] emptyArgs = new String[] { }; - dumpApplicationMemoryUsage(null, pw, " ", emptyArgs, true, catPw); - pw.println(); - dumpProcessesLocked(null, pw, emptyArgs, 0, false); - pw.println(); - dumpServicesLocked(null, pw, emptyArgs, 0, false, false); - pw.println(); - dumpActivitiesLocked(null, pw, emptyArgs, 0, false, false); + StringBuilder tag = new StringBuilder(128); + synchronized (ActivityManagerService.this) { + dumpProcessesLocked(null, catPw, emptyArgs, 0, false, null); + catPw.println(); + dumpServicesLocked(null, catPw, emptyArgs, 0, false, false, null); + catPw.println(); + dumpActivitiesLocked(null, catPw, emptyArgs, 0, false, false, null); + catPw.println(); + } + tag.append("Low on memory -- "); + dumpApplicationMemoryUsage(null, pw, " ", emptyArgs, true, catPw, tag); String memUsage = sw.toString(); dropBuilder.append('\n'); dropBuilder.append(memUsage); dropBuilder.append(catSw.toString()); logBuilder.append(memUsage); - addErrorToDropBox("watchdog", null, "system_server", null, - null, "Low on memory -- no more background processes", - dropBuilder.toString(), null, null); + addErrorToDropBox("lowmem", null, "system_server", null, + null, tag.toString(), dropBuilder.toString(), null, null); Slog.i(TAG, logBuilder.toString()); synchronized (ActivityManagerService.this) { long now = SystemClock.uptimeMillis(); @@ -1416,7 +1419,8 @@ public final class ActivityManagerService extends ActivityManagerNative return; } - mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, false, null); + mActivityManagerService.dumpApplicationMemoryUsage(fd, pw, " ", args, + false, null, null); } } @@ -1677,7 +1681,9 @@ public final class ActivityManagerService extends ActivityManagerNative final void setFocusedActivityLocked(ActivityRecord r) { if (mFocusedActivity != r) { mFocusedActivity = r; - mWindowManager.setFocusedApp(r.appToken, true); + if (r != null) { + mWindowManager.setFocusedApp(r.appToken, true); + } } } @@ -7903,6 +7909,7 @@ public final class ActivityManagerService extends ActivityManagerNative boolean dumpAll = false; boolean dumpClient = false; + String dumpPackage = null; int opti = 0; while (opti < args.length) { @@ -7920,13 +7927,14 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println(" [-a] [-c] [-h] [cmd] ..."); pw.println(" cmd may be one of:"); pw.println(" a[ctivities]: activity stack state"); - pw.println(" b[roadcasts]: broadcast state"); - pw.println(" i[ntents]: pending intent state"); - pw.println(" p[rocesses]: process state"); + pw.println(" b[roadcasts] [PACKAGE_NAME]: broadcast state"); + pw.println(" i[ntents] [PACKAGE_NAME]: pending intent state"); + pw.println(" p[rocesses] [PACKAGE_NAME]: process state"); pw.println(" o[om]: out of memory management"); pw.println(" prov[iders] [COMP_SPEC ...]: content provider state"); pw.println(" s[ervices] [COMP_SPEC ...]: service state"); pw.println(" service [COMP_SPEC]: service client-side state"); + pw.println(" package [PACKAGE_NAME]: all state related to given package"); pw.println(" all: dump all activities"); pw.println(" top: dump the top activity"); pw.println(" cmd may also be a COMP_SPEC to dump activities."); @@ -7947,22 +7955,58 @@ public final class ActivityManagerService extends ActivityManagerNative opti++; if ("activities".equals(cmd) || "a".equals(cmd)) { synchronized (this) { - dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient); + dumpActivitiesLocked(fd, pw, args, opti, true, dumpClient, null); } return; } else if ("broadcasts".equals(cmd) || "b".equals(cmd)) { + String[] newArgs; + String name; + if (opti >= args.length) { + name = null; + newArgs = EMPTY_STRING_ARRAY; + } else { + name = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + } synchronized (this) { - dumpBroadcastsLocked(fd, pw, args, opti, true); + dumpBroadcastsLocked(fd, pw, args, opti, true, name); } return; } else if ("intents".equals(cmd) || "i".equals(cmd)) { + String[] newArgs; + String name; + if (opti >= args.length) { + name = null; + newArgs = EMPTY_STRING_ARRAY; + } else { + name = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + } synchronized (this) { - dumpPendingIntentsLocked(fd, pw, args, opti, true); + dumpPendingIntentsLocked(fd, pw, args, opti, true, name); } return; } else if ("processes".equals(cmd) || "p".equals(cmd)) { + String[] newArgs; + String name; + if (opti >= args.length) { + name = null; + newArgs = EMPTY_STRING_ARRAY; + } else { + name = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + } synchronized (this) { - dumpProcessesLocked(fd, pw, args, opti, true); + dumpProcessesLocked(fd, pw, args, opti, true, name); } return; } else if ("oom".equals(cmd) || "o".equals(cmd)) { @@ -7972,7 +8016,7 @@ public final class ActivityManagerService extends ActivityManagerNative return; } else if ("providers".equals(cmd) || "prov".equals(cmd)) { synchronized (this) { - dumpProvidersLocked(fd, pw, args, opti, true); + dumpProvidersLocked(fd, pw, args, opti, true, null); } return; } else if ("service".equals(cmd)) { @@ -7985,16 +8029,32 @@ public final class ActivityManagerService extends ActivityManagerNative name = args[opti]; opti++; newArgs = new String[args.length - opti]; - if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, args.length - opti); + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); } if (!dumpService(fd, pw, name, newArgs, 0, dumpAll)) { pw.println("No services match: " + name); pw.println("Use -h for help."); } return; + } else if ("package".equals(cmd)) { + String[] newArgs; + if (opti >= args.length) { + pw.println("package: no package name specified"); + pw.println("Use -h for help."); + return; + } else { + dumpPackage = args[opti]; + opti++; + newArgs = new String[args.length - opti]; + if (args.length > 2) System.arraycopy(args, opti, newArgs, 0, + args.length - opti); + args = newArgs; + opti = 0; + } } else if ("services".equals(cmd) || "s".equals(cmd)) { synchronized (this) { - dumpServicesLocked(fd, pw, args, opti, true, dumpClient); + dumpServicesLocked(fd, pw, args, opti, true, dumpClient, null); } return; } else { @@ -8010,76 +8070,78 @@ public final class ActivityManagerService extends ActivityManagerNative // No piece of data specified, dump everything. synchronized (this) { boolean needSep; - needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll); + needSep = dumpPendingIntentsLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll); + needSep = dumpBroadcastsLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll); + needSep = dumpProvidersLocked(fd, pw, args, opti, dumpAll, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient); + needSep = dumpServicesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient); + needSep = dumpActivitiesLocked(fd, pw, args, opti, dumpAll, dumpClient, dumpPackage); if (needSep) { pw.println(" "); } if (dumpAll) { pw.println("-------------------------------------------------------------------------------"); } - dumpProcessesLocked(fd, pw, args, opti, dumpAll); + dumpProcessesLocked(fd, pw, args, opti, dumpAll, dumpPackage); } } boolean dumpActivitiesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, boolean dumpClient) { + int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { pw.println("ACTIVITY MANAGER ACTIVITIES (dumpsys activity activities)"); pw.println(" Main stack:"); - dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient); + dumpHistoryList(fd, pw, mMainStack.mHistory, " ", "Hist", true, !dumpAll, dumpClient, + dumpPackage); pw.println(" "); pw.println(" Running activities (most recent first):"); - dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false); + dumpHistoryList(fd, pw, mMainStack.mLRUActivities, " ", "Run", false, !dumpAll, false, + dumpPackage); if (mMainStack.mWaitingVisibleActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting for another to become visible:"); dumpHistoryList(fd, pw, mMainStack.mWaitingVisibleActivities, " ", "Wait", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } if (mMainStack.mStoppingActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to stop:"); dumpHistoryList(fd, pw, mMainStack.mStoppingActivities, " ", "Stop", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } if (mMainStack.mGoingToSleepActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to sleep:"); dumpHistoryList(fd, pw, mMainStack.mGoingToSleepActivities, " ", "Sleep", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } if (mMainStack.mFinishingActivities.size() > 0) { pw.println(" "); pw.println(" Activities waiting to finish:"); dumpHistoryList(fd, pw, mMainStack.mFinishingActivities, " ", "Fin", false, - !dumpAll, false); + !dumpAll, false, dumpPackage); } pw.println(" "); @@ -8102,6 +8164,12 @@ public final class ActivityManagerService extends ActivityManagerNative final int N = mRecentTasks.size(); for (int i=0; i<N; i++) { TaskRecord tr = mRecentTasks.get(i); + if (dumpPackage != null) { + if (tr.realActivity == null || + !dumpPackage.equals(tr.realActivity)) { + continue; + } + } pw.print(" * Recent #"); pw.print(i); pw.print(": "); pw.println(tr); if (dumpAll) { @@ -8119,7 +8187,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpProcessesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; int numPers = 0; @@ -8129,11 +8197,14 @@ public final class ActivityManagerService extends ActivityManagerNative for (SparseArray<ProcessRecord> procs : mProcessNames.getMap().values()) { final int NA = procs.size(); for (int ia=0; ia<NA; ia++) { + ProcessRecord r = procs.valueAt(ia); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } if (!needSep) { pw.println(" All known processes:"); needSep = true; } - ProcessRecord r = procs.valueAt(ia); pw.print(r.persistent ? " *PERS*" : " *APP*"); pw.print(" UID "); pw.print(procs.keyAt(ia)); pw.print(" "); pw.println(r); @@ -8150,31 +8221,49 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Process LRU list (sorted by oom_adj):"); dumpProcessOomList(pw, this, mLruProcesses, " ", - "Proc", "PERS", false); + "Proc", "PERS", false, dumpPackage); needSep = true; } if (dumpAll) { synchronized (mPidsSelfLocked) { - if (mPidsSelfLocked.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" PID mappings:"); - for (int i=0; i<mPidsSelfLocked.size(); i++) { - pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); - pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); + boolean printed = false; + for (int i=0; i<mPidsSelfLocked.size(); i++) { + ProcessRecord r = mPidsSelfLocked.valueAt(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" PID mappings:"); + printed = true; + } + pw.print(" PID #"); pw.print(mPidsSelfLocked.keyAt(i)); + pw.print(": "); pw.println(mPidsSelfLocked.valueAt(i)); } } } if (mForegroundProcesses.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Foreground Processes:"); - for (int i=0; i<mForegroundProcesses.size(); i++) { - pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i)); - pw.print(": "); pw.println(mForegroundProcesses.valueAt(i)); + synchronized (mPidsSelfLocked) { + boolean printed = false; + for (int i=0; i<mForegroundProcesses.size(); i++) { + ProcessRecord r = mPidsSelfLocked.get( + mForegroundProcesses.valueAt(i).pid); + if (dumpPackage != null && (r == null + || !dumpPackage.equals(r.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Foreground Processes:"); + printed = true; + } + pw.print(" PID #"); pw.print(mForegroundProcesses.keyAt(i)); + pw.print(": "); pw.println(mForegroundProcesses.valueAt(i)); + } } } @@ -8183,7 +8272,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Persisent processes that are starting:"); dumpProcessList(pw, this, mPersistentStartingProcesses, " ", - "Starting Norm", "Restarting PERS"); + "Starting Norm", "Restarting PERS", dumpPackage); } if (mRemovedProcesses.size() > 0) { @@ -8191,7 +8280,7 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are being removed:"); dumpProcessList(pw, this, mRemovedProcesses, " ", - "Removed Norm", "Removed PERS"); + "Removed Norm", "Removed PERS", dumpPackage); } if (mProcessesOnHold.size() > 0) { @@ -8199,23 +8288,34 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Processes that are on old until the system is ready:"); dumpProcessList(pw, this, mProcessesOnHold, " ", - "OnHold Norm", "OnHold PERS"); + "OnHold Norm", "OnHold PERS", dumpPackage); } - needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll); + needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, dumpPackage); if (mProcessCrashTimes.getMap().size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Time since processes crashed:"); + boolean printed = false; long now = SystemClock.uptimeMillis(); for (Map.Entry<String, SparseArray<Long>> procs : mProcessCrashTimes.getMap().entrySet()) { + String pname = procs.getKey(); SparseArray<Long> uids = procs.getValue(); final int N = uids.size(); for (int i=0; i<N; i++) { - pw.print(" Process "); pw.print(procs.getKey()); - pw.print(" uid "); pw.print(uids.keyAt(i)); + int puid = uids.keyAt(i); + ProcessRecord r = mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !dumpPackage.equals(r.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Time since processes crashed:"); + printed = true; + } + pw.print(" Process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); pw.print(": last crashed "); pw.print((now-uids.valueAt(i))); pw.println(" ms ago"); @@ -8224,16 +8324,26 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mBadProcesses.getMap().size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Bad processes:"); + boolean printed = false; for (Map.Entry<String, SparseArray<Long>> procs : mBadProcesses.getMap().entrySet()) { + String pname = procs.getKey(); SparseArray<Long> uids = procs.getValue(); final int N = uids.size(); for (int i=0; i<N; i++) { - pw.print(" Bad process "); pw.print(procs.getKey()); - pw.print(" uid "); pw.print(uids.keyAt(i)); + int puid = uids.keyAt(i); + ProcessRecord r = mProcessNames.get(pname, puid); + if (dumpPackage != null && (r == null + || !dumpPackage.equals(r.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Bad processes:"); + } + pw.print(" Bad process "); pw.print(pname); + pw.print(" uid "); pw.print(puid); pw.print(": crashed at time "); pw.println(uids.valueAt(i)); } @@ -8250,11 +8360,18 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpAll) { pw.println(" mConfigWillChange: " + mMainStack.mConfigWillChange); if (mCompatModePackages.getPackages().size() > 0) { - pw.println(" mScreenCompatPackages:"); + boolean printed = false; for (Map.Entry<String, Integer> entry : mCompatModePackages.getPackages().entrySet()) { String pkg = entry.getKey(); int mode = entry.getValue(); + if (dumpPackage != null && !dumpPackage.equals(pkg)) { + continue; + } + if (!printed) { + pw.println(" mScreenCompatPackages:"); + printed = true; + } pw.print(" "); pw.print(pkg); pw.print(": "); pw.print(mode); pw.println(); } @@ -8303,14 +8420,21 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpProcessesToGc(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean needSep, boolean dumpAll) { + int opti, boolean needSep, boolean dumpAll, String dumpPackage) { if (mProcessesToGc.size() > 0) { - if (needSep) pw.println(" "); - needSep = true; - pw.println(" Processes that are waiting to GC:"); + boolean printed = false; long now = SystemClock.uptimeMillis(); for (int i=0; i<mProcessesToGc.size(); i++) { ProcessRecord proc = mProcessesToGc.get(i); + if (dumpPackage != null && !dumpPackage.equals(proc.info.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Processes that are waiting to GC:"); + printed = true; + } pw.print(" Process "); pw.println(proc); pw.print(" lowMem="); pw.print(proc.reportLowMemory); pw.print(", last gced="); @@ -8350,11 +8474,11 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; pw.println(" Process OOM control:"); dumpProcessOomList(pw, this, mLruProcesses, " ", - "Proc", "PERS", true); + "Proc", "PERS", true, null); needSep = true; } - needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll); + needSep = dumpProcessesToGc(fd, pw, args, opti, needSep, dumpAll, null); pw.println(); pw.println(" mHomeProcess: " + mHomeProcess); @@ -8654,64 +8778,103 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpBroadcastsLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; pw.println("ACTIVITY MANAGER BROADCAST STATE (dumpsys activity broadcasts)"); if (dumpAll) { if (mRegisteredReceivers.size() > 0) { - pw.println(" Registered Receivers:"); + boolean printed = false; Iterator it = mRegisteredReceivers.values().iterator(); while (it.hasNext()) { ReceiverList r = (ReceiverList)it.next(); + if (dumpPackage != null && (r.app == null || + !dumpPackage.equals(r.app.info.packageName))) { + continue; + } + if (!printed) { + pw.println(" Registered Receivers:"); + needSep = true; + printed = true; + } pw.print(" * "); pw.println(r); r.dump(pw, " "); } } - - pw.println(); - pw.println(" Receiver Resolver Table:"); - mReceiverResolver.dump(pw, null, " ", null, false); - needSep = true; + + if (mReceiverResolver.dump(pw, needSep ? + "\n Receiver Resolver Table:" : " Receiver Resolver Table:", + " ", dumpPackage, false)) { + needSep = true; + } } if (mParallelBroadcasts.size() > 0 || mOrderedBroadcasts.size() > 0 || mPendingBroadcast != null) { - if (mParallelBroadcasts.size() > 0) { - pw.println(); - pw.println(" Active broadcasts:"); - } + boolean printed = false; for (int i=mParallelBroadcasts.size()-1; i>=0; i--) { + BroadcastRecord br = mParallelBroadcasts.get(i); + if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Active broadcasts:"); + } pw.println(" Broadcast #" + i + ":"); - mParallelBroadcasts.get(i).dump(pw, " "); - } - if (mOrderedBroadcasts.size() > 0) { - pw.println(); - pw.println(" Active ordered broadcasts:"); + br.dump(pw, " "); } + printed = false; for (int i=mOrderedBroadcasts.size()-1; i>=0; i--) { - pw.println(" Serialized Broadcast #" + i + ":"); + BroadcastRecord br = mOrderedBroadcasts.get(i); + if (dumpPackage != null && !dumpPackage.equals(br.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Active ordered broadcasts:"); + } + pw.println(" Ordered Broadcast #" + i + ":"); mOrderedBroadcasts.get(i).dump(pw, " "); } - pw.println(); - pw.println(" Pending broadcast:"); - if (mPendingBroadcast != null) { - mPendingBroadcast.dump(pw, " "); - } else { - pw.println(" (null)"); + if (dumpPackage == null || (mPendingBroadcast != null + && dumpPackage.equals(mPendingBroadcast.callerPackage))) { + if (needSep) { + pw.println(); + } + pw.println(" Pending broadcast:"); + if (mPendingBroadcast != null) { + mPendingBroadcast.dump(pw, " "); + } else { + pw.println(" (null)"); + } + needSep = true; } - needSep = true; } - if (needSep) { - pw.println(); - } - pw.println(" Historical broadcasts:"); + boolean printed = false; for (int i=0; i<MAX_BROADCAST_HISTORY; i++) { BroadcastRecord r = mBroadcastHistory[i]; if (r == null) { break; } + if (dumpPackage != null && !dumpPackage.equals(r.callerPackage)) { + continue; + } + if (!printed) { + if (needSep) { + pw.println(); + } + needSep = true; + pw.println(" Historical broadcasts:"); + printed = true; + } if (dumpAll) { pw.print(" Historical Broadcast #"); pw.print(i); pw.println(":"); r.dump(pw, " "); @@ -8725,8 +8888,11 @@ public final class ActivityManagerService extends ActivityManagerNative } needSep = true; - if (mStickyBroadcasts != null) { - pw.println(); + if (mStickyBroadcasts != null && dumpPackage == null) { + if (needSep) { + pw.println(); + } + needSep = true; pw.println(" Sticky broadcasts:"); StringBuilder sb = new StringBuilder(128); for (Map.Entry<String, ArrayList<Intent>> ent @@ -8766,7 +8932,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpServicesLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll, boolean dumpClient) { + int opti, boolean dumpAll, boolean dumpClient, String dumpPackage) { boolean needSep = false; ItemMatcher matcher = new ItemMatcher(); @@ -8774,7 +8940,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("ACTIVITY MANAGER SERVICES (dumpsys activity services)"); if (mServices.size() > 0) { - pw.println(" Active services:"); + boolean printed = false; long nowReal = SystemClock.elapsedRealtime(); Iterator<ServiceRecord> it = mServices.values().iterator(); needSep = false; @@ -8783,6 +8949,13 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + pw.println(" Active services:"); + printed = true; + } if (needSep) { pw.println(); } @@ -8834,17 +9007,25 @@ public final class ActivityManagerService extends ActivityManagerNative needSep = true; } } - needSep = true; + needSep = printed; } if (mPendingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Pending services:"); + boolean printed = false; for (int i=0; i<mPendingServices.size(); i++) { ServiceRecord r = mPendingServices.get(i); if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Pending services:"); + printed = true; + } pw.print(" * Pending "); pw.println(r); r.dump(pw, " "); } @@ -8852,13 +9033,21 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mRestartingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Restarting services:"); + boolean printed = false; for (int i=0; i<mRestartingServices.size(); i++) { ServiceRecord r = mRestartingServices.get(i); if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Restarting services:"); + printed = true; + } pw.print(" * Restarting "); pw.println(r); r.dump(pw, " "); } @@ -8866,13 +9055,21 @@ public final class ActivityManagerService extends ActivityManagerNative } if (mStoppingServices.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Stopping services:"); + boolean printed = false; for (int i=0; i<mStoppingServices.size(); i++) { ServiceRecord r = mStoppingServices.get(i); if (!matcher.match(r, r.name)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(r.appInfo.packageName)) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Stopping services:"); + printed = true; + } pw.print(" * Stopping "); pw.println(r); r.dump(pw, " "); } @@ -8881,8 +9078,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (dumpAll) { if (mServiceConnections.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Connection bindings to services:"); + boolean printed = false; Iterator<ArrayList<ConnectionRecord>> it = mServiceConnections.values().iterator(); while (it.hasNext()) { @@ -8892,6 +9088,16 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(cr.binding.service, cr.binding.service.name)) { continue; } + if (dumpPackage != null && (cr.binding.client == null + || !dumpPackage.equals(cr.binding.client.info.packageName))) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Connection bindings to services:"); + printed = true; + } pw.print(" * "); pw.println(cr); cr.dump(pw, " "); } @@ -8904,7 +9110,7 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpProvidersLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; ItemMatcher matcher = new ItemMatcher(); @@ -8912,8 +9118,7 @@ public final class ActivityManagerService extends ActivityManagerNative pw.println("ACTIVITY MANAGER CONTENT PROVIDERS (dumpsys activity providers)"); if (mProvidersByClass.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Published content providers (by class):"); + boolean printed = false; Iterator<Map.Entry<ComponentName, ContentProviderRecord>> it = mProvidersByClass.entrySet().iterator(); while (it.hasNext()) { @@ -8928,6 +9133,15 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(r, comp)) { continue; } + if (dumpPackage != null && !dumpPackage.equals(comp.getPackageName())) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Published content providers (by class):"); + printed = true; + } pw.print(" * "); pw.print(cls); pw.print(" ("); pw.print(comp.flattenToShortString()); pw.println(")"); if (dumpAll) { @@ -8946,13 +9160,11 @@ public final class ActivityManagerService extends ActivityManagerNative } } } - needSep = true; } if (dumpAll) { if (mProvidersByName.size() > 0) { - pw.println(" "); - pw.println(" Authority to provider mappings:"); + boolean printed = false; Iterator<Map.Entry<String, ContentProviderRecord>> it = mProvidersByName.entrySet().iterator(); while (it.hasNext()) { @@ -8961,25 +9173,42 @@ public final class ActivityManagerService extends ActivityManagerNative if (!matcher.match(r, r.name)) { continue; } - pw.print(" "); pw.print(e.getKey()); pw.print(": "); - pw.println(r); + if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Authority to provider mappings:"); + printed = true; + } + pw.print(" "); pw.print(e.getKey()); pw.println(":"); + pw.print(" "); pw.println(r); } - needSep = true; } } if (mLaunchingProviders.size() > 0) { - if (needSep) pw.println(" "); - pw.println(" Launching content providers:"); + boolean printed = false; for (int i=mLaunchingProviders.size()-1; i>=0; i--) { + ContentProviderRecord r = mLaunchingProviders.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.name.getPackageName())) { + continue; + } + if (!printed) { + if (needSep) pw.println(" "); + needSep = true; + pw.println(" Launching content providers:"); + printed = true; + } pw.print(" Launching #"); pw.print(i); pw.print(": "); - pw.println(mLaunchingProviders.get(i)); + pw.println(r); } - needSep = true; } if (mGrantedUriPermissions.size() > 0) { - pw.println(); + if (needSep) pw.println(); + needSep = true; pw.println("Granted Uri Permissions:"); for (int i=0; i<mGrantedUriPermissions.size(); i++) { int uid = mGrantedUriPermissions.keyAt(i); @@ -9001,16 +9230,24 @@ public final class ActivityManagerService extends ActivityManagerNative } boolean dumpPendingIntentsLocked(FileDescriptor fd, PrintWriter pw, String[] args, - int opti, boolean dumpAll) { + int opti, boolean dumpAll, String dumpPackage) { boolean needSep = false; - if (this.mIntentSenderRecords.size() > 0) { - pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); + if (mIntentSenderRecords.size() > 0) { + boolean printed = false; Iterator<WeakReference<PendingIntentRecord>> it = mIntentSenderRecords.values().iterator(); while (it.hasNext()) { WeakReference<PendingIntentRecord> ref = it.next(); PendingIntentRecord rec = ref != null ? ref.get(): null; + if (dumpPackage != null && (rec == null + || !dumpPackage.equals(rec.key.packageName))) { + continue; + } + if (!printed) { + pw.println("ACTIVITY MANAGER PENDING INTENTS (dumpsys activity intents)"); + printed = true; + } needSep = true; if (rec != null) { pw.print(" * "); pw.println(rec); @@ -9027,13 +9264,17 @@ public final class ActivityManagerService extends ActivityManagerNative } private static final void dumpHistoryList(FileDescriptor fd, PrintWriter pw, List list, - String prefix, String label, boolean complete, boolean brief, boolean client) { + String prefix, String label, boolean complete, boolean brief, boolean client, + String dumpPackage) { TaskRecord lastTask = null; boolean needNL = false; final String innerPrefix = prefix + " "; final String[] args = new String[0]; for (int i=list.size()-1; i>=0; i--) { final ActivityRecord r = (ActivityRecord)list.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.packageName)) { + continue; + } final boolean full = !brief && (complete || !r.isInHistory()); if (needNL) { pw.println(" "); @@ -9101,11 +9342,15 @@ public final class ActivityManagerService extends ActivityManagerNative private static final int dumpProcessList(PrintWriter pw, ActivityManagerService service, List list, - String prefix, String normalLabel, String persistentLabel) { + String prefix, String normalLabel, String persistentLabel, + String dumpPackage) { int numPers = 0; final int N = list.size()-1; for (int i=N; i>=0; i--) { ProcessRecord r = (ProcessRecord)list.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } pw.println(String.format("%s%s #%2d: %s", prefix, (r.persistent ? persistentLabel : normalLabel), i, r.toString())); @@ -9116,17 +9361,25 @@ public final class ActivityManagerService extends ActivityManagerNative return numPers; } - private static final void dumpProcessOomList(PrintWriter pw, + private static final boolean dumpProcessOomList(PrintWriter pw, ActivityManagerService service, List<ProcessRecord> origList, String prefix, String normalLabel, String persistentLabel, - boolean inclDetails) { + boolean inclDetails, String dumpPackage) { ArrayList<Pair<ProcessRecord, Integer>> list = new ArrayList<Pair<ProcessRecord, Integer>>(origList.size()); for (int i=0; i<origList.size(); i++) { + ProcessRecord r = origList.get(i); + if (dumpPackage != null && !dumpPackage.equals(r.info.packageName)) { + continue; + } list.add(new Pair<ProcessRecord, Integer>(origList.get(i), i)); } + if (list.size() <= 0) { + return false; + } + Comparator<Pair<ProcessRecord, Integer>> comparator = new Comparator<Pair<ProcessRecord, Integer>>() { @Override @@ -9149,8 +9402,7 @@ public final class ActivityManagerService extends ActivityManagerNative final long curUptime = SystemClock.uptimeMillis(); final long uptimeSince = curUptime - service.mLastPowerCheckUptime; - final int N = list.size()-1; - for (int i=N; i>=0; i--) { + for (int i=list.size()-1; i>=0; i--) { ProcessRecord r = list.get(i).first; String oomAdj; if (r.setAdj >= ProcessList.HIDDEN_APP_MIN_ADJ) { @@ -9202,8 +9454,8 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(String.format("%s%s #%2d: adj=%s/%s%s trm=%2d %s (%s)", prefix, (r.persistent ? persistentLabel : normalLabel), - N-list.get(i).second, oomAdj, schedGroup, foreground, r.trimMemoryLevel, - r.toShortString(), r.adjType)); + (origList.size()-1)-list.get(i).second, oomAdj, schedGroup, + foreground, r.trimMemoryLevel, r.toShortString(), r.adjType)); if (r.adjSource != null || r.adjTarget != null) { pw.print(prefix); pw.print(" "); @@ -9275,6 +9527,7 @@ public final class ActivityManagerService extends ActivityManagerNative } } } + return true; } ArrayList<ProcessRecord> collectProcesses(PrintWriter pw, int start, String[] args) { @@ -9346,16 +9599,20 @@ public final class ActivityManagerService extends ActivityManagerNative final static class MemItem { final String label; + final String shortLabel; final long pss; + final int id; ArrayList<MemItem> subitems; - public MemItem(String _label, long _pss) { + public MemItem(String _label, String _shortLabel, long _pss, int _id) { label = _label; + shortLabel = _shortLabel; pss = _pss; + id = _id; } } - final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items, + static final void dumpMemItems(PrintWriter pw, String prefix, ArrayList<MemItem> items, boolean sort) { if (sort) { Collections.sort(items, new Comparator<MemItem>() { @@ -9373,16 +9630,56 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<items.size(); i++) { MemItem mi = items.get(i); - pw.print(prefix); pw.printf("%7d Kb: ", mi.pss); pw.println(mi.label); + pw.print(prefix); pw.printf("%7d kB: ", mi.pss); pw.println(mi.label); if (mi.subitems != null) { dumpMemItems(pw, prefix + " ", mi.subitems, true); } } } + // These are in KB. + static final long[] DUMP_MEM_BUCKETS = new long[] { + 5*1024, 7*1024, 10*1024, 15*1024, 20*1024, 30*1024, 40*1024, 80*1024, + 120*1024, 160*1024, 200*1024, + 250*1024, 300*1024, 350*1024, 400*1024, 500*1024, 600*1024, 800*1024, + 1*1024*1024, 2*1024*1024, 5*1024*1024, 10*1024*1024, 20*1024*1024 + }; + + static final void appendMemBucket(StringBuilder out, long memKB, String label) { + int start = label.lastIndexOf('.'); + if (start >= 0) start++; + else start = 0; + int end = label.length(); + for (int i=0; i<DUMP_MEM_BUCKETS.length; i++) { + if (DUMP_MEM_BUCKETS[i] >= memKB) { + long bucket = DUMP_MEM_BUCKETS[i]/1024; + out.append(bucket); + out.append("MB "); + out.append(label, start, end); + return; + } + } + out.append(memKB/1024); + out.append("MB "); + out.append(label, start, end); + } + + static final int[] DUMP_MEM_OOM_ADJ = new int[] { + ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ, + ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, + ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, + ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ + }; + static final String[] DUMP_MEM_OOM_LABEL = new String[] { + "System", "Persistent", "Foreground", + "Visible", "Perceptible", "Heavy Weight", + "Backup", "A Services", "Home", "Previous", + "B Services", "Background" + }; + final void dumpApplicationMemoryUsage(FileDescriptor fd, PrintWriter pw, String prefix, String[] args, boolean brief, - PrintWriter categoryPw) { + PrintWriter categoryPw, StringBuilder outTag) { boolean dumpAll = false; boolean oomOnly = false; @@ -9438,20 +9735,9 @@ public final class ActivityManagerService extends ActivityManagerNative long nativePss=0, dalvikPss=0, otherPss=0; long[] miscPss = new long[Debug.MemoryInfo.NUM_OTHER_STATS]; - final int[] oomAdj = new int[] { - ProcessList.SYSTEM_ADJ, ProcessList.PERSISTENT_PROC_ADJ, ProcessList.FOREGROUND_APP_ADJ, - ProcessList.VISIBLE_APP_ADJ, ProcessList.PERCEPTIBLE_APP_ADJ, ProcessList.HEAVY_WEIGHT_APP_ADJ, - ProcessList.BACKUP_APP_ADJ, ProcessList.SERVICE_ADJ, ProcessList.HOME_APP_ADJ, - ProcessList.PREVIOUS_APP_ADJ, ProcessList.SERVICE_B_ADJ, ProcessList.HIDDEN_APP_MAX_ADJ - }; - final String[] oomLabel = new String[] { - "System", "Persistent", "Foreground", - "Visible", "Perceptible", "Heavy Weight", - "Backup", "A Services", "Home", "Previous", - "B Services", "Background" - }; - long oomPss[] = new long[oomLabel.length]; - ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[])new ArrayList[oomLabel.length]; + long oomPss[] = new long[DUMP_MEM_OOM_LABEL.length]; + ArrayList<MemItem>[] oomProcs = (ArrayList<MemItem>[]) + new ArrayList[DUMP_MEM_OOM_LABEL.length]; long totalPss = 0; @@ -9481,7 +9767,7 @@ public final class ActivityManagerService extends ActivityManagerNative long myTotalPss = mi.getTotalPss(); totalPss += myTotalPss; MemItem pssItem = new MemItem(r.processName + " (pid " + r.pid + ")", - myTotalPss); + r.processName, myTotalPss, 0); procMems.add(pssItem); nativePss += mi.nativePss; @@ -9494,7 +9780,8 @@ public final class ActivityManagerService extends ActivityManagerNative } for (int oomIndex=0; oomIndex<oomPss.length; oomIndex++) { - if (r.setAdj <= oomAdj[oomIndex] || oomIndex == (oomPss.length-1)) { + if (r.setAdj <= DUMP_MEM_OOM_ADJ[oomIndex] + || oomIndex == (oomPss.length-1)) { oomPss[oomIndex] += myTotalPss; if (oomProcs[oomIndex] == null) { oomProcs[oomIndex] = new ArrayList<MemItem>(); @@ -9510,22 +9797,47 @@ public final class ActivityManagerService extends ActivityManagerNative if (!isCheckinRequest && procs.size() > 1) { ArrayList<MemItem> catMems = new ArrayList<MemItem>(); - catMems.add(new MemItem("Native", nativePss)); - catMems.add(new MemItem("Dalvik", dalvikPss)); - catMems.add(new MemItem("Unknown", otherPss)); + catMems.add(new MemItem("Native", "Native", nativePss, -1)); + catMems.add(new MemItem("Dalvik", "Dalvik", dalvikPss, -2)); + catMems.add(new MemItem("Unknown", "Unknown", otherPss, -3)); for (int j=0; j<Debug.MemoryInfo.NUM_OTHER_STATS; j++) { - catMems.add(new MemItem(Debug.MemoryInfo.getOtherLabel(j), miscPss[j])); + String label = Debug.MemoryInfo.getOtherLabel(j); + catMems.add(new MemItem(label, label, miscPss[j], j)); } ArrayList<MemItem> oomMems = new ArrayList<MemItem>(); for (int j=0; j<oomPss.length; j++) { if (oomPss[j] != 0) { - MemItem item = new MemItem(oomLabel[j], oomPss[j]); + String label = DUMP_MEM_OOM_LABEL[j]; + MemItem item = new MemItem(label, label, oomPss[j], + DUMP_MEM_OOM_ADJ[j]); item.subitems = oomProcs[j]; oomMems.add(item); } } + if (outTag != null) { + appendMemBucket(outTag, totalPss, "total"); + for (int i=0; i<oomMems.size(); i++) { + MemItem miCat = oomMems.get(i); + if (miCat.subitems == null || miCat.subitems.size() < 1) { + continue; + } + if (miCat.id < ProcessList.SERVICE_ADJ + || miCat.id == ProcessList.HOME_APP_ADJ + || miCat.id == ProcessList.PREVIOUS_APP_ADJ) { + outTag.append(" / "); + for (int j=0; j<miCat.subitems.size(); j++) { + MemItem mi = miCat.subitems.get(j); + if (j > 0) { + outTag.append(" "); + } + appendMemBucket(outTag, mi.pss, mi.shortLabel); + } + } + } + } + if (!brief && !oomOnly) { pw.println(); pw.println("Total PSS by process:"); @@ -9541,7 +9853,7 @@ public final class ActivityManagerService extends ActivityManagerNative dumpMemItems(out, " ", catMems, true); } pw.println(); - pw.print("Total PSS: "); pw.print(totalPss); pw.println(" Kb"); + pw.print("Total PSS: "); pw.print(totalPss); pw.println(" kB"); } } @@ -13991,7 +14303,6 @@ public final class ActivityManagerService extends ActivityManagerNative // application processes based on their current state. int i = mLruProcesses.size(); int curHiddenAdj = ProcessList.HIDDEN_APP_MIN_ADJ; - int numBg = 0; while (i > 0) { i--; ProcessRecord app = mLruProcesses.get(i); @@ -14015,12 +14326,7 @@ public final class ActivityManagerService extends ActivityManagerNative app.processName, app.setAdj, "too many background"); app.killedBackground = true; Process.killProcessQuiet(app.pid); - } else { - numBg++; } - } else if (app.curAdj >= ProcessList.HOME_APP_ADJ - && app.curAdj != ProcessList.SERVICE_B_ADJ) { - numBg++; } } } @@ -14035,12 +14341,18 @@ public final class ActivityManagerService extends ActivityManagerNative // memory they want. if (numHidden <= (ProcessList.MAX_HIDDEN_APPS/2)) { final int N = mLruProcesses.size(); - factor = numBg/3; + factor = numHidden/3; + int minFactor = 2; + if (mHomeProcess != null) minFactor++; + if (mPreviousProcess != null) minFactor++; + if (factor < minFactor) factor = minFactor; step = 0; int curLevel = ComponentCallbacks2.TRIM_MEMORY_COMPLETE; for (i=0; i<N; i++) { ProcessRecord app = mLruProcesses.get(i); - if (app.curAdj >= ProcessList.HIDDEN_APP_MIN_ADJ && !app.killedBackground) { + if (app.curAdj >= ProcessList.HOME_APP_ADJ + && app.curAdj != ProcessList.SERVICE_B_ADJ + && !app.killedBackground) { if (app.trimMemoryLevel < curLevel && app.thread != null) { try { app.thread.scheduleTrimMemory(curLevel); diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java index 6f6266d8100c..38355537e1c6 100644 --- a/services/java/com/android/server/am/ContentProviderRecord.java +++ b/services/java/com/android/server/am/ContentProviderRecord.java @@ -73,15 +73,14 @@ class ContentProviderRecord extends ContentProviderHolder { pw.print("multiprocess="); pw.print(info.multiprocess); pw.print(" initOrder="); pw.println(info.initOrder); } + if (externals != 0) { + pw.print(prefix); pw.print("externals="); pw.println(externals); + } if (clients.size() > 0) { pw.print(prefix); pw.println("Clients:"); for (ProcessRecord cproc : clients) { - pw.print(prefix); pw.println(" - "); pw.println(cproc); + pw.print(prefix); pw.print(" - "); pw.println(cproc.toShortString()); } - pw.print(prefix); pw.print("clients="); pw.println(clients); - } - if (externals != 0) { - pw.print(prefix); pw.print("externals="); pw.println(externals); } } diff --git a/services/java/com/android/server/net/NetworkPolicyManagerService.java b/services/java/com/android/server/net/NetworkPolicyManagerService.java index 2a1b1db0bf77..8c0f1e01f843 100644 --- a/services/java/com/android/server/net/NetworkPolicyManagerService.java +++ b/services/java/com/android/server/net/NetworkPolicyManagerService.java @@ -393,6 +393,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // on background handler thread, and verified // READ_NETWORK_USAGE_HISTORY permission above. + maybeRefreshTrustedTime(); synchronized (mRulesLock) { updateNetworkEnabledLocked(); updateNotificationsLocked(); @@ -445,7 +446,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // cycle boundary to recompute notifications. // examine stats for each active policy - final long currentTime = currentTimeMillis(true); + final long currentTime = currentTimeMillis(); for (NetworkPolicy policy : mNetworkPolicy.values()) { // ignore policies that aren't relevant to user if (!isTemplateRelevant(policy.template)) continue; @@ -683,6 +684,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void onReceive(Context context, Intent intent) { // on background handler thread, and verified CONNECTIVITY_INTERNAL // permission above. + + maybeRefreshTrustedTime(); synchronized (mRulesLock) { ensureActiveMobilePolicyLocked(); updateNetworkEnabledLocked(); @@ -702,7 +705,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // TODO: reset any policy-disabled networks when any policy is removed // completely, which is currently rare case. - final long currentTime = currentTimeMillis(true); + final long currentTime = currentTimeMillis(); for (NetworkPolicy policy : mNetworkPolicy.values()) { // shortcut when policy has no limit if (policy.limitBytes == LIMIT_DISABLED) { @@ -802,7 +805,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { // apply each policy that we found ifaces for; compute remaining data // based on current cycle and historical stats, and push to kernel. - final long currentTime = currentTimeMillis(true); + final long currentTime = currentTimeMillis(); for (NetworkPolicy policy : mNetworkRules.keySet()) { final String[] ifaces = mNetworkRules.get(policy); @@ -1092,6 +1095,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void setNetworkPolicies(NetworkPolicy[] policies) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + maybeRefreshTrustedTime(); synchronized (mRulesLock) { mNetworkPolicy.clear(); for (NetworkPolicy policy : policies) { @@ -1119,7 +1123,8 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void snoozePolicy(NetworkTemplate template) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); - final long currentTime = currentTimeMillis(true); + maybeRefreshTrustedTime(); + final long currentTime = currentTimeMillis(); synchronized (mRulesLock) { // find and snooze local policy that matches final NetworkPolicy policy = mNetworkPolicy.get(template); @@ -1140,6 +1145,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { public void setRestrictBackground(boolean restrictBackground) { mContext.enforceCallingOrSelfPermission(MANAGE_NETWORK_POLICY, TAG); + maybeRefreshTrustedTime(); synchronized (mRulesLock) { mRestrictBackground = restrictBackground; updateRulesForRestrictBackgroundLocked(); @@ -1193,7 +1199,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { return null; } - final long currentTime = currentTimeMillis(false); + final long currentTime = currentTimeMillis(); // find total bytes used under policy final long start = computeLastCycleBoundary(currentTime, policy); @@ -1472,6 +1478,7 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { case MSG_LIMIT_REACHED: { final String iface = (String) msg.obj; + maybeRefreshTrustedTime(); synchronized (mRulesLock) { if (mMeteredIfaces.contains(iface)) { try { @@ -1551,12 +1558,16 @@ public class NetworkPolicyManagerService extends INetworkPolicyManager.Stub { } } - private long currentTimeMillis(boolean allowRefresh) { - // try refreshing time source when stale - if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE && allowRefresh) { + /** + * Try refreshing {@link #mTime} when stale. + */ + private void maybeRefreshTrustedTime() { + if (mTime.getCacheAge() > TIME_CACHE_MAX_AGE) { mTime.forceRefresh(); } + } + private long currentTimeMillis() { return mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); } diff --git a/services/java/com/android/server/net/NetworkStatsService.java b/services/java/com/android/server/net/NetworkStatsService.java index 28cb983ad18f..871ed682e629 100644 --- a/services/java/com/android/server/net/NetworkStatsService.java +++ b/services/java/com/android/server/net/NetworkStatsService.java @@ -203,6 +203,8 @@ public class NetworkStatsService extends INetworkStatsService.Stub { /** Set of historical {@code xtables} stats for known UIDs. */ private HashMap<UidStatsKey, NetworkStatsHistory> mUidStats = Maps.newHashMap(); + /** Flag if {@link #mNetworkDevStats} have been loaded from disk. */ + private boolean mNetworkStatsLoaded = false; /** Flag if {@link #mUidStats} have been loaded from disk. */ private boolean mUidStatsLoaded = false; @@ -272,6 +274,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // until actually needed. readNetworkDevStatsLocked(); readNetworkXtStatsLocked(); + mNetworkStatsLoaded = true; } // bootstrap initial stats to prevent double-counting later @@ -322,14 +325,17 @@ public class NetworkStatsService extends INetworkStatsService.Stub { mTeleManager.listen(mPhoneListener, LISTEN_NONE); - writeNetworkDevStatsLocked(); - writeNetworkXtStatsLocked(); + if (mNetworkStatsLoaded) { + writeNetworkDevStatsLocked(); + writeNetworkXtStatsLocked(); + } if (mUidStatsLoaded) { writeUidStatsLocked(); } mNetworkDevStats.clear(); mNetworkXtStats.clear(); mUidStats.clear(); + mNetworkStatsLoaded = false; mUidStatsLoaded = false; } @@ -472,6 +478,18 @@ public class NetworkStatsService extends INetworkStatsService.Stub { } } + private long getHistoryStartLocked( + NetworkTemplate template, HashMap<NetworkIdentitySet, NetworkStatsHistory> source) { + long start = Long.MAX_VALUE; + for (NetworkIdentitySet ident : source.keySet()) { + if (templateMatches(template, ident)) { + final NetworkStatsHistory history = source.get(ident); + start = Math.min(start, history.getStart()); + } + } + return start; + } + @Override public NetworkStats getSummaryForAllUid( NetworkTemplate template, long start, long end, boolean includeTags) { @@ -771,6 +789,12 @@ public class NetworkStatsService extends INetworkStatsService.Stub { private void performPoll(int flags) { synchronized (mStatsLock) { mWakeLock.acquire(); + + // try refreshing time source when stale + if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) { + mTime.forceRefresh(); + } + try { performPollLocked(flags); } finally { @@ -791,11 +815,6 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final boolean persistUid = (flags & FLAG_PERSIST_UID) != 0; final boolean persistForce = (flags & FLAG_PERSIST_FORCE) != 0; - // try refreshing time source when stale - if (mTime.getCacheAge() > mSettings.getTimeCacheMaxAge()) { - mTime.forceRefresh(); - } - // TODO: consider marking "untrusted" times in historical stats final long currentTime = mTime.hasCache() ? mTime.currentTimeMillis() : System.currentTimeMillis(); @@ -981,6 +1000,7 @@ public class NetworkStatsService extends INetworkStatsService.Stub { final long start = end - largestBucketSize; final long trustedTime = mTime.hasCache() ? mTime.currentTimeMillis() : -1; + long devHistoryStart = Long.MAX_VALUE; NetworkTemplate template = null; NetworkStats.Entry devTotal = null; @@ -990,24 +1010,27 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // collect mobile sample template = buildTemplateMobileAll(getActiveSubscriberId(mContext)); devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal); + devHistoryStart = getHistoryStartLocked(template, mNetworkDevStats); xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal); uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal); + EventLogTags.writeNetstatsMobileSample( devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets, xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets, - trustedTime); + trustedTime, devHistoryStart); // collect wifi sample template = buildTemplateWifi(); devTotal = getSummaryForNetworkDev(template, start, end).getTotal(devTotal); + devHistoryStart = getHistoryStartLocked(template, mNetworkDevStats); xtTotal = getSummaryForNetworkXt(template, start, end).getTotal(xtTotal); uidTotal = getSummaryForAllUid(template, start, end, false).getTotal(uidTotal); EventLogTags.writeNetstatsWifiSample( devTotal.rxBytes, devTotal.rxPackets, devTotal.txBytes, devTotal.txPackets, xtTotal.rxBytes, xtTotal.rxPackets, xtTotal.txBytes, xtTotal.txPackets, uidTotal.rxBytes, uidTotal.rxPackets, uidTotal.txBytes, uidTotal.txPackets, - trustedTime); + trustedTime, devHistoryStart); } /** @@ -1243,11 +1266,28 @@ public class NetworkStatsService extends INetworkStatsService.Stub { // trim any history beyond max if (mTime.hasCache()) { - final long currentTime = Math.min( - System.currentTimeMillis(), mTime.currentTimeMillis()); + final long systemCurrentTime = System.currentTimeMillis(); + final long trustedCurrentTime = mTime.currentTimeMillis(); + + final long currentTime = Math.min(systemCurrentTime, trustedCurrentTime); final long maxHistory = mSettings.getNetworkMaxHistory(); + for (NetworkStatsHistory history : input.values()) { + final int beforeSize = history.size(); history.removeBucketsBefore(currentTime - maxHistory); + final int afterSize = history.size(); + + if (beforeSize > 24 && afterSize < beforeSize / 2) { + // yikes, dropping more than half of significant history + final StringBuilder builder = new StringBuilder(); + builder.append("yikes, dropping more than half of history").append('\n'); + builder.append("systemCurrentTime=").append(systemCurrentTime).append('\n'); + builder.append("trustedCurrentTime=").append(trustedCurrentTime).append('\n'); + builder.append("maxHistory=").append(maxHistory).append('\n'); + builder.append("beforeSize=").append(beforeSize).append('\n'); + builder.append("afterSize=").append(afterSize).append('\n'); + mDropBox.addText(TAG_NETSTATS_ERROR, builder.toString()); + } } } diff --git a/services/java/com/android/server/pm/PackageManagerService.java b/services/java/com/android/server/pm/PackageManagerService.java index 0e9f64c34480..700554157ea1 100644 --- a/services/java/com/android/server/pm/PackageManagerService.java +++ b/services/java/com/android/server/pm/PackageManagerService.java @@ -7947,7 +7947,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (dumpState.isDumping(DumpState.DUMP_PROVIDERS)) { boolean printedSomething = false; - for (PackageParser.Provider p : mProviders.values()) { + for (PackageParser.Provider p : mProvidersByComponent.values()) { if (packageName != null && !packageName.equals(p.info.packageName)) { continue; } @@ -7957,8 +7957,23 @@ public class PackageManagerService extends IPackageManager.Stub { pw.println("Registered ContentProviders:"); printedSomething = true; } - pw.print(" ["); pw.print(p.info.authority); pw.print("]: "); - pw.println(p.toString()); + pw.print(" "); pw.print(p.getComponentShortName()); pw.println(":"); + pw.print(" "); pw.println(p.toString()); + } + printedSomething = false; + for (Map.Entry<String, PackageParser.Provider> entry : mProviders.entrySet()) { + PackageParser.Provider p = entry.getValue(); + if (packageName != null && !packageName.equals(p.info.packageName)) { + continue; + } + if (!printedSomething) { + if (dumpState.onTitlePrinted()) + pw.println(" "); + pw.println("ContentProvider Authorities:"); + printedSomething = true; + } + pw.print(" ["); pw.print(entry.getKey()); pw.println("]:"); + pw.print(" "); pw.println(p.toString()); } } diff --git a/services/jni/com_android_server_InputManager.cpp b/services/jni/com_android_server_InputManager.cpp index 7e9fba891946..f25988339a4d 100644 --- a/services/jni/com_android_server_InputManager.cpp +++ b/services/jni/com_android_server_InputManager.cpp @@ -98,6 +98,7 @@ static struct { jfieldID mName; jfieldID mSources; jfieldID mKeyboardType; + jfieldID mKeyCharacterMapFile; } gInputDeviceClassInfo; static struct { @@ -1231,10 +1232,16 @@ static jobject android_server_InputManager_nativeGetInputDevice(JNIEnv* env, return NULL; } + jstring fileStr = env->NewStringUTF(deviceInfo.getKeyCharacterMapFile()); + if (!fileStr) { + return NULL; + } + env->SetIntField(deviceObj, gInputDeviceClassInfo.mId, deviceInfo.getId()); env->SetObjectField(deviceObj, gInputDeviceClassInfo.mName, deviceNameObj); env->SetIntField(deviceObj, gInputDeviceClassInfo.mSources, deviceInfo.getSources()); env->SetIntField(deviceObj, gInputDeviceClassInfo.mKeyboardType, deviceInfo.getKeyboardType()); + env->SetObjectField(deviceObj, gInputDeviceClassInfo.mKeyCharacterMapFile, fileStr); const Vector<InputDeviceInfo::MotionRange>& ranges = deviceInfo.getMotionRanges(); for (size_t i = 0; i < ranges.size(); i++) { @@ -1516,6 +1523,9 @@ int register_android_server_InputManager(JNIEnv* env) { GET_FIELD_ID(gInputDeviceClassInfo.mKeyboardType, gInputDeviceClassInfo.clazz, "mKeyboardType", "I"); + GET_FIELD_ID(gInputDeviceClassInfo.mKeyCharacterMapFile, gInputDeviceClassInfo.clazz, + "mKeyCharacterMapFile", "Ljava/lang/String;"); + // Configuration FIND_CLASS(clazz, "android/content/res/Configuration"); diff --git a/services/surfaceflinger/Android.mk b/services/surfaceflinger/Android.mk index 53502dbc4fbd..61a8358332eb 100644 --- a/services/surfaceflinger/Android.mk +++ b/services/surfaceflinger/Android.mk @@ -30,10 +30,6 @@ ifeq ($(TARGET_BOARD_PLATFORM), s5pc110) LOCAL_CFLAGS += -DHAS_CONTEXT_PRIORITY -DNEVER_DEFAULT_TO_ASYNC_MODE endif -ifneq (,$(findstring $(TARGET_DEVICE),tuna toro maguro)) - LOCAL_CFLAGS += -DREFRESH_RATE=59 -endif - LOCAL_SHARED_LIBRARIES := \ libcutils \ diff --git a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp index 329c052708ac..f94d32149b0f 100644 --- a/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp +++ b/services/surfaceflinger/DisplayHardware/DisplayHardware.cpp @@ -284,22 +284,6 @@ void DisplayHardware::init(uint32_t dpy) glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTextureSize); glGetIntegerv(GL_MAX_VIEWPORT_DIMS, mMaxViewportDims); - -#ifdef EGL_ANDROID_swap_rectangle - if (extensions.hasExtension("EGL_ANDROID_swap_rectangle")) { - if (eglSetSwapRectangleANDROID(display, surface, - 0, 0, mWidth, mHeight) == EGL_TRUE) { - // This could fail if this extension is not supported by this - // specific surface (of config) - mFlags |= SWAP_RECTANGLE; - } - } - // when we have the choice between PARTIAL_UPDATES and SWAP_RECTANGLE - // choose PARTIAL_UPDATES, which should be more efficient - if (mFlags & PARTIAL_UPDATES) - mFlags &= ~SWAP_RECTANGLE; -#endif - LOGI("EGL informations:"); LOGI("# of configs : %d", numConfigs); LOGI("vendor : %s", extensions.getEglVendor()); diff --git a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java index 636ba210191f..21a8ec2c0091 100644 --- a/services/tests/servicestests/src/com/android/server/EntropyServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/EntropyMixerTest.java @@ -23,9 +23,9 @@ import android.test.AndroidTestCase; import java.io.File; /** - * Tests for {@link com.android.server.EntropyService} + * Tests for {@link com.android.server.EntropyMixer} */ -public class EntropyServiceTest extends AndroidTestCase { +public class EntropyMixerTest extends AndroidTestCase { public void testInitialWrite() throws Exception { File dir = getContext().getDir("testInitialWrite", Context.MODE_PRIVATE); @@ -34,7 +34,7 @@ public class EntropyServiceTest extends AndroidTestCase { assertEquals(0, FileUtils.readTextFile(file, 0, null).length()); // The constructor has the side effect of writing to file - new EntropyService("/dev/null", file.getCanonicalPath()); + new EntropyMixer("/dev/null", file.getCanonicalPath()); assertTrue(FileUtils.readTextFile(file, 0, null).length() > 0); } diff --git a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java index 061837478a5d..7c61e9a0bc68 100644 --- a/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/NetworkPolicyManagerServiceTest.java @@ -693,12 +693,13 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectSetInterfaceQuota(TEST_IFACE, Long.MAX_VALUE); expectMeteredIfacesChanged(TEST_IFACE); - expectClearNotifications(); + future = expectClearNotifications(); tagFuture = expectEnqueueNotification(); replay(); mService.snoozePolicy(sTemplateWifi); assertNotificationType(TYPE_LIMIT_SNOOZED, tagFuture.get()); + future.get(); verifyAndReset(); } } @@ -734,9 +735,11 @@ public class NetworkPolicyManagerServiceTest extends AndroidTestCase { expectLastCall().atLeastOnce(); } - private void expectClearNotifications() throws Exception { + private Future<Void> expectClearNotifications() throws Exception { + final FutureAnswer future = new FutureAnswer(); mNotifManager.cancelNotificationWithTag(isA(String.class), isA(String.class), anyInt()); - expectLastCall().anyTimes(); + expectLastCall().andAnswer(future).anyTimes(); + return future; } private Future<String> expectEnqueueNotification() throws Exception { diff --git a/telephony/java/android/telephony/PhoneNumberUtils.java b/telephony/java/android/telephony/PhoneNumberUtils.java index f2ccb5bf016e..07afe3077ff2 100644 --- a/telephony/java/android/telephony/PhoneNumberUtils.java +++ b/telephony/java/android/telephony/PhoneNumberUtils.java @@ -1575,6 +1575,17 @@ public class PhoneNumberUtils // If the number passed in is null, just return false: if (number == null) return false; + // If the number passed in is a SIP address, return false, since the + // concept of "emergency numbers" is only meaningful for calls placed + // over the cell network. + // (Be sure to do this check *before* calling extractNetworkPortionAlt(), + // since the whole point of extractNetworkPortionAlt() is to filter out + // any non-dialable characters (which would turn 'abc911def@example.com' + // into '911', for example.)) + if (isUriNumber(number)) { + return false; + } + // Strip the separators from the number before comparing it // to the list. number = extractNetworkPortionAlt(number); @@ -2107,6 +2118,31 @@ public class PhoneNumberUtils } /** + * @return the "username" part of the specified SIP address, + * i.e. the part before the "@" character (or "%40"). + * + * @param number SIP address of the form "username@domainname" + * (or the URI-escaped equivalent "username%40domainname") + * @see isUriNumber + * + * @hide + */ + public static String getUsernameFromUriNumber(String number) { + // The delimiter between username and domain name can be + // either "@" or "%40" (the URI-escaped equivalent.) + int delimiterIndex = number.indexOf('@'); + if (delimiterIndex < 0) { + delimiterIndex = number.indexOf("%40"); + } + if (delimiterIndex < 0) { + Log.w(LOG_TAG, + "getUsernameFromUriNumber: no delimiter found in SIP addr '" + number + "'"); + delimiterIndex = number.length(); + } + return number.substring(0, delimiterIndex); + } + + /** * This function handles the plus code conversion within NANP CDMA network * If the number format is * 1)+1NANP,remove +, diff --git a/telephony/java/android/telephony/TelephonyManager.java b/telephony/java/android/telephony/TelephonyManager.java index 8ead45ebb953..db78e2e98dea 100755 --- a/telephony/java/android/telephony/TelephonyManager.java +++ b/telephony/java/android/telephony/TelephonyManager.java @@ -60,16 +60,16 @@ public class TelephonyManager { /** @hide */ public TelephonyManager(Context context) { - context = context.getApplicationContext(); if (sContext == null) { - sContext = context; + Context appContext = context.getApplicationContext(); + if (appContext != null) { + sContext = appContext; + } else { + sContext = context; + } sRegistry = ITelephonyRegistry.Stub.asInterface(ServiceManager.getService( "telephony.registry")); - } else if (sContext != context) { - Log.e(TAG, "Hidden constructor called more than once per process!"); - Log.e(TAG, "Original: " + sContext.getPackageName() + ", new: " + - context.getPackageName()); } } diff --git a/telephony/java/com/android/internal/telephony/CallerInfo.java b/telephony/java/com/android/internal/telephony/CallerInfo.java index 6324550cc1df..5d1f7588a584 100644 --- a/telephony/java/com/android/internal/telephony/CallerInfo.java +++ b/telephony/java/com/android/internal/telephony/CallerInfo.java @@ -288,7 +288,7 @@ public class CallerInfo { String number, CallerInfo previousResult) { if (!previousResult.contactExists && PhoneNumberUtils.isUriNumber(number)) { - String username = number.substring(0, number.indexOf('@')); + String username = PhoneNumberUtils.getUsernameFromUriNumber(number); if (PhoneNumberUtils.isGlobalPhoneNumber(username)) { previousResult = getCallerInfo(context, Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, diff --git a/telephony/java/com/android/internal/telephony/DataCallState.java b/telephony/java/com/android/internal/telephony/DataCallState.java index 6d8956f452b7..efbf608d9020 100644 --- a/telephony/java/com/android/internal/telephony/DataCallState.java +++ b/telephony/java/com/android/internal/telephony/DataCallState.java @@ -126,6 +126,8 @@ public class DataCallState { // set link addresses if (addresses != null && addresses.length > 0) { for (String addr : addresses) { + addr = addr.trim(); + if (addr.isEmpty()) continue; LinkAddress la; int addrPrefixLen; @@ -159,6 +161,8 @@ public class DataCallState { // set dns servers if (dnses != null && dnses.length > 0) { for (String addr : dnses) { + addr = addr.trim(); + if (addr.isEmpty()) continue; InetAddress ia; try { ia = NetworkUtils.numericToInetAddress(addr); @@ -174,6 +178,8 @@ public class DataCallState { dnsServers[0] = SystemProperties.get(propertyPrefix + "dns1"); dnsServers[1] = SystemProperties.get(propertyPrefix + "dns2"); for (String dnsAddr : dnsServers) { + dnsAddr = dnsAddr.trim(); + if (dnsAddr.isEmpty()) continue; InetAddress ia; try { ia = NetworkUtils.numericToInetAddress(dnsAddr); @@ -198,6 +204,8 @@ public class DataCallState { } } for (String addr : gateways) { + addr = addr.trim(); + if (addr.isEmpty()) continue; InetAddress ia; try { ia = NetworkUtils.numericToInetAddress(addr); diff --git a/telephony/java/com/android/internal/telephony/SmsHeader.java b/telephony/java/com/android/internal/telephony/SmsHeader.java index 9492e0e58634..c32388f85ee1 100644 --- a/telephony/java/com/android/internal/telephony/SmsHeader.java +++ b/telephony/java/com/android/internal/telephony/SmsHeader.java @@ -190,7 +190,9 @@ public class SmsHeader { public static byte[] toByteArray(SmsHeader smsHeader) { if ((smsHeader.portAddrs == null) && (smsHeader.concatRef == null) && - (smsHeader.miscEltList.size() == 0)) { + (smsHeader.miscEltList.isEmpty()) && + (smsHeader.languageShiftTable == 0) && + (smsHeader.languageTable == 0)) { return null; } diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java index 7cd01a167d78..de09dfb3565a 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java @@ -1848,8 +1848,14 @@ public final class GsmDataConnectionTracker extends DataConnectionTracker { DataConnection dc = apnContext.getDataConnection(); if (DBG) { - log(String.format("onDataSetupComplete: success apn=%s", - apnContext.getWaitingApns().get(0).apn)); + // TODO We may use apnContext.getApnSetting() directly + // instead of getWaitingApns().get(0) + String apnStr = "<unknown>"; + if (apnContext.getWaitingApns() != null + && !apnContext.getWaitingApns().isEmpty()){ + apnStr = apnContext.getWaitingApns().get(0).apn; + } + log("onDataSetupComplete: success apn=" + apnStr); } ApnSetting apn = apnContext.getApnSetting(); if (apn.proxy != null && apn.proxy.length() != 0) { diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java index d29e4884cc1d..8a75f51ff5ff 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -239,7 +239,11 @@ public final class GsmSMSDispatcher extends SMSDispatcher { byte[] data, PendingIntent sentIntent, PendingIntent deliveryIntent) { SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( scAddr, destAddr, destPort, data, (deliveryIntent != null)); - sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); + if (pdu != null) { + sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); + } else { + Log.e(TAG, "GsmSMSDispatcher.sendData(): getSubmitPdu() returned null"); + } } /** {@inheritDoc} */ @@ -248,7 +252,11 @@ public final class GsmSMSDispatcher extends SMSDispatcher { PendingIntent sentIntent, PendingIntent deliveryIntent) { SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu( scAddr, destAddr, text, (deliveryIntent != null)); - sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); + if (pdu != null) { + sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); + } else { + Log.e(TAG, "GsmSMSDispatcher.sendText(): getSubmitPdu() returned null"); + } } /** {@inheritDoc} */ @@ -266,7 +274,11 @@ public final class GsmSMSDispatcher extends SMSDispatcher { SmsMessage.SubmitPdu pdu = SmsMessage.getSubmitPdu(scAddress, destinationAddress, message, deliveryIntent != null, SmsHeader.toByteArray(smsHeader), encoding, smsHeader.languageTable, smsHeader.languageShiftTable); - sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); + if (pdu != null) { + sendRawPdu(pdu.encodedScAddress, pdu.encodedMessage, sentIntent, deliveryIntent); + } else { + Log.e(TAG, "GsmSMSDispatcher.sendNewSubmitPdu(): getSubmitPdu() returned null"); + } } /** {@inheritDoc} */ diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java index 677923f44756..da605841d4bb 100644 --- a/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java +++ b/telephony/java/com/android/internal/telephony/gsm/SmsMessage.java @@ -16,22 +16,22 @@ package com.android.internal.telephony.gsm; -import android.os.Parcel; import android.telephony.PhoneNumberUtils; import android.text.format.Time; import android.util.Log; -import com.android.internal.telephony.IccUtils; + import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import java.io.ByteArrayOutputStream; import java.io.UnsupportedEncodingException; +import static android.telephony.SmsMessage.ENCODING_16BIT; import static android.telephony.SmsMessage.ENCODING_7BIT; import static android.telephony.SmsMessage.ENCODING_8BIT; -import static android.telephony.SmsMessage.ENCODING_16BIT; import static android.telephony.SmsMessage.ENCODING_KSC5601; import static android.telephony.SmsMessage.ENCODING_UNKNOWN; import static android.telephony.SmsMessage.MAX_USER_DATA_BYTES; @@ -240,18 +240,43 @@ public class SmsMessage extends SmsMessageBase { return null; } + if (encoding == ENCODING_UNKNOWN) { + // Find the best encoding to use + TextEncodingDetails ted = calculateLength(message, false); + encoding = ted.codeUnitSize; + languageTable = ted.languageTable; + languageShiftTable = ted.languageShiftTable; + + if (encoding == ENCODING_7BIT && (languageTable != 0 || languageShiftTable != 0)) { + if (header != null) { + SmsHeader smsHeader = SmsHeader.fromByteArray(header); + if (smsHeader.languageTable != languageTable + || smsHeader.languageShiftTable != languageShiftTable) { + Log.w(LOG_TAG, "Updating language table in SMS header: " + + smsHeader.languageTable + " -> " + languageTable + ", " + + smsHeader.languageShiftTable + " -> " + languageShiftTable); + smsHeader.languageTable = languageTable; + smsHeader.languageShiftTable = languageShiftTable; + header = SmsHeader.toByteArray(smsHeader); + } + } else { + SmsHeader smsHeader = new SmsHeader(); + smsHeader.languageTable = languageTable; + smsHeader.languageShiftTable = languageShiftTable; + header = SmsHeader.toByteArray(smsHeader); + } + } + } + SubmitPdu ret = new SubmitPdu(); // MTI = SMS-SUBMIT, UDHI = header != null byte mtiByte = (byte)(0x01 | (header != null ? 0x40 : 0x00)); ByteArrayOutputStream bo = getSubmitPduHead( scAddress, destinationAddress, mtiByte, statusReportRequested, ret); + // User Data (and length) byte[] userData; - if (encoding == ENCODING_UNKNOWN) { - // First, try encoding it with the GSM alphabet - encoding = ENCODING_7BIT; - } try { if (encoding == ENCODING_7BIT) { userData = GsmAlphabet.stringToGsm7BitPackedWithHeader(message, header, @@ -283,6 +308,7 @@ public class SmsMessage extends SmsMessageBase { if (encoding == ENCODING_7BIT) { if ((0xff & userData[0]) > MAX_USER_DATA_SEPTETS) { // Message too long + Log.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " septets)"); return null; } // TP-Data-Coding-Scheme @@ -297,6 +323,7 @@ public class SmsMessage extends SmsMessageBase { } else { // assume UCS-2 if ((0xff & userData[0]) > MAX_USER_DATA_BYTES) { // Message too long + Log.e(LOG_TAG, "Message too long (" + (0xff & userData[0]) + " bytes)"); return null; } // TP-Data-Coding-Scheme diff --git a/tests/FrameworkPerf/Android.mk b/tests/FrameworkPerf/Android.mk index 03893d6254c5..2eb52f0ecf81 100644 --- a/tests/FrameworkPerf/Android.mk +++ b/tests/FrameworkPerf/Android.mk @@ -7,6 +7,8 @@ LOCAL_SRC_FILES := $(call all-subdir-java-files) LOCAL_PACKAGE_NAME := FrameworkPerf +LOCAL_JAVA_LIBRARIES := android.test.runner + LOCAL_AAPT_FLAGS = -c 120dpi,240dpi,160dpi,161dpi,320dpi,nodpi include $(BUILD_PACKAGE) diff --git a/tests/FrameworkPerf/AndroidManifest.xml b/tests/FrameworkPerf/AndroidManifest.xml index e88f4fb6e02f..2591aaf8f1a6 100644 --- a/tests/FrameworkPerf/AndroidManifest.xml +++ b/tests/FrameworkPerf/AndroidManifest.xml @@ -4,6 +4,7 @@ <uses-sdk android:minSdkVersion="5" /> <application android:hardwareAccelerated="false"> + <uses-library android:name="android.test.runner" /> <activity android:name="FrameworkPerfActivity" android:label="Framework Perf"> <intent-filter> <action android:name="android.intent.action.MAIN" /> @@ -20,4 +21,9 @@ <receiver android:name="Receiver" android:exported="true"> </receiver> </application> + + <instrumentation android:name="android.test.InstrumentationTestRunner" + android:targetPackage="com.android.frameworkperf" + android:label="Framework Perf Runner" + /> </manifest> diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java index 175f227f7e0f..8ee5978729e8 100644 --- a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java +++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfActivity.java @@ -73,6 +73,8 @@ public class FrameworkPerfActivity extends Activity final ArrayList<RunResult> mResults = new ArrayList<RunResult>(); + Object mResultNotifier = new Object(); + class TestConnection implements ServiceConnection, IBinder.DeathRecipient { Messenger mService; boolean mLinked; @@ -266,7 +268,9 @@ public class FrameworkPerfActivity extends Activity log(String.format("%s: fg=%d*%gms/op (%dms) / bg=%d*%gms/op (%dms)", result.name, result.fgOps, result.getFgMsPerOp(), result.fgTime, result.bgOps, result.getBgMsPerOp(), result.bgTime)); - mResults.add(result); + synchronized (mResults) { + mResults.add(result); + } if (!mStarted) { log("Stop"); stopRunning(); @@ -376,7 +380,9 @@ public class FrameworkPerfActivity extends Activity startService(new Intent(this, SchedulerService.class)); mCurOpIndex = 0; mMaxRunTime = Integer.parseInt(mTestTime.getText().toString()); - mResults.clear(); + synchronized (mResults) { + mResults.clear(); + } startCurOp(); } } @@ -393,17 +399,22 @@ public class FrameworkPerfActivity extends Activity mBgSpinner.setEnabled(true); updateWakeLock(); stopService(new Intent(this, SchedulerService.class)); - for (int i=0; i<mResults.size(); i++) { - RunResult result = mResults.get(i); - float fgMsPerOp = result.getFgMsPerOp(); - float bgMsPerOp = result.getBgMsPerOp(); - String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : ""; - String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : ""; - Log.i("PerfRes", "\t" + result.name + "\t" + result.fgOps - + "\t" + result.getFgMsPerOp() + "\t" + result.fgTime - + "\t" + result.fgLongName + "\t" + result.bgOps - + "\t" + result.getBgMsPerOp() + "\t" + result.bgTime - + "\t" + result.bgLongName); + synchronized (mResults) { + for (int i=0; i<mResults.size(); i++) { + RunResult result = mResults.get(i); + float fgMsPerOp = result.getFgMsPerOp(); + float bgMsPerOp = result.getBgMsPerOp(); + String fgMsPerOpStr = fgMsPerOp != 0 ? Float.toString(fgMsPerOp) : ""; + String bgMsPerOpStr = bgMsPerOp != 0 ? Float.toString(bgMsPerOp) : ""; + Log.i("PerfRes", "\t" + result.name + "\t" + result.fgOps + + "\t" + result.getFgMsPerOp() + "\t" + result.fgTime + + "\t" + result.fgLongName + "\t" + result.bgOps + + "\t" + result.getBgMsPerOp() + "\t" + result.bgTime + + "\t" + result.bgLongName); + } + } + synchronized (mResultNotifier) { + mResultNotifier.notifyAll(); } } } diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfTest.java b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfTest.java new file mode 100644 index 000000000000..2a53b06e3c36 --- /dev/null +++ b/tests/FrameworkPerf/src/com/android/frameworkperf/FrameworkPerfTest.java @@ -0,0 +1,41 @@ +package com.android.frameworkperf; + +import android.app.Activity; +import android.os.Bundle; +import android.test.ActivityInstrumentationTestCase2; + +public class FrameworkPerfTest extends ActivityInstrumentationTestCase2<FrameworkPerfActivity> { + + private static final int TEST_TIMEOUT = 15 * 60 * 1000; //15 minutes + + public FrameworkPerfTest() { + super("com.android.frameworkperf", FrameworkPerfActivity.class); + } + + public void testFrameworkPerf() { + final FrameworkPerfActivity activity = getActivity(); + synchronized (activity.mResultNotifier) { + getInstrumentation().runOnMainSync(new Runnable() { + @Override + public void run() { + activity.startRunning(); + } + }); + try { + activity.mResultNotifier.wait(TEST_TIMEOUT); + } catch (InterruptedException e) { + fail("test interrupted."); + } + } + Bundle testResult = new Bundle(); + synchronized (activity.mResults) { + assertTrue("test results were empty.", activity.mResults.size() > 0); + for (RunResult result : activity.mResults) { + testResult.putString(result.name, String.format("%f,%d,%d,%f,%d,%d", + result.getFgMsPerOp(), result.fgOps, result.fgTime, + result.getBgMsPerOp(), result.bgOps, result.bgTime)); + } + } + getInstrumentation().sendStatus(Activity.RESULT_OK, testResult); + } +} diff --git a/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java b/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java index 3d939bdf6a80..8cf1ac2932e3 100644 --- a/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java +++ b/tests/FrameworkPerf/src/com/android/frameworkperf/TestService.java @@ -33,6 +33,7 @@ import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Paint; import android.os.Bundle; import android.os.FileUtils; import android.os.Handler; @@ -64,6 +65,9 @@ public class TestService extends Service { new MethodCallOp(), new ReadFileOp(), new SchedulerOp(), new SchedulerOp(), new GcOp(), new NoOp(), + new ObjectGcOp(), new NoOp(), + new FinalizingGcOp(), new NoOp(), + new PaintGcOp(), new NoOp(), new IpcOp(), new NoOp(), new IpcOp(), new CpuOp(), new IpcOp(), new SchedulerOp(), @@ -111,6 +115,10 @@ public class TestService extends Service { new CpuOp(), new SchedulerOp(), new MethodCallOp(), + new GcOp(), + new ObjectGcOp(), + new FinalizingGcOp(), + new PaintGcOp(), new IpcOp(), new CreateFileOp(), new CreateWriteFileOp(), @@ -467,6 +475,47 @@ public class TestService extends Service { } } + static class ObjectGcOp extends Op { + ObjectGcOp() { + super("ObjectGc", "Run garbage collector with simple objects"); + } + + boolean onRun() { + Object obj = new Object(); + return true; + } + } + + static class FinalizingGcOp extends Op { + class Finalizable { + Finalizable() {} + @Override + protected void finalize() throws Throwable { + super.finalize(); + } + } + + FinalizingGcOp() { + super("FinalizingGc", "Run garbage collector with finalizable objects"); + } + + boolean onRun() { + Finalizable obj = new Finalizable(); + return true; + } + } + + static class PaintGcOp extends Op { + PaintGcOp() { + super("PaintGc", "Run garbage collector with Paint objects"); + } + + boolean onRun() { + Paint p = new Paint(); + return true; + } + } + static class MethodCallOp extends Op { MethodCallOp() { super("MethodCall", "Method call"); diff --git a/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml index 823467758827..59a251dbf837 100644 --- a/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml +++ b/tests/RenderScriptTests/PerfTest/res/menu/loader_menu.xml @@ -18,8 +18,11 @@ --> <menu xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:id="@+id/benchmark_mode" - android:title="@string/benchmark_mode" /> + <item android:id="@+id/benchmark_all" + android:title="@string/benchmark_all" /> + <item android:id="@+id/benchmark_one" + android:title="@string/benchmark_one" /> <item android:id="@+id/debug_mode" android:title="@string/debug_mode" /> </menu> + diff --git a/tests/RenderScriptTests/PerfTest/res/raw/singletexf.glsl b/tests/RenderScriptTests/PerfTest/res/raw/singletexf.glsl new file mode 100644 index 000000000000..83dfc7f4b1f5 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/res/raw/singletexf.glsl @@ -0,0 +1,8 @@ +varying vec2 varTex0; + +void main() { + lowp vec3 col0 = texture2D(UNI_Tex0, varTex0).rgb; + gl_FragColor.xyz = col0; + gl_FragColor.w = 0.5; +} + diff --git a/tests/RenderScriptTests/PerfTest/res/values/strings.xml b/tests/RenderScriptTests/PerfTest/res/values/strings.xml index 627ac212db8d..ce9819e8d815 100644 --- a/tests/RenderScriptTests/PerfTest/res/values/strings.xml +++ b/tests/RenderScriptTests/PerfTest/res/values/strings.xml @@ -19,6 +19,8 @@ <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2"> <skip /> - <string name="benchmark_mode">Benchmark Mode</string> + <string name="benchmark_all">Benchmark All</string> + <string name="benchmark_one">Benchmark One</string> <string name="debug_mode">Debug Mode</string> </resources> + diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java new file mode 100644 index 000000000000..06b4af78fbbc --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/FillTest.java @@ -0,0 +1,151 @@ +/* + * 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 com.android.perftest; + +import android.os.Environment; +import android.content.res.Resources; +import android.renderscript.*; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; + + +import android.util.Log; + + +public class FillTest implements RsBenchBaseTest{ + + private static final String TAG = "FillTest"; + private RenderScriptGL mRS; + private Resources mRes; + + // Custom shaders + private ProgramFragment mProgFragmentMultitex; + private ProgramFragment mProgFragmentSingletex; + private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); + int mBenchmarkDimX; + int mBenchmarkDimY; + + private ScriptC_fill_test mFillScript; + ScriptField_TestScripts_s.Item[] mTests; + + private final String[] mNames = { + "Fill screen 10x singletexture", + "Fill screen 10x 3tex multitexture", + "Fill screen 10x blended singletexture", + "Fill screen 10x blended 3tex multitexture" + }; + + public FillTest() { + mOptionsARGB.inScaled = false; + mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; + mBenchmarkDimX = 1280; + mBenchmarkDimY = 720; + } + + void addTest(int index, int testId, int blend, int quadCount) { + mTests[index] = new ScriptField_TestScripts_s.Item(); + mTests[index].testScript = mFillScript; + mTests[index].testName = Allocation.createFromString(mRS, + mNames[index], + Allocation.USAGE_SCRIPT); + + ScriptField_FillTestData_s.Item dataItem = new ScriptField_FillTestData_s.Item(); + dataItem.testId = testId; + dataItem.blend = blend; + dataItem.quadCount = quadCount; + ScriptField_FillTestData_s testData = new ScriptField_FillTestData_s(mRS, 1); + testData.set(dataItem, 0, true); + mTests[index].testData = testData.getAllocation(); + } + + public boolean init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + initCustomShaders(); + initFillScript(); + mTests = new ScriptField_TestScripts_s.Item[mNames.length]; + + int index = 0; + + addTest(index++, 1 /*testId*/, 0 /*blend*/, 10 /*quadCount*/); + addTest(index++, 0 /*testId*/, 0 /*blend*/, 10 /*quadCount*/); + addTest(index++, 1 /*testId*/, 1 /*blend*/, 10 /*quadCount*/); + addTest(index++, 0 /*testId*/, 1 /*blend*/, 10 /*quadCount*/); + + return true; + } + + public ScriptField_TestScripts_s.Item[] getTests() { + return mTests; + } + + public String[] getTestNames() { + return mNames; + } + + private void initCustomShaders() { + ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.multitexf); + for (int texCount = 0; texCount < 3; texCount ++) { + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + } + mProgFragmentMultitex = pfbCustom.create(); + + pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.singletexf); + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + mProgFragmentSingletex = pfbCustom.create(); + } + + private Allocation loadTextureARGB(int id) { + Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB); + return Allocation.createFromBitmap(mRS, b, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + private Allocation loadTextureRGB(int id) { + return Allocation.createFromBitmapResource(mRS, mRes, id, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + void initFillScript() { + mFillScript = new ScriptC_fill_test(mRS, mRes, R.raw.fill_test); + + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + ProgramVertexFixedFunction progVertex = pvb.create(); + ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)progVertex).bindConstants(PVA); + Matrix4f proj = new Matrix4f(); + proj.loadOrthoWindow(mBenchmarkDimX, mBenchmarkDimY); + PVA.setProjection(proj); + mFillScript.set_gProgVertex(progVertex); + + mFillScript.set_gProgFragmentTexture(mProgFragmentSingletex); + mFillScript.set_gProgFragmentMultitex(mProgFragmentMultitex); + mFillScript.set_gProgStoreBlendNone(ProgramStore.BLEND_NONE_DEPTH_NONE(mRS)); + mFillScript.set_gProgStoreBlendAlpha(ProgramStore.BLEND_ALPHA_DEPTH_NONE(mRS)); + + mFillScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); + mFillScript.set_gLinearWrap(Sampler.WRAP_LINEAR(mRS)); + mFillScript.set_gTexTorus(loadTextureRGB(R.drawable.torusmap)); + mFillScript.set_gTexOpaque(loadTextureRGB(R.drawable.data)); + mFillScript.set_gTexTransparent(loadTextureARGB(R.drawable.leaf)); + mFillScript.set_gTexChecker(loadTextureRGB(R.drawable.checker)); + } +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/MeshTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/MeshTest.java new file mode 100644 index 000000000000..f39e7db3f00d --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/MeshTest.java @@ -0,0 +1,167 @@ +/* + * 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 com.android.perftest; + +import android.os.Environment; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.renderscript.*; +import android.renderscript.Element.DataKind; +import android.renderscript.Element.DataType; +import android.renderscript.Allocation.MipmapControl; +import android.renderscript.Program.TextureType; +import android.renderscript.RenderScript.RSMessageHandler; +import android.renderscript.Mesh.Primitive; +import android.renderscript.Matrix4f; +import android.renderscript.ProgramVertexFixedFunction; + +import android.util.Log; + + +public class MeshTest implements RsBenchBaseTest{ + + private static final String TAG = "MeshTest"; + private RenderScriptGL mRS; + private Resources mRes; + + int mBenchmarkDimX; + int mBenchmarkDimY; + + private Mesh m10by10Mesh; + private Mesh m100by100Mesh; + private Mesh mWbyHMesh; + + private ScriptC_mesh_test mGeoScript; + + private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); + + ScriptField_TestScripts_s.Item[] mTests; + + private final String[] mNames = { + "Full screen mesh 10 by 10", + "Full screen mesh 100 by 100", + "Full screen mesh W / 4 by H / 4" + }; + + public MeshTest() { + mBenchmarkDimX = 1280; + mBenchmarkDimY = 720; + } + + void addTest(int index, int meshNum) { + mTests[index] = new ScriptField_TestScripts_s.Item(); + mTests[index].testScript = mGeoScript; + mTests[index].testName = Allocation.createFromString(mRS, + mNames[index], + Allocation.USAGE_SCRIPT); + + ScriptField_MeshTestData_s.Item dataItem = new ScriptField_MeshTestData_s.Item(); + dataItem.meshNum = meshNum; + ScriptField_MeshTestData_s testData = new ScriptField_MeshTestData_s(mRS, 1); + testData.set(dataItem, 0, true); + mTests[index].testData = testData.getAllocation(); + } + + public boolean init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + initGeoScript(); + mTests = new ScriptField_TestScripts_s.Item[mNames.length]; + + int index = 0; + addTest(index++, 0 /*meshNum*/); + addTest(index++, 1 /*meshNum*/); + addTest(index++, 2 /*meshNum*/); + + return true; + } + + public ScriptField_TestScripts_s.Item[] getTests() { + return mTests; + } + + public String[] getTestNames() { + return mNames; + } + + private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) { + + Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, + 2, Mesh.TriangleMeshBuilder.TEXTURE_0); + + for (int y = 0; y <= hResolution; y++) { + final float normalizedY = (float)y / hResolution; + final float yOffset = (normalizedY - 0.5f) * height; + for (int x = 0; x <= wResolution; x++) { + float normalizedX = (float)x / wResolution; + float xOffset = (normalizedX - 0.5f) * width; + tmb.setTexture((float)x % 2, (float)y % 2); + tmb.addVertex(xOffset, yOffset); + } + } + + for (int y = 0; y < hResolution; y++) { + final int curY = y * (wResolution + 1); + final int belowY = (y + 1) * (wResolution + 1); + for (int x = 0; x < wResolution; x++) { + int curV = curY + x; + int belowV = belowY + x; + tmb.addTriangle(curV, belowV, curV + 1); + tmb.addTriangle(belowV, belowV + 1, curV + 1); + } + } + + return tmb.create(true); + } + + private Allocation loadTextureRGB(int id) { + return Allocation.createFromBitmapResource(mRS, mRes, id, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + void initGeoScript() { + mGeoScript = new ScriptC_mesh_test(mRS, mRes, R.raw.mesh_test); + + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + ProgramVertexFixedFunction progVertex = pvb.create(); + ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)progVertex).bindConstants(PVA); + Matrix4f proj = new Matrix4f(); + proj.loadOrthoWindow(mBenchmarkDimX, mBenchmarkDimY); + PVA.setProjection(proj); + + mGeoScript.set_gProgVertex(progVertex); + ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + mGeoScript.set_gProgFragmentTexture(texBuilder.create()); + mGeoScript.set_gProgStoreBlendNone(ProgramStore.BLEND_NONE_DEPTH_NONE(mRS)); + + mGeoScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); + mGeoScript.set_gTexOpaque(loadTextureRGB(R.drawable.data)); + + m10by10Mesh = getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, 10, 10); + m100by100Mesh = getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, 100, 100); + mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4); + + mGeoScript.set_g10by10Mesh(m10by10Mesh); + mGeoScript.set_g100by100Mesh(m100by100Mesh); + mGeoScript.set_gWbyHMesh(mWbyHMesh); + } +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java index b336a4d03550..4ed42b423f31 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBench.java @@ -93,22 +93,36 @@ public class RsBench extends Activity { public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { - case R.id.benchmark_mode: - mView.setBenchmarkMode(); + case R.id.benchmark_all: + mView.setBenchmarkMode(-1); return true; - case R.id.debug_mode: + case R.id.benchmark_one: AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle("Pick a Test"); builder.setItems(mView.getTestNames(), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int item) { Toast.makeText(getApplicationContext(), + "Starting to benchmark: " + mView.getTestNames()[item], + Toast.LENGTH_SHORT).show(); + mView.setBenchmarkMode(item); + } + }); + builder.show(); + return true; + case R.id.debug_mode: + AlertDialog.Builder debugBuilder = new AlertDialog.Builder(this); + debugBuilder.setTitle("Pick a Test"); + debugBuilder.setItems(mView.getTestNames(), + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int item) { + Toast.makeText(getApplicationContext(), "Switching to: " + mView.getTestNames()[item], Toast.LENGTH_SHORT).show(); mView.setDebugMode(item); } }); - builder.show(); + debugBuilder.show(); return true; default: return super.onOptionsItemSelected(item); diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java new file mode 100644 index 000000000000..a9e1777f436a --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchBaseTest.java @@ -0,0 +1,26 @@ +/* + * 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 com.android.perftest; +import android.renderscript.*; +import android.content.res.Resources; + +interface RsBenchBaseTest { + boolean init(RenderScriptGL rs, Resources res); + + ScriptField_TestScripts_s.Item[] getTests(); + String[] getTestNames(); +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java index c375be5e58bc..c51bd8269d16 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchRS.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * Copyright (C) 2010-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. @@ -26,16 +26,11 @@ import java.io.OutputStream; import android.os.Environment; import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; import android.renderscript.*; import android.renderscript.Element.DataKind; import android.renderscript.Element.DataType; import android.renderscript.Allocation.MipmapControl; import android.renderscript.Program.TextureType; -import android.renderscript.ProgramStore.DepthFunc; -import android.renderscript.ProgramStore.BlendSrcFunc; -import android.renderscript.ProgramStore.BlendDstFunc; import android.renderscript.RenderScript.RSMessageHandler; import android.renderscript.Sampler.Value; import android.renderscript.Mesh.Primitive; @@ -48,10 +43,6 @@ import android.util.Log; public class RsBenchRS { private static final String TAG = "RsBenchRS"; - private static final String SAMPLE_TEXT = "Bench Test"; - private static final String LIST_TEXT = - "This is a sample list of text to show in the list view"; - private static int PARTICLES_COUNT = 12000; int mWidth; int mHeight; int mLoops; @@ -68,8 +59,6 @@ public class RsBenchRS { mRes = res; mWidth = width; mHeight = height; - mOptionsARGB.inScaled = false; - mOptionsARGB.inPreferredConfig = Bitmap.Config.ARGB_8888; mMode = 0; mMaxModes = 0; mLoops = loops; @@ -84,10 +73,8 @@ public class RsBenchRS { private Resources mRes; private RenderScriptGL mRS; - private ProgramStore mProgStoreBlendNoneDepth; private ProgramStore mProgStoreBlendNone; private ProgramStore mProgStoreBlendAlpha; - private ProgramStore mProgStoreBlendAdd; private ProgramFragment mProgFragmentTexture; private ProgramFragment mProgFragmentColor; @@ -96,47 +83,9 @@ public class RsBenchRS { private ProgramVertexFixedFunction.Constants mPVA; private ProgramVertexFixedFunction.Constants mPvProjectionAlloc; - // Custom shaders - private ProgramVertex mProgVertexCustom; - private ProgramFragment mProgFragmentCustom; - private ProgramFragment mProgFragmentMultitex; - private ProgramVertex mProgVertexPixelLight; - private ProgramVertex mProgVertexPixelLightMove; - private ProgramFragment mProgFragmentPixelLight; - private ScriptField_VertexShaderConstants_s mVSConst; - private ScriptField_FragentShaderConstants_s mFSConst; - private ScriptField_VertexShaderConstants3_s mVSConstPixel; - private ScriptField_FragentShaderConstants3_s mFSConstPixel; - - - private Allocation mTexTorus; - private Allocation mTexOpaque; - private Allocation mTexTransparent; - private Allocation mTexChecker; - private Allocation mTexGlobe; - - private Mesh m10by10Mesh; - private Mesh m100by100Mesh; - private Mesh mWbyHMesh; - private Mesh mTorus; - private Mesh mSingleMesh; - private Mesh mParticlesMesh; - - Font mFontSans; - Font mFontSerif; - private Allocation mTextAlloc; - - private ScriptField_ListAllocs_s mTextureAllocs; - private ScriptField_ListAllocs_s mSampleTextAllocs; - private ScriptField_ListAllocs_s mSampleListViewAllocs; - private ScriptField_VpConsts mPvStarAlloc; - - private ScriptC_rsbench mScript; - private ScriptC_text_test mTextScript; - private ScriptC_torus_test mTorusScript; - private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); + ScriptField_TestScripts_s.Item[] mIndividualTests; int mMode; int mMaxModes; @@ -144,10 +93,39 @@ public class RsBenchRS { String[] mTestNames; float[] mLocalTestResults; - public void onActionDown(int x, int y) { - mMode ++; - mMode = mMode % mMaxModes; - mScript.set_gDisplayMode(mMode); + void appendTests(RsBenchBaseTest testSet) { + ScriptField_TestScripts_s.Item[] newTests = testSet.getTests(); + if (mIndividualTests != null) { + ScriptField_TestScripts_s.Item[] combined; + combined = new ScriptField_TestScripts_s.Item[newTests.length + mIndividualTests.length]; + System.arraycopy(mIndividualTests, 0, combined, 0, mIndividualTests.length); + System.arraycopy(newTests, 0, combined, mIndividualTests.length, newTests.length); + mIndividualTests = combined; + } else { + mIndividualTests = newTests; + } + + String[] newNames = testSet.getTestNames(); + if (mTestNames != null) { + String[] combinedNames; + combinedNames = new String[newNames.length + mTestNames.length]; + System.arraycopy(mTestNames, 0, combinedNames, 0, mTestNames.length); + System.arraycopy(newNames, 0, combinedNames, mTestNames.length, newNames.length); + mTestNames = combinedNames; + } else { + mTestNames = newNames; + } + } + + void createTestAllocation() { + int numTests = mIndividualTests.length; + ScriptField_TestScripts_s allTests; + allTests = new ScriptField_TestScripts_s(mRS, numTests); + for (int i = 0; i < numTests; i ++) { + allTests.set(mIndividualTests[i], i, false); + } + allTests.copyAll(); + mScript.bind_gTestScripts(allTests); } private void saveTestResults() { @@ -225,98 +203,6 @@ public class RsBenchRS { } } - ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) { - ProgramStore.Builder builder = new ProgramStore.Builder(rs); - builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS); - builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ONE); - builder.setDitherEnabled(false); - builder.setDepthMaskEnabled(false); - return builder.create(); - } - - private Mesh getMbyNMesh(float width, float height, int wResolution, int hResolution) { - - Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, - 2, Mesh.TriangleMeshBuilder.TEXTURE_0); - - for (int y = 0; y <= hResolution; y++) { - final float normalizedY = (float)y / hResolution; - final float yOffset = (normalizedY - 0.5f) * height; - for (int x = 0; x <= wResolution; x++) { - float normalizedX = (float)x / wResolution; - float xOffset = (normalizedX - 0.5f) * width; - tmb.setTexture((float)x % 2, (float)y % 2); - tmb.addVertex(xOffset, yOffset); - } - } - - for (int y = 0; y < hResolution; y++) { - final int curY = y * (wResolution + 1); - final int belowY = (y + 1) * (wResolution + 1); - for (int x = 0; x < wResolution; x++) { - int curV = curY + x; - int belowV = belowY + x; - tmb.addTriangle(curV, belowV, curV + 1); - tmb.addTriangle(belowV, belowV + 1, curV + 1); - } - } - - return tmb.create(true); - } - - /** - * Create a mesh with a single quad for the given width and height. - */ - private Mesh getSingleMesh(float width, float height) { - Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, - 2, Mesh.TriangleMeshBuilder.TEXTURE_0); - float xOffset = width/2; - float yOffset = height/2; - tmb.setTexture(0, 0); - tmb.addVertex(-1.0f * xOffset, -1.0f * yOffset); - tmb.setTexture(1, 0); - tmb.addVertex(xOffset, -1.0f * yOffset); - tmb.setTexture(1, 1); - tmb.addVertex(xOffset, yOffset); - tmb.setTexture(0, 1); - tmb.addVertex(-1.0f * xOffset, yOffset); - tmb.addTriangle(0, 3, 1); - tmb.addTriangle(1, 3, 2); - return tmb.create(true); - } - - private void initProgramStore() { - // Use stock the stock program store object - mProgStoreBlendNoneDepth = ProgramStore.BLEND_NONE_DEPTH_TEST(mRS); - mProgStoreBlendNone = ProgramStore.BLEND_NONE_DEPTH_NONE(mRS); - - // Create a custom program store - ProgramStore.Builder builder = new ProgramStore.Builder(mRS); - builder.setDepthFunc(ProgramStore.DepthFunc.ALWAYS); - builder.setBlendFunc(ProgramStore.BlendSrcFunc.SRC_ALPHA, - ProgramStore.BlendDstFunc.ONE_MINUS_SRC_ALPHA); - builder.setDitherEnabled(false); - builder.setDepthMaskEnabled(false); - mProgStoreBlendAlpha = builder.create(); - - mProgStoreBlendAdd = BLEND_ADD_DEPTH_NONE(mRS); - - mScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth); - - mScript.set_gProgStoreBlendNone(mProgStoreBlendNone); - mScript.set_gProgStoreBlendAlpha(mProgStoreBlendAlpha); - mScript.set_gProgStoreBlendAdd(mProgStoreBlendAdd); - - // For GALAXY - builder = new ProgramStore.Builder(mRS); - builder.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO); - mRS.bindProgramStore(builder.create()); - - builder.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE); - mScript.set_gPSLights(builder.create()); - - } - private void initProgramFragment() { ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS); @@ -329,64 +215,9 @@ public class RsBenchRS { colBuilder.setVaryingColor(false); mProgFragmentColor = colBuilder.create(); - mScript.set_gProgFragmentColor(mProgFragmentColor); - mScript.set_gProgFragmentTexture(mProgFragmentTexture); - - - - // For Galaxy live wallpaper drawing - ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS); - builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, - ProgramFragmentFixedFunction.Builder.Format.RGB, 0); - ProgramFragment pfb = builder.create(); - pfb.bindSampler(Sampler.WRAP_NEAREST(mRS), 0); - mScript.set_gPFBackground(pfb); - - builder = new ProgramFragmentFixedFunction.Builder(mRS); - builder.setPointSpriteTexCoordinateReplacement(true); - builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE, - ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); - builder.setVaryingColor(true); - ProgramFragment pfs = builder.create(); - pfs.bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0); - mScript.set_gPFStars(pfs); - } - private Matrix4f getProjectionNormalized(int w, int h) { - // range -1,1 in the narrow axis at z = 0. - Matrix4f m1 = new Matrix4f(); - Matrix4f m2 = new Matrix4f(); - - if(w > h) { - float aspect = ((float)w) / h; - m1.loadFrustum(-aspect,aspect, -1,1, 1,100); - } else { - float aspect = ((float)h) / w; - m1.loadFrustum(-1,1, -aspect,aspect, 1,100); - } - - m2.loadRotate(180, 0, 1, 0); - m1.loadMultiply(m1, m2); - - m2.loadScale(-2, 2, 1); - m1.loadMultiply(m1, m2); - - m2.loadTranslate(0, 0, 2); - m1.loadMultiply(m1, m2); - return m1; - } - - private void updateProjectionMatrices() { - Matrix4f projNorm = getProjectionNormalized(mBenchmarkDimX, mBenchmarkDimY); - ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item(); - i.Proj = projNorm; - i.MVP = projNorm; - mPvStarAlloc.set(i, 0, true); - mPvProjectionAlloc.setProjection(projNorm); - } - private void initProgramVertex() { ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); mProgVertex = pvb.create(); @@ -398,201 +229,6 @@ public class RsBenchRS { mPVA.setProjection(proj); mScript.set_gProgVertex(mProgVertex); - - - // For galaxy live wallpaper - mPvStarAlloc = new ScriptField_VpConsts(mRS, 1); - mScript.bind_vpConstants(mPvStarAlloc); - mPvProjectionAlloc = new ProgramVertexFixedFunction.Constants(mRS); - updateProjectionMatrices(); - - pvb = new ProgramVertexFixedFunction.Builder(mRS); - ProgramVertex pvbp = pvb.create(); - ((ProgramVertexFixedFunction)pvbp).bindConstants(mPvProjectionAlloc); - mScript.set_gPVBkProj(pvbp); - - ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS); - String t = "varying vec4 varColor;\n" + - "varying vec2 varTex0;\n" + - "void main() {\n" + - " float dist = ATTRIB_position.y;\n" + - " float angle = ATTRIB_position.x;\n" + - " float x = dist * sin(angle);\n" + - " float y = dist * cos(angle) * 0.892;\n" + - " float p = dist * 5.5;\n" + - " float s = cos(p);\n" + - " float t = sin(p);\n" + - " vec4 pos;\n" + - " pos.x = t * x + s * y;\n" + - " pos.y = s * x - t * y;\n" + - " pos.z = ATTRIB_position.z;\n" + - " pos.w = 1.0;\n" + - " gl_Position = UNI_MVP * pos;\n" + - " gl_PointSize = ATTRIB_color.a * 10.0;\n" + - " varColor.rgb = ATTRIB_color.rgb;\n" + - " varColor.a = 1.0;\n" + - "}\n"; - sb.setShader(t); - sb.addInput(mParticlesMesh.getVertexAllocation(0).getType().getElement()); - sb.addConstant(mPvStarAlloc.getType()); - ProgramVertex pvs = sb.create(); - pvs.bindConstants(mPvStarAlloc.getAllocation(), 0); - mScript.set_gPVStars(pvs); - } - - private void initCustomShaders() { - mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1); - mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1); - - - mVSConstPixel = new ScriptField_VertexShaderConstants3_s(mRS, 1); - mFSConstPixel = new ScriptField_FragentShaderConstants3_s(mRS, 1); - - - // Initialize the shader builder - ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS); - // Specify the resource that contains the shader string - pvbCustom.setShader(mRes, R.raw.shaderv); - // Use a script field to specify the input layout - pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); - // Define the constant input layout - pvbCustom.addConstant(mVSConst.getAllocation().getType()); - mProgVertexCustom = pvbCustom.create(); - // Bind the source of constant data - mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0); - - ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS); - // Specify the resource that contains the shader string - pfbCustom.setShader(mRes, R.raw.shaderf); - // Tell the builder how many textures we have - pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); - // Define the constant input layout - pfbCustom.addConstant(mFSConst.getAllocation().getType()); - mProgFragmentCustom = pfbCustom.create(); - // Bind the source of constant data - mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0); - - pvbCustom = new ProgramVertex.Builder(mRS); - pvbCustom.setShader(mRes, R.raw.shader2v); - pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); - pvbCustom.addConstant(mVSConstPixel.getAllocation().getType()); - mProgVertexPixelLight = pvbCustom.create(); - mProgVertexPixelLight.bindConstants(mVSConstPixel.getAllocation(), 0); - - pvbCustom = new ProgramVertex.Builder(mRS); - pvbCustom.setShader(mRes, R.raw.shader2movev); - pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); - pvbCustom.addConstant(mVSConstPixel.getAllocation().getType()); - mProgVertexPixelLightMove = pvbCustom.create(); - mProgVertexPixelLightMove.bindConstants(mVSConstPixel.getAllocation(), 0); - - pfbCustom = new ProgramFragment.Builder(mRS); - pfbCustom.setShader(mRes, R.raw.shader2f); - pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); - pfbCustom.addConstant(mFSConstPixel.getAllocation().getType()); - mProgFragmentPixelLight = pfbCustom.create(); - mProgFragmentPixelLight.bindConstants(mFSConstPixel.getAllocation(), 0); - - pfbCustom = new ProgramFragment.Builder(mRS); - pfbCustom.setShader(mRes, R.raw.multitexf); - for (int texCount = 0; texCount < 3; texCount ++) { - pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); - } - mProgFragmentMultitex = pfbCustom.create(); - - - mScript.set_gProgFragmentMultitex(mProgFragmentMultitex); - } - - private Allocation loadTextureRGB(int id) { - return Allocation.createFromBitmapResource(mRS, mRes, id, - Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, - Allocation.USAGE_GRAPHICS_TEXTURE); - } - - private Allocation loadTextureARGB(int id) { - Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB); - return Allocation.createFromBitmap(mRS, b, - Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, - Allocation.USAGE_GRAPHICS_TEXTURE); - } - - private void loadImages() { - mTexTorus = loadTextureRGB(R.drawable.torusmap); - mTexOpaque = loadTextureRGB(R.drawable.data); - mTexTransparent = loadTextureARGB(R.drawable.leaf); - mTexChecker = loadTextureRGB(R.drawable.checker); - mTexGlobe = loadTextureRGB(R.drawable.globe); - - mScript.set_gTexTorus(mTexTorus); - mScript.set_gTexOpaque(mTexOpaque); - mScript.set_gTexTransparent(mTexTransparent); - mScript.set_gTexChecker(mTexChecker); - mScript.set_gTexGlobe(mTexGlobe); - - // For Galaxy live wallpaper - mScript.set_gTSpace(loadTextureRGB(R.drawable.space)); - mScript.set_gTLight1(loadTextureRGB(R.drawable.light1)); - mScript.set_gTFlares(loadTextureARGB(R.drawable.flares)); - } - - private void initFonts() { - // Sans font by family name - mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8); - mFontSerif = Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8); - // Create fonts by family and style - - mTextAlloc = Allocation.createFromString(mRS, "String from allocation", Allocation.USAGE_SCRIPT); - - mScript.set_gFontSans(mFontSans); - mScript.set_gFontSerif(mFontSerif); - } - - private void createParticlesMesh() { - ScriptField_Particle p = new ScriptField_Particle(mRS, PARTICLES_COUNT); - - final Mesh.AllocationBuilder meshBuilder = new Mesh.AllocationBuilder(mRS); - meshBuilder.addVertexAllocation(p.getAllocation()); - final int vertexSlot = meshBuilder.getCurrentVertexTypeIndex(); - meshBuilder.addIndexSetType(Primitive.POINT); - mParticlesMesh = meshBuilder.create(); - - mScript.set_gParticlesMesh(mParticlesMesh); - mScript.bind_Particles(p); - } - - private void initMesh() { - m10by10Mesh = getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, 10, 10); - mScript.set_g10by10Mesh(m10by10Mesh); - m100by100Mesh = getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, 100, 100); - mScript.set_g100by100Mesh(m100by100Mesh); - mWbyHMesh= getMbyNMesh(mBenchmarkDimX, mBenchmarkDimY, mBenchmarkDimX/4, mBenchmarkDimY/4); - mScript.set_gWbyHMesh(mWbyHMesh); - mSingleMesh = getSingleMesh(1, 1); // a unit size mesh - mScript.set_gSingleMesh(mSingleMesh); - - FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus); - FileA3D.IndexEntry entry = model.getIndexEntry(0); - if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) { - Log.e("rs", "could not load model"); - } else { - mTorus = (Mesh)entry.getObject(); - } - - createParticlesMesh(); - } - - private void initSamplers() { - mScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); - mScript.set_gLinearWrap(Sampler.WRAP_LINEAR(mRS)); - mScript.set_gMipLinearWrap(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS)); - mScript.set_gNearestClamp(Sampler.CLAMP_NEAREST(mRS)); - } - - private void initProgramRaster() { - mScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS)); - mScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS)); - mScript.set_gCullNone(ProgramRaster.CULL_NONE(mRS)); } private int strlen(byte[] array) { @@ -606,75 +242,30 @@ public class RsBenchRS { private void prepareTestData() { mTestNames = new String[mMaxModes]; mLocalTestResults = new float[mMaxModes]; - int scratchSize = 1024; - Allocation scratch = Allocation.createSized(mRS, Element.U8(mRS), scratchSize); - byte[] tmp = new byte[scratchSize]; - mScript.bind_gStringBuffer(scratch); - for (int i = 0; i < mMaxModes; i ++) { - mScript.invoke_getTestName(i); - scratch.copyTo(tmp); - int len = strlen(tmp); - mTestNames[i] = new String(tmp, 0, len); - } } public void setDebugMode(int num) { mScript.invoke_setDebugMode(num); } - public void setBenchmarkMode() { - mScript.invoke_setBenchmarkMode(); - } - - void initTextScript() { - mTextScript = new ScriptC_text_test(mRS, mRes, R.raw.text_test); - mTextScript.set_gFontSans(mFontSans); - mTextScript.set_gFontSerif(mFontSerif); - } - - void initTorusScript() { - mTorusScript = new ScriptC_torus_test(mRS, mRes, R.raw.torus_test); - mTorusScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS)); - mTorusScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS)); - mTorusScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); - mTorusScript.set_gTorusMesh(mTorus); - mTorusScript.set_gTexTorus(mTexTorus); - mTorusScript.set_gProgVertexCustom(mProgVertexCustom); - mTorusScript.set_gProgFragmentCustom(mProgFragmentCustom); - mTorusScript.set_gProgVertexPixelLight(mProgVertexPixelLight); - mTorusScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove); - mTorusScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight); - mTorusScript.bind_gVSConstPixel(mVSConstPixel); - mTorusScript.bind_gFSConstPixel(mFSConstPixel); - mTorusScript.bind_gVSConstants(mVSConst); - mTorusScript.bind_gFSConstants(mFSConst); - mTorusScript.set_gProgVertex(mProgVertex); - mTorusScript.set_gProgFragmentTexture(mProgFragmentTexture); - mTorusScript.set_gProgFragmentColor(mProgFragmentColor); - mTorusScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth); + public void setBenchmarkMode(int benchNum) { + mScript.invoke_setBenchmarkMode(benchNum); } private void initRS() { mScript = new ScriptC_rsbench(mRS, mRes, R.raw.rsbench); - + mRS.bindRootScript(mScript); mRS.setMessageHandler(mRsMessage); - mMaxModes = mScript.get_gMaxModes(); mScript.set_gMaxLoops(mLoops); prepareTestData(); - initSamplers(); - initMesh(); initProgramVertex(); - initProgramStore(); initProgramFragment(); - initFonts(); - loadImages(); - initProgramRaster(); - initCustomShaders(); + mScript.set_gFontSerif(Font.create(mRS, mRes, "serif", Font.Style.NORMAL, 8)); Type.Builder b = new Type.Builder(mRS, Element.RGBA_8888(mRS)); b.setX(mBenchmarkDimX).setY(mBenchmarkDimY); @@ -692,41 +283,30 @@ public class RsBenchRS { b.create(), Allocation.USAGE_GRAPHICS_RENDER_TARGET); mScript.set_gRenderBufferDepth(offscreen); + mScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); - mTextureAllocs = new ScriptField_ListAllocs_s(mRS, 100); - for (int i = 0; i < 100; i++) { - ScriptField_ListAllocs_s.Item texElem = new ScriptField_ListAllocs_s.Item(); - texElem.item = loadTextureRGB(R.drawable.globe); - mTextureAllocs.set(texElem, i, false); + RsBenchBaseTest test = new TextTest(); + if (test.init(mRS, mRes)) { + appendTests(test); } - mTextureAllocs.copyAll(); - mScript.bind_gTexList100(mTextureAllocs); - - mSampleTextAllocs = new ScriptField_ListAllocs_s(mRS, 100); - for (int i = 0; i < 100; i++) { - ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item(); - textElem.item = Allocation.createFromString(mRS, SAMPLE_TEXT, Allocation.USAGE_SCRIPT); - mSampleTextAllocs.set(textElem, i, false); + test = new FillTest(); + if (test.init(mRS, mRes)) { + appendTests(test); } - mSampleTextAllocs.copyAll(); - mScript.bind_gSampleTextList100(mSampleTextAllocs); - - mSampleListViewAllocs = new ScriptField_ListAllocs_s(mRS, 1000); - for (int i = 0; i < 1000; i++) { - ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item(); - textElem.item = Allocation.createFromString(mRS, LIST_TEXT, Allocation.USAGE_SCRIPT); - mSampleListViewAllocs.set(textElem, i, false); + test = new MeshTest(); + if (test.init(mRS, mRes)) { + appendTests(test); } - mSampleListViewAllocs.copyAll(); - mScript.bind_gListViewText(mSampleListViewAllocs); - - initTextScript(); - initTorusScript(); - - mScript.set_gFontScript(mTextScript); - mScript.set_gTorusScript(mTorusScript); - mScript.set_gDummyAlloc(Allocation.createSized(mRS, Element.I32(mRS), 1)); + test = new TorusTest(); + if (test.init(mRS, mRes)) { + appendTests(test); + } + test = new UiTest(); + if (test.init(mRS, mRes)) { + appendTests(test); + } + createTestAllocation(); - mRS.bindRootScript(mScript); + mScript.set_gLoadComplete(true); } } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java index 61aa3e129395..004e6bf4131f 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/RsBenchView.java @@ -71,24 +71,6 @@ public class RsBenchView extends RSSurfaceView { } } - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - return super.onKeyDown(keyCode, event); - } - - - @Override - public boolean onTouchEvent(MotionEvent ev) { - boolean ret = false; - int act = ev.getAction(); - if (act == ev.ACTION_DOWN) { - mRender.onActionDown((int)ev.getX(), (int)ev.getY()); - ret = true; - } - - return ret; - } - /** * Set the total number of loops the benchmark tests will run * before the test results are collected. @@ -106,8 +88,8 @@ public class RsBenchView extends RSSurfaceView { return mRender.testIsFinished(); } - void setBenchmarkMode() { - mRender.setBenchmarkMode(); + void setBenchmarkMode(int benchNum) { + mRender.setBenchmarkMode(benchNum); } void setDebugMode(int num) { diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TextTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TextTest.java new file mode 100644 index 000000000000..ca9e660c67d7 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TextTest.java @@ -0,0 +1,90 @@ +/* + * 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 com.android.perftest; + +import android.os.Environment; +import android.content.res.Resources; +import android.renderscript.*; +import android.util.DisplayMetrics; + +import android.util.Log; + + +public class TextTest implements RsBenchBaseTest{ + + private static final String TAG = "TextTest"; + private RenderScriptGL mRS; + private Resources mRes; + + private ScriptC_text_test mTextScript; + ScriptField_TestScripts_s.Item[] mTests; + + private final String[] mNames = { + "Fill screen with text 1 time", + "Fill screen with text 3 times", + "Fill screen with text 5 times" + }; + + public TextTest() { + } + + void addTest(int index, int fillNum) { + mTests[index] = new ScriptField_TestScripts_s.Item(); + mTests[index].testScript = mTextScript; + mTests[index].testName = Allocation.createFromString(mRS, + mNames[index], + Allocation.USAGE_SCRIPT); + + ScriptField_TextTestData_s.Item dataItem = new ScriptField_TextTestData_s.Item(); + dataItem.fillNum = fillNum; + ScriptField_TextTestData_s testData = new ScriptField_TextTestData_s(mRS, 1); + testData.set(dataItem, 0, true); + mTests[index].testData = testData.getAllocation(); + } + + public boolean init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + initTextScript(); + mTests = new ScriptField_TestScripts_s.Item[mNames.length]; + + int index = 0; + addTest(index++, 1 /*fillNum*/); + addTest(index++, 3 /*fillNum*/); + addTest(index++, 5 /*fillNum*/); + + return true; + } + + public ScriptField_TestScripts_s.Item[] getTests() { + return mTests; + } + + public String[] getTestNames() { + return mNames; + } + + void initTextScript() { + DisplayMetrics metrics = mRes.getDisplayMetrics(); + + mTextScript = new ScriptC_text_test(mRS, mRes, R.raw.text_test); + mTextScript.set_gFontSans(Font.create(mRS, mRes, "sans-serif", + Font.Style.NORMAL, 8.0f / metrics.density)); + mTextScript.set_gFontSerif(Font.create(mRS, mRes, "serif", + Font.Style.NORMAL, 8.0f / metrics.density)); + } +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TorusTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TorusTest.java new file mode 100644 index 000000000000..d785dc1707bf --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/TorusTest.java @@ -0,0 +1,269 @@ +/* + * 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 com.android.perftest; + +import android.os.Environment; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.renderscript.*; +import android.renderscript.Element.DataKind; +import android.renderscript.Element.DataType; +import android.renderscript.Allocation.MipmapControl; +import android.renderscript.Program.TextureType; +import android.renderscript.RenderScript.RSMessageHandler; +import android.renderscript.Mesh.Primitive; +import android.renderscript.Matrix4f; +import android.renderscript.ProgramVertexFixedFunction; + +import android.util.Log; + + +public class TorusTest implements RsBenchBaseTest{ + + private static final String TAG = "TorusTest"; + private RenderScriptGL mRS; + private Resources mRes; + + private ProgramStore mProgStoreBlendNoneDepth; + private ProgramStore mProgStoreBlendNone; + private ProgramStore mProgStoreBlendAlpha; + + private ProgramFragment mProgFragmentTexture; + private ProgramFragment mProgFragmentColor; + + private ProgramVertex mProgVertex; + private ProgramVertexFixedFunction.Constants mPVA; + private ProgramVertexFixedFunction.Constants mPvProjectionAlloc; + + // Custom shaders + private ProgramVertex mProgVertexCustom; + private ProgramFragment mProgFragmentCustom; + private ProgramFragment mProgFragmentMultitex; + private ProgramVertex mProgVertexPixelLight; + private ProgramVertex mProgVertexPixelLightMove; + private ProgramFragment mProgFragmentPixelLight; + private ScriptField_VertexShaderConstants_s mVSConst; + private ScriptField_FragentShaderConstants_s mFSConst; + private ScriptField_VertexShaderConstants3_s mVSConstPixel; + private ScriptField_FragentShaderConstants3_s mFSConstPixel; + + private Allocation mTexTorus; + private Mesh mTorus; + + private ScriptC_torus_test mTorusScript; + + private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); + + ScriptField_TestScripts_s.Item[] mTests; + + private final String[] mNames = { + "Geo test 25.6k flat color", + "Geo test 51.2k flat color", + "Geo test 204.8k small tries flat color", + "Geo test 25.6k single texture", + "Geo test 51.2k single texture", + "Geo test 204.8k small tries single texture", + "Geo test 25.6k geo heavy vertex", + "Geo test 51.2k geo heavy vertex", + "Geo test 204.8k geo raster load heavy vertex", + "Geo test 25.6k heavy fragment", + "Geo test 51.2k heavy fragment", + "Geo test 204.8k small tries heavy fragment", + "Geo test 25.6k heavy fragment heavy vertex", + "Geo test 51.2k heavy fragment heavy vertex", + "Geo test 204.8k small tries heavy fragment heavy vertex" + }; + + public TorusTest() { + } + + void addTest(int index, int testId, int user1, int user2) { + mTests[index] = new ScriptField_TestScripts_s.Item(); + mTests[index].testScript = mTorusScript; + mTests[index].testName = Allocation.createFromString(mRS, + mNames[index], + Allocation.USAGE_SCRIPT); + + ScriptField_TorusTestData_s.Item dataItem = new ScriptField_TorusTestData_s.Item(); + dataItem.testId = testId; + dataItem.user1 = user1; + dataItem.user2 = user2; + ScriptField_TorusTestData_s testData = new ScriptField_TorusTestData_s(mRS, 1); + testData.set(dataItem, 0, true); + mTests[index].testData = testData.getAllocation(); + } + + public boolean init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + initCustomShaders(); + loadImages(); + initMesh(); + initTorusScript(); + mTests = new ScriptField_TestScripts_s.Item[mNames.length]; + + int index = 0; + addTest(index++, 0, 0 /*useTexture*/, 1 /*numMeshes*/); + addTest(index++, 0, 0 /*useTexture*/, 2 /*numMeshes*/); + addTest(index++, 0, 0 /*useTexture*/, 8 /*numMeshes*/); + addTest(index++, 0, 1 /*useTexture*/, 1 /*numMeshes*/); + addTest(index++, 0, 1 /*useTexture*/, 2 /*numMeshes*/); + addTest(index++, 0, 1 /*useTexture*/, 8 /*numMeshes*/); + + // Secont test + addTest(index++, 1, 1 /*numMeshes*/, 0 /*unused*/); + addTest(index++, 1, 2 /*numMeshes*/, 0 /*unused*/); + addTest(index++, 1, 8 /*numMeshes*/, 0 /*unused*/); + + // Third test + addTest(index++, 2, 1 /*numMeshes*/, 0 /*heavyVertex*/); + addTest(index++, 2, 2 /*numMeshes*/, 0 /*heavyVertex*/); + addTest(index++, 2, 8 /*numMeshes*/, 0 /*heavyVertex*/); + addTest(index++, 2, 1 /*numMeshes*/, 1 /*heavyVertex*/); + addTest(index++, 2, 2 /*numMeshes*/, 1 /*heavyVertex*/); + addTest(index++, 2, 8 /*numMeshes*/, 1 /*heavyVertex*/); + + return true; + } + + public ScriptField_TestScripts_s.Item[] getTests() { + return mTests; + } + + public String[] getTestNames() { + return mNames; + } + + private void initCustomShaders() { + mVSConst = new ScriptField_VertexShaderConstants_s(mRS, 1); + mFSConst = new ScriptField_FragentShaderConstants_s(mRS, 1); + + mVSConstPixel = new ScriptField_VertexShaderConstants3_s(mRS, 1); + mFSConstPixel = new ScriptField_FragentShaderConstants3_s(mRS, 1); + + // Initialize the shader builder + ProgramVertex.Builder pvbCustom = new ProgramVertex.Builder(mRS); + // Specify the resource that contains the shader string + pvbCustom.setShader(mRes, R.raw.shaderv); + // Use a script field to specify the input layout + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + // Define the constant input layout + pvbCustom.addConstant(mVSConst.getAllocation().getType()); + mProgVertexCustom = pvbCustom.create(); + // Bind the source of constant data + mProgVertexCustom.bindConstants(mVSConst.getAllocation(), 0); + + ProgramFragment.Builder pfbCustom = new ProgramFragment.Builder(mRS); + // Specify the resource that contains the shader string + pfbCustom.setShader(mRes, R.raw.shaderf); + // Tell the builder how many textures we have + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + // Define the constant input layout + pfbCustom.addConstant(mFSConst.getAllocation().getType()); + mProgFragmentCustom = pfbCustom.create(); + // Bind the source of constant data + mProgFragmentCustom.bindConstants(mFSConst.getAllocation(), 0); + + pvbCustom = new ProgramVertex.Builder(mRS); + pvbCustom.setShader(mRes, R.raw.shader2v); + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + pvbCustom.addConstant(mVSConstPixel.getAllocation().getType()); + mProgVertexPixelLight = pvbCustom.create(); + mProgVertexPixelLight.bindConstants(mVSConstPixel.getAllocation(), 0); + + pvbCustom = new ProgramVertex.Builder(mRS); + pvbCustom.setShader(mRes, R.raw.shader2movev); + pvbCustom.addInput(ScriptField_VertexShaderInputs_s.createElement(mRS)); + pvbCustom.addConstant(mVSConstPixel.getAllocation().getType()); + mProgVertexPixelLightMove = pvbCustom.create(); + mProgVertexPixelLightMove.bindConstants(mVSConstPixel.getAllocation(), 0); + + pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.shader2f); + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + pfbCustom.addConstant(mFSConstPixel.getAllocation().getType()); + mProgFragmentPixelLight = pfbCustom.create(); + mProgFragmentPixelLight.bindConstants(mFSConstPixel.getAllocation(), 0); + + pfbCustom = new ProgramFragment.Builder(mRS); + pfbCustom.setShader(mRes, R.raw.multitexf); + for (int texCount = 0; texCount < 3; texCount ++) { + pfbCustom.addTexture(Program.TextureType.TEXTURE_2D); + } + mProgFragmentMultitex = pfbCustom.create(); + + ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + colBuilder.setVaryingColor(false); + mProgFragmentColor = colBuilder.create(); + + ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + mProgFragmentTexture = texBuilder.create(); + + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + mProgVertex = pvb.create(); + ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)mProgVertex).bindConstants(PVA); + Matrix4f proj = new Matrix4f(); + proj.loadOrthoWindow(1280, 720); + PVA.setProjection(proj); + } + + private Allocation loadTextureRGB(int id) { + return Allocation.createFromBitmapResource(mRS, mRes, id, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + private void loadImages() { + mTexTorus = loadTextureRGB(R.drawable.torusmap); + } + + private void initMesh() { + FileA3D model = FileA3D.createFromResource(mRS, mRes, R.raw.torus); + FileA3D.IndexEntry entry = model.getIndexEntry(0); + if (entry == null || entry.getEntryType() != FileA3D.EntryType.MESH) { + Log.e("rs", "could not load model"); + } else { + mTorus = (Mesh)entry.getObject(); + } + } + + void initTorusScript() { + mTorusScript = new ScriptC_torus_test(mRS, mRes, R.raw.torus_test); + mTorusScript.set_gCullFront(ProgramRaster.CULL_FRONT(mRS)); + mTorusScript.set_gCullBack(ProgramRaster.CULL_BACK(mRS)); + mTorusScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); + mTorusScript.set_gTorusMesh(mTorus); + mTorusScript.set_gTexTorus(mTexTorus); + mTorusScript.set_gProgVertexCustom(mProgVertexCustom); + mTorusScript.set_gProgFragmentCustom(mProgFragmentCustom); + mTorusScript.set_gProgVertexPixelLight(mProgVertexPixelLight); + mTorusScript.set_gProgVertexPixelLightMove(mProgVertexPixelLightMove); + mTorusScript.set_gProgFragmentPixelLight(mProgFragmentPixelLight); + mTorusScript.bind_gVSConstPixel(mVSConstPixel); + mTorusScript.bind_gFSConstPixel(mFSConstPixel); + mTorusScript.bind_gVSConstants(mVSConst); + mTorusScript.bind_gFSConstants(mFSConst); + mTorusScript.set_gProgVertex(mProgVertex); + mTorusScript.set_gProgFragmentTexture(mProgFragmentTexture); + mTorusScript.set_gProgFragmentColor(mProgFragmentColor); + mTorusScript.set_gProgStoreBlendNoneDepth(mProgStoreBlendNoneDepth); + } +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/UiTest.java b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/UiTest.java new file mode 100644 index 000000000000..cca237eb7198 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/UiTest.java @@ -0,0 +1,334 @@ +/* + * 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 com.android.perftest; + +import android.os.Environment; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.renderscript.*; +import android.renderscript.Element.DataKind; +import android.renderscript.Element.DataType; +import android.renderscript.Allocation.MipmapControl; +import android.renderscript.Program.TextureType; +import android.renderscript.ProgramStore.DepthFunc; +import android.renderscript.ProgramStore.BlendSrcFunc; +import android.renderscript.ProgramStore.BlendDstFunc; +import android.renderscript.RenderScript.RSMessageHandler; +import android.renderscript.Mesh.Primitive; +import android.renderscript.Matrix4f; +import android.renderscript.ProgramVertexFixedFunction; + +import android.util.Log; + + +public class UiTest implements RsBenchBaseTest{ + + private static final String TAG = "UiTest"; + private static final String SAMPLE_TEXT = "Bench Test"; + private static final String LIST_TEXT = + "This is a sample list of text to show in the list view"; + private static int PARTICLES_COUNT = 12000; + + private RenderScriptGL mRS; + private Resources mRes; + + Font mFontSans; + + private ScriptField_ListAllocs_s mTextureAllocs; + private ScriptField_ListAllocs_s mSampleTextAllocs; + private ScriptField_ListAllocs_s mSampleListViewAllocs; + private ScriptField_VpConsts mPvStarAlloc; + private ProgramVertexFixedFunction.Constants mPvProjectionAlloc; + + private Mesh mSingleMesh; + private Mesh mParticlesMesh; + + private ScriptC_ui_test mUiScript; + + private final BitmapFactory.Options mOptionsARGB = new BitmapFactory.Options(); + + ScriptField_TestScripts_s.Item[] mTests; + + private final String[] mNames = { + "UI test with icon display 10 by 10", + "UI test with icon display 100 by 100", + "UI test with image and text display 3 pages", + "UI test with image and text display 5 pages", + "UI test with list view", + "UI test with live wallpaper" + }; + + public UiTest() { + } + + void addTest(int index, int testId, int user1, int user2, int user3) { + mTests[index] = new ScriptField_TestScripts_s.Item(); + mTests[index].testScript = mUiScript; + mTests[index].testName = Allocation.createFromString(mRS, + mNames[index], + Allocation.USAGE_SCRIPT); + + ScriptField_UiTestData_s.Item dataItem = new ScriptField_UiTestData_s.Item(); + dataItem.testId = testId; + dataItem.user1 = user1; + dataItem.user2 = user2; + dataItem.user3 = user3; + ScriptField_UiTestData_s testData = new ScriptField_UiTestData_s(mRS, 1); + testData.set(dataItem, 0, true); + mTests[index].testData = testData.getAllocation(); + } + + public boolean init(RenderScriptGL rs, Resources res) { + mRS = rs; + mRes = res; + mFontSans = Font.create(mRS, mRes, "sans-serif", Font.Style.NORMAL, 8); + mSingleMesh = getSingleMesh(1, 1); // a unit size mesh + + initUiScript(); + mTests = new ScriptField_TestScripts_s.Item[mNames.length]; + + int index = 0; + + addTest(index++, 0, 0 /*meshMode*/, 0 /*unused*/, 0 /*unused*/); + addTest(index++, 0, 1 /*meshMode*/, 0 /*unused*/, 0 /*unused*/); + addTest(index++, 1, 7 /*wResolution*/, 5 /*hResolution*/, 0 /*meshMode*/); + addTest(index++, 1, 7 /*wResolution*/, 5 /*hResolution*/, 1 /*meshMode*/); + addTest(index++, 2, 0 /*unused*/, 0 /*unused*/, 0 /*unused*/); + addTest(index++, 3, 7 /*wResolution*/, 5 /*hResolution*/, 0 /*unused*/); + + return true; + } + + public ScriptField_TestScripts_s.Item[] getTests() { + return mTests; + } + + public String[] getTestNames() { + return mNames; + } + + private Allocation loadTextureRGB(int id) { + return Allocation.createFromBitmapResource(mRS, mRes, id, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + private Allocation loadTextureARGB(int id) { + Bitmap b = BitmapFactory.decodeResource(mRes, id, mOptionsARGB); + return Allocation.createFromBitmap(mRS, b, + Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, + Allocation.USAGE_GRAPHICS_TEXTURE); + } + + private void createParticlesMesh() { + ScriptField_Particle p = new ScriptField_Particle(mRS, PARTICLES_COUNT); + + final Mesh.AllocationBuilder meshBuilder = new Mesh.AllocationBuilder(mRS); + meshBuilder.addVertexAllocation(p.getAllocation()); + final int vertexSlot = meshBuilder.getCurrentVertexTypeIndex(); + meshBuilder.addIndexSetType(Primitive.POINT); + mParticlesMesh = meshBuilder.create(); + + mUiScript.set_gParticlesMesh(mParticlesMesh); + mUiScript.bind_Particles(p); + } + + /** + * Create a mesh with a single quad for the given width and height. + */ + private Mesh getSingleMesh(float width, float height) { + Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, + 2, Mesh.TriangleMeshBuilder.TEXTURE_0); + float xOffset = width/2; + float yOffset = height/2; + tmb.setTexture(0, 0); + tmb.addVertex(-1.0f * xOffset, -1.0f * yOffset); + tmb.setTexture(1, 0); + tmb.addVertex(xOffset, -1.0f * yOffset); + tmb.setTexture(1, 1); + tmb.addVertex(xOffset, yOffset); + tmb.setTexture(0, 1); + tmb.addVertex(-1.0f * xOffset, yOffset); + tmb.addTriangle(0, 3, 1); + tmb.addTriangle(1, 3, 2); + return tmb.create(true); + } + + private Matrix4f getProjectionNormalized(int w, int h) { + // range -1,1 in the narrow axis at z = 0. + Matrix4f m1 = new Matrix4f(); + Matrix4f m2 = new Matrix4f(); + + if(w > h) { + float aspect = ((float)w) / h; + m1.loadFrustum(-aspect,aspect, -1,1, 1,100); + } else { + float aspect = ((float)h) / w; + m1.loadFrustum(-1,1, -aspect,aspect, 1,100); + } + + m2.loadRotate(180, 0, 1, 0); + m1.loadMultiply(m1, m2); + + m2.loadScale(-2, 2, 1); + m1.loadMultiply(m1, m2); + + m2.loadTranslate(0, 0, 2); + m1.loadMultiply(m1, m2); + return m1; + } + + private void updateProjectionMatrices() { + Matrix4f projNorm = getProjectionNormalized(1280, 720); + ScriptField_VpConsts.Item i = new ScriptField_VpConsts.Item(); + i.Proj = projNorm; + i.MVP = projNorm; + mPvStarAlloc.set(i, 0, true); + mPvProjectionAlloc.setProjection(projNorm); + } + + void initUiScript() { + mUiScript = new ScriptC_ui_test(mRS, mRes, R.raw.ui_test); + + ProgramFragmentFixedFunction.Builder colBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + colBuilder.setVaryingColor(false); + ProgramFragmentFixedFunction.Builder texBuilder = new ProgramFragmentFixedFunction.Builder(mRS); + texBuilder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + + ProgramVertexFixedFunction.Builder pvb = new ProgramVertexFixedFunction.Builder(mRS); + ProgramVertexFixedFunction progVertex = pvb.create(); + ProgramVertexFixedFunction.Constants PVA = new ProgramVertexFixedFunction.Constants(mRS); + ((ProgramVertexFixedFunction)progVertex).bindConstants(PVA); + Matrix4f proj = new Matrix4f(); + proj.loadOrthoWindow(1280, 720); + PVA.setProjection(proj); + + mUiScript.set_gProgVertex(progVertex); + mUiScript.set_gProgFragmentColor(colBuilder.create()); + mUiScript.set_gProgFragmentTexture(texBuilder.create()); + mUiScript.set_gProgStoreBlendAlpha(ProgramStore.BLEND_ALPHA_DEPTH_NONE(mRS)); + + mUiScript.set_gLinearClamp(Sampler.CLAMP_LINEAR(mRS)); + + mUiScript.set_gTexTorus(loadTextureRGB(R.drawable.torusmap)); + mUiScript.set_gTexOpaque(loadTextureRGB(R.drawable.data)); + mUiScript.set_gTexGlobe(loadTextureRGB(R.drawable.globe)); + mUiScript.set_gSingleMesh(mSingleMesh); + + // For GALAXY + ProgramStore.Builder psb = new ProgramStore.Builder(mRS); + psb.setBlendFunc(BlendSrcFunc.ONE, BlendDstFunc.ZERO); + mRS.bindProgramStore(psb.create()); + + psb.setBlendFunc(BlendSrcFunc.SRC_ALPHA, BlendDstFunc.ONE); + mUiScript.set_gPSLights(psb.create()); + + // For Galaxy live wallpaper drawing + ProgramFragmentFixedFunction.Builder builder = new ProgramFragmentFixedFunction.Builder(mRS); + builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.REPLACE, + ProgramFragmentFixedFunction.Builder.Format.RGB, 0); + ProgramFragment pfb = builder.create(); + pfb.bindSampler(Sampler.WRAP_NEAREST(mRS), 0); + mUiScript.set_gPFBackground(pfb); + + builder = new ProgramFragmentFixedFunction.Builder(mRS); + builder.setPointSpriteTexCoordinateReplacement(true); + builder.setTexture(ProgramFragmentFixedFunction.Builder.EnvMode.MODULATE, + ProgramFragmentFixedFunction.Builder.Format.RGBA, 0); + builder.setVaryingColor(true); + ProgramFragment pfs = builder.create(); + pfs.bindSampler(Sampler.WRAP_LINEAR_MIP_LINEAR(mRS), 0); + mUiScript.set_gPFStars(pfs); + + mTextureAllocs = new ScriptField_ListAllocs_s(mRS, 100); + for (int i = 0; i < 100; i++) { + ScriptField_ListAllocs_s.Item texElem = new ScriptField_ListAllocs_s.Item(); + texElem.item = loadTextureRGB(R.drawable.globe); + mTextureAllocs.set(texElem, i, false); + } + mTextureAllocs.copyAll(); + mUiScript.bind_gTexList100(mTextureAllocs); + + mSampleTextAllocs = new ScriptField_ListAllocs_s(mRS, 100); + for (int i = 0; i < 100; i++) { + ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item(); + textElem.item = Allocation.createFromString(mRS, SAMPLE_TEXT, Allocation.USAGE_SCRIPT); + mSampleTextAllocs.set(textElem, i, false); + } + mSampleTextAllocs.copyAll(); + mUiScript.bind_gSampleTextList100(mSampleTextAllocs); + + mSampleListViewAllocs = new ScriptField_ListAllocs_s(mRS, 1000); + for (int i = 0; i < 1000; i++) { + ScriptField_ListAllocs_s.Item textElem = new ScriptField_ListAllocs_s.Item(); + textElem.item = Allocation.createFromString(mRS, LIST_TEXT, Allocation.USAGE_SCRIPT); + mSampleListViewAllocs.set(textElem, i, false); + } + mSampleListViewAllocs.copyAll(); + mUiScript.bind_gListViewText(mSampleListViewAllocs); + + // For galaxy live wallpaper + mPvStarAlloc = new ScriptField_VpConsts(mRS, 1); + mUiScript.bind_vpConstants(mPvStarAlloc); + mPvProjectionAlloc = new ProgramVertexFixedFunction.Constants(mRS); + updateProjectionMatrices(); + + pvb = new ProgramVertexFixedFunction.Builder(mRS); + ProgramVertex pvbp = pvb.create(); + ((ProgramVertexFixedFunction)pvbp).bindConstants(mPvProjectionAlloc); + mUiScript.set_gPVBkProj(pvbp); + + createParticlesMesh(); + + ProgramVertex.Builder sb = new ProgramVertex.Builder(mRS); + String t = "varying vec4 varColor;\n" + + "varying vec2 varTex0;\n" + + "void main() {\n" + + " float dist = ATTRIB_position.y;\n" + + " float angle = ATTRIB_position.x;\n" + + " float x = dist * sin(angle);\n" + + " float y = dist * cos(angle) * 0.892;\n" + + " float p = dist * 5.5;\n" + + " float s = cos(p);\n" + + " float t = sin(p);\n" + + " vec4 pos;\n" + + " pos.x = t * x + s * y;\n" + + " pos.y = s * x - t * y;\n" + + " pos.z = ATTRIB_position.z;\n" + + " pos.w = 1.0;\n" + + " gl_Position = UNI_MVP * pos;\n" + + " gl_PointSize = ATTRIB_color.a * 10.0;\n" + + " varColor.rgb = ATTRIB_color.rgb;\n" + + " varColor.a = 1.0;\n" + + "}\n"; + sb.setShader(t); + sb.addInput(mParticlesMesh.getVertexAllocation(0).getType().getElement()); + sb.addConstant(mPvStarAlloc.getType()); + ProgramVertex pvs = sb.create(); + pvs.bindConstants(mPvStarAlloc.getAllocation(), 0); + mUiScript.set_gPVStars(pvs); + + // For Galaxy live wallpaper + mUiScript.set_gTSpace(loadTextureRGB(R.drawable.space)); + mUiScript.set_gTLight1(loadTextureRGB(R.drawable.light1)); + mUiScript.set_gTFlares(loadTextureARGB(R.drawable.flares)); + + mUiScript.set_gFontSans(mFontSans); + } +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs new file mode 100644 index 000000000000..23832d394eda --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/fill_test.rs @@ -0,0 +1,137 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.perftest) + +#include "rs_graphics.rsh" +#include "subtest_def.rsh" + +rs_program_vertex gProgVertex; +rs_program_fragment gProgFragmentTexture; +rs_program_fragment gProgFragmentMultitex; + +rs_program_store gProgStoreBlendNone; +rs_program_store gProgStoreBlendAlpha; + +rs_allocation gTexOpaque; +rs_allocation gTexTorus; +rs_allocation gTexTransparent; +rs_allocation gTexChecker; + +rs_sampler gLinearClamp; +rs_sampler gLinearWrap; + +typedef struct FillTestData_s { + int testId; + int blend; + int quadCount; +} FillTestData; +FillTestData *gData; + +static float gDt = 0.0f; + +void init() { +} + +static int gRenderSurfaceW = 1280; +static int gRenderSurfaceH = 720; + +static void bindProgramVertexOrtho() { + // Default vertex shader + rsgBindProgramVertex(gProgVertex); + // Setup the projection matrix + rs_matrix4x4 proj; + rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500); + rsgProgramVertexLoadProjectionMatrix(&proj); +} + +static void displaySingletexFill(bool blend, int quadCount) { + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + if (!blend) { + rsgBindProgramStore(gProgStoreBlendNone); + } else { + rsgBindProgramStore(gProgStoreBlendAlpha); + } + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); + + for (int i = 0; i < quadCount; i ++) { + float startX = 5 * i, startY = 5 * i; + float width = gRenderSurfaceW - startX, height = gRenderSurfaceH - startY; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1, + startX + width, startY + height, 0, 1, 1, + startX + width, startY, 0, 1, 0); + } +} + +static void displayMultitextureSample(bool blend, int quadCount) { + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadIdentity(&matrix); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + if (!blend) { + rsgBindProgramStore(gProgStoreBlendNone); + } else { + rsgBindProgramStore(gProgStoreBlendAlpha); + } + rsgBindProgramFragment(gProgFragmentMultitex); + rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp); + rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap); + rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp); + rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker); + rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus); + rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent); + + for (int i = 0; i < quadCount; i ++) { + float startX = 10 * i, startY = 10 * i; + float width = gRenderSurfaceW - startX, height = gRenderSurfaceH - startY; + rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, + startX, startY + height, 0, 0, 1, + startX + width, startY + height, 0, 1, 1, + startX + width, startY, 0, 1, 0); + } +} + + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + TestData *testData = (TestData*)usrData; + gRenderSurfaceW = testData->renderSurfaceW; + gRenderSurfaceH = testData->renderSurfaceH; + gDt = testData->dt; + + gData = (FillTestData*)v_in; + + switch(gData->testId) { + case 0: + displayMultitextureSample(gData->blend == 1 ? true : false, gData->quadCount); + break; + case 1: + displaySingletexFill(gData->blend == 1 ? true : false, gData->quadCount); + break; + default: + rsDebug("Wrong test number", 0); + break; + } +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/mesh_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/mesh_test.rs new file mode 100644 index 000000000000..d7e4857208c9 --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/mesh_test.rs @@ -0,0 +1,89 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.perftest) + +#include "rs_graphics.rsh" +#include "shader_def.rsh" +#include "subtest_def.rsh" + +rs_program_vertex gProgVertex; +rs_program_fragment gProgFragmentTexture; + +rs_program_store gProgStoreBlendNone; + +rs_allocation gTexOpaque; + +rs_mesh g10by10Mesh; +rs_mesh g100by100Mesh; +rs_mesh gWbyHMesh; + +rs_sampler gLinearClamp; +static int gRenderSurfaceW; +static int gRenderSurfaceH; + +static float gDt = 0; + +typedef struct MeshTestData_s { + int meshNum; +} MeshTestData; +MeshTestData *gData; + +void init() { +} + +static void bindProgramVertexOrtho() { + // Default vertex shader + rsgBindProgramVertex(gProgVertex); + // Setup the projection matrix + rs_matrix4x4 proj; + rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500); + rsgProgramVertexLoadProjectionMatrix(&proj); +} + +static void displayMeshSamples(int meshNum) { + + bindProgramVertexOrtho(); + rs_matrix4x4 matrix; + rsMatrixLoadTranslate(&matrix, gRenderSurfaceW/2, gRenderSurfaceH/2, 0); + rsgProgramVertexLoadModelMatrix(&matrix); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendNone); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + + rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); + + if (meshNum == 0) { + rsgDrawMesh(g10by10Mesh); + } else if (meshNum == 1) { + rsgDrawMesh(g100by100Mesh); + } else if (meshNum == 2) { + rsgDrawMesh(gWbyHMesh); + } +} + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + TestData *testData = (TestData*)usrData; + gRenderSurfaceW = testData->renderSurfaceW; + gRenderSurfaceH = testData->renderSurfaceH; + gDt = testData->dt; + + gData = (MeshTestData*)v_in; + + displayMeshSamples(gData->meshNum); +} diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs index db9783530971..2b849a280552 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/rsbench.rs @@ -1,4 +1,4 @@ -// Copyright (C) 2009 The Android Open Source Project +// Copyright (C) 2010-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. @@ -24,104 +24,32 @@ const int RS_MSG_TEST_DONE = 100; const int RS_MSG_RESULTS_READY = 101; -const int gMaxModes = 31; -int gMaxLoops; - -// Parameters for galaxy live wallpaper -rs_allocation gTSpace; -rs_allocation gTLight1; -rs_allocation gTFlares; -rs_mesh gParticlesMesh; - -rs_program_fragment gPFBackground; -rs_program_fragment gPFStars; -rs_program_vertex gPVStars; -rs_program_vertex gPVBkProj; -rs_program_store gPSLights; - -float gXOffset = 0.5f; - -#define ELLIPSE_RATIO 0.892f -#define PI 3.1415f -#define TWO_PI 6.283f -#define ELLIPSE_TWIST 0.023333333f - -static float angle = 50.f; -static int gOldWidth; -static int gOldHeight; -static int gWidth; -static int gHeight; -static float gSpeed[12000]; -static int gGalaxyRadius = 300; -static rs_allocation gParticlesBuffer; - -typedef struct __attribute__((packed, aligned(4))) Particle { - uchar4 color; - float3 position; -} Particle_t; -Particle_t *Particles; - -typedef struct VpConsts { - rs_matrix4x4 Proj; - rs_matrix4x4 MVP; -} VpConsts_t; -VpConsts_t *vpConstants; -// End of parameters for galaxy live wallpaper - -// Allocation to send test names back to java -char *gStringBuffer = 0; +static const int gMaxModes = 64; +int gMaxLoops = 1; +int gDisplayMode = 1; + // Allocation to write the results into static float gResultBuffer[gMaxModes]; -rs_program_vertex gProgVertex; -rs_program_fragment gProgFragmentColor; -rs_program_fragment gProgFragmentTexture; - -rs_program_store gProgStoreBlendNoneDepth; -rs_program_store gProgStoreBlendNone; -rs_program_store gProgStoreBlendAlpha; -rs_program_store gProgStoreBlendAdd; - -rs_allocation gTexOpaque; -rs_allocation gTexTorus; -rs_allocation gTexTransparent; -rs_allocation gTexChecker; -rs_allocation gTexGlobe; - -typedef struct ListAllocs_s { - rs_allocation item; -} ListAllocs; - -ListAllocs *gTexList100; -ListAllocs *gSampleTextList100; -ListAllocs *gListViewText; - -rs_mesh g10by10Mesh; -rs_mesh g100by100Mesh; -rs_mesh gWbyHMesh; -rs_mesh gSingleMesh; - -rs_font gFontSans; rs_font gFontSerif; - -int gDisplayMode; - rs_sampler gLinearClamp; -rs_sampler gLinearWrap; -rs_sampler gMipLinearWrap; -rs_sampler gNearestClamp; -rs_program_raster gCullBack; -rs_program_raster gCullFront; -rs_program_raster gCullNone; +rs_program_vertex gProgVertex; +rs_program_fragment gProgFragmentTexture; + +rs_allocation gRenderBufferColor; +rs_allocation gRenderBufferDepth; -// Export these out to easily set the inputs to shader VertexShaderInputs *gVSInputs; -rs_program_fragment gProgFragmentMultitex; +typedef struct TestScripts_s { + rs_allocation testData; + rs_allocation testName; + rs_script testScript; +} TestScripts; +TestScripts *gTestScripts; -rs_allocation gRenderBufferColor; -rs_allocation gRenderBufferDepth; +bool gLoadComplete = false; static float gDt = 0; @@ -131,158 +59,17 @@ void init() { static int gRenderSurfaceW; static int gRenderSurfaceH; -/** - * Methods to draw the galaxy live wall paper - */ -static float mapf(float minStart, float minStop, float maxStart, float maxStop, float value) { - return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart)); -} - -/** - * Helper function to generate the stars. - */ -static float randomGauss() { - float x1; - float x2; - float w = 2.f; - - while (w >= 1.0f) { - x1 = rsRand(2.0f) - 1.0f; - x2 = rsRand(2.0f) - 1.0f; - w = x1 * x1 + x2 * x2; - } - - w = sqrt(-2.0f * log(w) / w); - return x1 * w; +static void fillSurfaceParams(TestData *testData) { + testData->renderSurfaceW = gRenderSurfaceW; + testData->renderSurfaceH = gRenderSurfaceH; + testData->dt = gDt; } -/** - * Generates the properties for a given star. - */ -static void createParticle(Particle_t *part, int idx, float scale) { - float d = fabs(randomGauss()) * gGalaxyRadius * 0.5f + rsRand(64.0f); - float id = d / gGalaxyRadius; - float z = randomGauss() * 0.4f * (1.0f - id); - float p = -d * ELLIPSE_TWIST; - - if (d < gGalaxyRadius * 0.33f) { - part->color.x = (uchar) (220 + id * 35); - part->color.y = 220; - part->color.z = 220; - } else { - part->color.x = 180; - part->color.y = 180; - part->color.z = (uchar) clamp(140.f + id * 115.f, 140.f, 255.f); - } - // Stash point size * 10 in Alpha - part->color.w = (uchar) (rsRand(1.2f, 2.1f) * 60); - - if (d > gGalaxyRadius * 0.15f) { - z *= 0.6f * (1.0f - id); - } else { - z *= 0.72f; - } - - // Map to the projection coordinates (viewport.x = -1.0 -> 1.0) - d = mapf(-4.0f, gGalaxyRadius + 4.0f, 0.0f, scale, d); - - part->position.x = rsRand(TWO_PI); - part->position.y = d; - gSpeed[idx] = rsRand(0.0015f, 0.0025f) * (0.5f + (scale / d)) * 0.8f; - - part->position.z = z / 5.0f; -} - -/** - * Initialize all the starts, called from Java - */ -void initParticles() { - Particle_t *part = Particles; - float scale = gGalaxyRadius / (gWidth * 0.5f); - int count = rsAllocationGetDimX(gParticlesBuffer); - for (int i = 0; i < count; i ++) { - createParticle(part, i, scale); - part++; - } -} - -static void drawSpace() { - rsgBindProgramFragment(gPFBackground); - rsgBindTexture(gPFBackground, 0, gTSpace); - rsgDrawQuadTexCoords( - 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, - gWidth, 0.0f, 0.0f, 2.0f, 1.0f, - gWidth, gHeight, 0.0f, 2.0f, 0.0f, - 0.0f, gHeight, 0.0f, 0.0f, 0.0f); -} - -static void drawLights() { - rsgBindProgramVertex(gPVBkProj); - rsgBindProgramFragment(gPFBackground); - rsgBindTexture(gPFBackground, 0, gTLight1); - - float scale = 512.0f / gWidth; - float x = -scale - scale * 0.05f; - float y = -scale; - - scale *= 2.0f; - - rsgDrawQuad(x, y, 0.0f, - x + scale * 1.1f, y, 0.0f, - x + scale * 1.1f, y + scale, 0.0f, - x, y + scale, 0.0f); -} - -static void drawParticles(float offset) { - float a = offset * angle; - float absoluteAngle = fabs(a); - - rs_matrix4x4 matrix; - rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, 10.0f - 6.0f * absoluteAngle / 50.0f); - if (gHeight > gWidth) { - rsMatrixScale(&matrix, 6.6f, 6.0f, 1.0f); - } else { - rsMatrixScale(&matrix, 12.6f, 12.0f, 1.0f); - } - rsMatrixRotate(&matrix, absoluteAngle, 1.0f, 0.0f, 0.0f); - rsMatrixRotate(&matrix, a, 0.0f, 0.4f, 0.1f); - rsMatrixLoad(&vpConstants->MVP, &vpConstants->Proj); - rsMatrixMultiply(&vpConstants->MVP, &matrix); - rsgAllocationSyncAll(rsGetAllocation(vpConstants)); - - rsgBindProgramVertex(gPVStars); - rsgBindProgramFragment(gPFStars); - rsgBindProgramStore(gPSLights); - rsgBindTexture(gPFStars, 0, gTFlares); - - Particle_t *vtx = Particles; - int count = rsAllocationGetDimX(gParticlesBuffer); - for (int i = 0; i < count; i++) { - vtx->position.x = vtx->position.x + gSpeed[i]; - vtx++; - } - - rsgDrawMesh(gParticlesMesh); -} -/* end of methods for drawing galaxy */ - static void setupOffscreenTarget() { rsgBindColorTarget(gRenderBufferColor, 0); rsgBindDepthTarget(gRenderBufferDepth); } -rs_script gFontScript; -rs_script gTorusScript; -rs_allocation gDummyAlloc; - -static void displayFontSamples(int fillNum) { - TestData testData; - testData.renderSurfaceW = gRenderSurfaceW; - testData.renderSurfaceH = gRenderSurfaceH; - testData.user = fillNum; - rsForEach(gFontScript, gDummyAlloc, gDummyAlloc, &testData, sizeof(testData)); -} - static void bindProgramVertexOrtho() { // Default vertex shader rsgBindProgramVertex(gProgVertex); @@ -292,302 +79,31 @@ static void bindProgramVertexOrtho() { rsgProgramVertexLoadProjectionMatrix(&proj); } -static void displaySingletexFill(bool blend, int quadCount) { - bindProgramVertexOrtho(); - rs_matrix4x4 matrix; - rsMatrixLoadIdentity(&matrix); - rsgProgramVertexLoadModelMatrix(&matrix); - - // Fragment shader with texture - if (!blend) { - rsgBindProgramStore(gProgStoreBlendNone); - } else { - rsgBindProgramStore(gProgStoreBlendAlpha); - } - rsgBindProgramFragment(gProgFragmentTexture); - rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); - rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); - - for (int i = 0; i < quadCount; i ++) { - float startX = 5 * i, startY = 5 * i; - float width = gRenderSurfaceW - startX, height = gRenderSurfaceH - startY; - rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, - startX, startY + height, 0, 0, 1, - startX + width, startY + height, 0, 1, 1, - startX + width, startY, 0, 1, 0); - } -} - -static void displayMeshSamples(int meshNum) { - - bindProgramVertexOrtho(); - rs_matrix4x4 matrix; - rsMatrixLoadTranslate(&matrix, gRenderSurfaceW/2, gRenderSurfaceH/2, 0); - rsgProgramVertexLoadModelMatrix(&matrix); - - // Fragment shader with texture - rsgBindProgramStore(gProgStoreBlendNone); - rsgBindProgramFragment(gProgFragmentTexture); - rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); - - rsgBindTexture(gProgFragmentTexture, 0, gTexOpaque); - - if (meshNum == 0) { - rsgDrawMesh(g10by10Mesh); - } else if (meshNum == 1) { - rsgDrawMesh(g100by100Mesh); - } else if (meshNum == 2) { - rsgDrawMesh(gWbyHMesh); - } -} - -// Display sample images in a mesh with different texture -static void displayIcons(int meshMode) { - bindProgramVertexOrtho(); - - // Fragment shader with texture - rsgBindProgramStore(gProgStoreBlendAlpha); - rsgBindProgramFragment(gProgFragmentTexture); - rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); - rsgBindTexture(gProgFragmentTexture, 0, gTexTorus); - rsgDrawQuadTexCoords( - 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, - 0.0f, gRenderSurfaceH, 0.0f, 0.0f, 1.0f, - gRenderSurfaceW, gRenderSurfaceH, 0.0f, 1.0f, 1.0f, - gRenderSurfaceW, 0.0f, 0.0f, 1.0f, 0.0f); - - int meshCount = (int)pow(10.0f, (float)(meshMode + 1)); - - float wSize = gRenderSurfaceW/(float)meshCount; - float hSize = gRenderSurfaceH/(float)meshCount; - rs_matrix4x4 matrix; - rsMatrixLoadScale(&matrix, wSize, hSize, 1.0); - - float yPos = 0; - float yPad = hSize / 2; - float xPad = wSize / 2; - for (int y = 0; y < meshCount; y++) { - yPos = y * hSize + yPad; - float xPos = 0; - for (int x = 0; x < meshCount; x++) { - xPos = x * wSize + xPad; - rs_matrix4x4 transMatrix; - rsMatrixLoadTranslate(&transMatrix, xPos, yPos, 0); - rsMatrixMultiply(&transMatrix, &matrix); - rsgProgramVertexLoadModelMatrix(&transMatrix); - int i = (x + y * meshCount) % 100; - rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item); - rsgDrawMesh(gSingleMesh); - } - } -} - -// Draw meshes in a single page with top left corner coordinates (xStart, yStart) -static void drawMeshInPage(float xStart, float yStart, int wResolution, int hResolution) { - // Draw wResolution * hResolution meshes in one page - float wMargin = 100.0f; - float hMargin = 100.0f; - float xPad = 50.0f; - float yPad = 20.0f; - float size = 100.0f; // size of images - - // font info - rs_font font = gFontSans; - rsgBindFont(font); - rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); - - // Measure text size - int left = 0, right = 0, top = 0, bottom = 0; - rsgMeasureText(gSampleTextList100[0].item, &left, &right, &top, &bottom); - float textHeight = (float)(top - bottom); - float textWidth = (float)(right - left); - - rs_matrix4x4 matrix; - rsMatrixLoadScale(&matrix, size, size, 1.0); - - for (int y = 0; y < hResolution; y++) { - float yPos = yStart + hMargin + y * size + y * yPad; - for (int x = 0; x < wResolution; x++) { - float xPos = xStart + wMargin + x * size + x * xPad; - - rs_matrix4x4 transMatrix; - rsMatrixLoadTranslate(&transMatrix, xPos + size/2, yPos + size/2, 0); - rsMatrixMultiply(&transMatrix, &matrix); // scale the mesh - rsgProgramVertexLoadModelMatrix(&transMatrix); - - int i = (y * wResolution + x) % 100; - rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item); - rsgDrawMesh(gSingleMesh); - rsgDrawText(gSampleTextList100[i].item, xPos, yPos + size + yPad/2 + textHeight); - } - } -} - -// Display both images and text as shown in launcher and homepage -// meshMode will decide how many pages we draw -// meshMode = 0: draw 3 pages of meshes -// meshMode = 1: draw 5 pages of meshes -static void displayImageWithText(int wResolution, int hResolution, int meshMode) { - bindProgramVertexOrtho(); - - // Fragment shader with texture - rsgBindProgramStore(gProgStoreBlendAlpha); - rsgBindProgramFragment(gProgFragmentTexture); - rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); - - drawMeshInPage(0, 0, wResolution, hResolution); - drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution); - drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution); - if (meshMode == 1) { - // draw another two pages of meshes - drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution); - drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution); - } -} - -// Display a list of text as the list view -static void displayListView() { - // set text color - rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f); - rsgBindFont(gFontSans); - - // get the size of the list - rs_allocation textAlloc; - textAlloc = rsGetAllocation(gListViewText); - int allocSize = rsAllocationGetDimX(textAlloc); - - int listItemHeight = 80; - int yOffset = listItemHeight; - - // set the color for the list divider - rsgBindProgramFragment(gProgFragmentColor); - rsgProgramFragmentConstantColor(gProgFragmentColor, 1.0, 1.0, 1.0, 1); - - // draw the list with divider - for (int i = 0; i < allocSize; i++) { - if (yOffset - listItemHeight > gRenderSurfaceH) { - break; - } - rsgDrawRect(0, yOffset - 1, gRenderSurfaceW, yOffset, 0); - rsgDrawText(gListViewText[i].item, 20, yOffset - 10); - yOffset += listItemHeight; - } -} - -static void drawGalaxy() { - rsgClearColor(0.f, 0.f, 0.f, 1.f); - gParticlesBuffer = rsGetAllocation(Particles); - rsgBindProgramFragment(gPFBackground); - - gWidth = rsgGetWidth(); - gHeight = rsgGetHeight(); - if ((gWidth != gOldWidth) || (gHeight != gOldHeight)) { - initParticles(); - gOldWidth = gWidth; - gOldHeight = gHeight; - } - - float offset = mix(-1.0f, 1.0f, gXOffset); - drawSpace(); - drawParticles(offset); - drawLights(); -} - -// Display images and text with live wallpaper in the background -static void displayLiveWallPaper(int wResolution, int hResolution) { - bindProgramVertexOrtho(); - - drawGalaxy(); - - rsgBindProgramVertex(gProgVertex); - rsgBindProgramStore(gProgStoreBlendAlpha); - rsgBindProgramFragment(gProgFragmentTexture); - rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); - - drawMeshInPage(0, 0, wResolution, hResolution); - drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution); - drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution); - drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution); - drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution); -} - -// Quick hack to get some geometry numbers -static void displaySimpleGeoSamples(bool useTexture, int numMeshes) { +static void runSubTest(int index) { TestData testData; - testData.renderSurfaceW = gRenderSurfaceW; - testData.renderSurfaceH = gRenderSurfaceH; - testData.dt = gDt; - testData.user = 0; - testData.user1 = useTexture ? 1 : 0; - testData.user2 = numMeshes; - rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData, sizeof(testData)); -} + fillSurfaceParams(&testData); -static void displayCustomShaderSamples(int numMeshes) { - TestData testData; - testData.renderSurfaceW = gRenderSurfaceW; - testData.renderSurfaceH = gRenderSurfaceH; - testData.dt = gDt; - testData.user = 1; - testData.user1 = numMeshes; - rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData, sizeof(testData)); + rs_allocation null_alloc; + rsForEach(gTestScripts[index].testScript, + gTestScripts[index].testData, + null_alloc, + &testData, + sizeof(testData)); } -static void displayPixelLightSamples(int numMeshes, bool heavyVertex) { - TestData testData; - testData.renderSurfaceW = gRenderSurfaceW; - testData.renderSurfaceH = gRenderSurfaceH; - testData.dt = gDt; - testData.user = 2; - testData.user1 = numMeshes; - testData.user2 = heavyVertex ? 1 : 0; - rsForEach(gTorusScript, gDummyAlloc, gDummyAlloc, &testData, sizeof(testData)); -} - -static void displayMultitextureSample(bool blend, int quadCount) { - bindProgramVertexOrtho(); - rs_matrix4x4 matrix; - rsMatrixLoadIdentity(&matrix); - rsgProgramVertexLoadModelMatrix(&matrix); - - // Fragment shader with texture - if (!blend) { - rsgBindProgramStore(gProgStoreBlendNone); - } else { - rsgBindProgramStore(gProgStoreBlendAlpha); - } - rsgBindProgramFragment(gProgFragmentMultitex); - rsgBindSampler(gProgFragmentMultitex, 0, gLinearClamp); - rsgBindSampler(gProgFragmentMultitex, 1, gLinearWrap); - rsgBindSampler(gProgFragmentMultitex, 2, gLinearClamp); - rsgBindTexture(gProgFragmentMultitex, 0, gTexChecker); - rsgBindTexture(gProgFragmentMultitex, 1, gTexTorus); - rsgBindTexture(gProgFragmentMultitex, 2, gTexTransparent); - - for (int i = 0; i < quadCount; i ++) { - float startX = 10 * i, startY = 10 * i; - float width = gRenderSurfaceW - startX, height = gRenderSurfaceH - startY; - rsgDrawQuadTexCoords(startX, startY, 0, 0, 0, - startX, startY + height, 0, 0, 1, - startX + width, startY + height, 0, 1, 1, - startX + width, startY, 0, 1, 0); - } -} static bool checkInit() { - static int countdown = 5; + static int countdown = 3; // Perform all the uploads so we only measure rendered time if(countdown > 1) { - displayFontSamples(5); - displaySingletexFill(true, 3); - displayMeshSamples(0); - displayMeshSamples(1); - displayMeshSamples(2); - displayMultitextureSample(true, 5); - displayPixelLightSamples(1, false); - displayPixelLightSamples(1, true); + int testCount = rsAllocationGetDimX(rsGetAllocation(gTestScripts)); + for(int i = 0; i < testCount; i ++) { + rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f); + runSubTest(i); + rsgFinish(); + } countdown --; rsgClearColor(0.2f, 0.2f, 0.2f, 0.0f); @@ -606,43 +122,11 @@ static bool checkInit() { } static int benchMode = 0; +static bool benchmarkSingleTest = false; +static int benchSubMode = 0; static int runningLoops = 0; static bool sendMsgFlag = false; -static const char *testNames[] = { - "Fill screen with text 1 time", - "Fill screen with text 3 times", - "Fill screen with text 5 times", - "Geo test 25.6k flat color", - "Geo test 51.2k flat color", - "Geo test 204.8k small tries flat color", - "Geo test 25.6k single texture", - "Geo test 51.2k single texture", - "Geo test 204.8k small tries single texture", - "Full screen mesh 10 by 10", - "Full screen mesh 100 by 100", - "Full screen mesh W / 4 by H / 4", - "Geo test 25.6k geo heavy vertex", - "Geo test 51.2k geo heavy vertex", - "Geo test 204.8k geo raster load heavy vertex", - "Fill screen 10x singletexture", - "Fill screen 10x 3tex multitexture", - "Fill screen 10x blended singletexture", - "Fill screen 10x blended 3tex multitexture", - "Geo test 25.6k heavy fragment", - "Geo test 51.2k heavy fragment", - "Geo test 204.8k small tries heavy fragment", - "Geo test 25.6k heavy fragment heavy vertex", - "Geo test 51.2k heavy fragment heavy vertex", - "Geo test 204.8k small tries heavy fragment heavy vertex", - "UI test with icon display 10 by 10", - "UI test with icon display 100 by 100", - "UI test with image and text display 3 pages", - "UI test with image and text display 5 pages", - "UI test with list view", - "UI test with live wallpaper", -}; - static bool gIsDebugMode = false; void setDebugMode(int testNumber) { gIsDebugMode = true; @@ -650,122 +134,17 @@ void setDebugMode(int testNumber) { rsgClearAllRenderTargets(); } -void setBenchmarkMode() { +void setBenchmarkMode(int testNumber) { gIsDebugMode = false; - benchMode = 0; - runningLoops = 0; -} - - -void getTestName(int testIndex) { - int bufferLen = rsAllocationGetDimX(rsGetAllocation(gStringBuffer)); - if (testIndex >= gMaxModes) { - return; - } - uint charIndex = 0; - while (testNames[testIndex][charIndex] != '\0' && charIndex < bufferLen) { - gStringBuffer[charIndex] = testNames[testIndex][charIndex]; - charIndex ++; + if (testNumber == -1) { + benchmarkSingleTest = false; + benchMode = 0; + } else { + benchmarkSingleTest = true; + benchMode = testNumber; } - gStringBuffer[charIndex] = '\0'; -} -static void runTest(int index) { - switch (index) { - case 0: - displayFontSamples(1); - break; - case 1: - displayFontSamples(3); - break; - case 2: - displayFontSamples(5); - break; - case 3: - displaySimpleGeoSamples(false, 1); - break; - case 4: - displaySimpleGeoSamples(false, 2); - break; - case 5: - displaySimpleGeoSamples(false, 8); - break; - case 6: - displaySimpleGeoSamples(true, 1); - break; - case 7: - displaySimpleGeoSamples(true, 2); - break; - case 8: - displaySimpleGeoSamples(true, 8); - break; - case 9: - displayMeshSamples(0); - break; - case 10: - displayMeshSamples(1); - break; - case 11: - displayMeshSamples(2); - break; - case 12: - displayCustomShaderSamples(1); - break; - case 13: - displayCustomShaderSamples(2); - break; - case 14: - displayCustomShaderSamples(10); - break; - case 15: - displaySingletexFill(false, 10); - break; - case 16: - displayMultitextureSample(false, 10); - break; - case 17: - displaySingletexFill(true, 10); - break; - case 18: - displayMultitextureSample(true, 10); - break; - case 19: - displayPixelLightSamples(1, false); - break; - case 20: - displayPixelLightSamples(2, false); - break; - case 21: - displayPixelLightSamples(8, false); - break; - case 22: - displayPixelLightSamples(1, true); - break; - case 23: - displayPixelLightSamples(2, true); - break; - case 24: - displayPixelLightSamples(8, true); - break; - case 25: - displayIcons(0); - break; - case 26: - displayIcons(1); - break; - case 27: - displayImageWithText(7, 5, 0); - break; - case 28: - displayImageWithText(7, 5, 1); - break; - case 29: - displayListView(); - break; - case 30: - displayLiveWallPaper(7, 5); - break; - } + runningLoops = 0; } static void drawOffscreenResult(int posX, int posY, int width, int height) { @@ -803,7 +182,7 @@ static void benchmark() { rsgClearColor(0.1f, 0.1f, 0.1f, 1.0f); rsgClearDepth(1.0f); - runTest(benchMode); + runSubTest(benchMode); rsgClearAllRenderTargets(); gRenderSurfaceW = rsgGetWidth(); gRenderSurfaceH = rsgGetHeight(); @@ -816,25 +195,33 @@ static void benchmark() { int64_t end = rsUptimeMillis(); float fps = (float)(frameCount) / ((float)(end - start)*0.001f); - rsDebug(testNames[benchMode], fps); + rsDebug("Finishes test ", fps); + gResultBuffer[benchMode] = fps; - drawOffscreenResult(0, 0, - gRenderSurfaceW / 2, - gRenderSurfaceH / 2); - const char* text = testNames[benchMode]; + int bufferW = rsAllocationGetDimX(gRenderBufferColor); + int bufferH = rsAllocationGetDimY(gRenderBufferColor); + + int quadW = gRenderSurfaceW / 2; + int quadH = (quadW * bufferH) / bufferW; + drawOffscreenResult(0, 0, quadW, quadH); + int left = 0, right = 0, top = 0, bottom = 0; uint width = rsgGetWidth(); uint height = rsgGetHeight(); rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f); rsgBindFont(gFontSerif); - rsgMeasureText(text, &left, &right, &top, &bottom); + rsgMeasureText(gTestScripts[benchMode].testName, &left, &right, &top, &bottom); rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); - rsgDrawText(text, 2 -left, height - 2 + bottom); + rsgDrawText(gTestScripts[benchMode].testName, 2 -left, height - 2 + bottom); - benchMode ++; + if (benchmarkSingleTest) { + return; + } - if (benchMode == gMaxModes) { - rsSendToClientBlocking(RS_MSG_RESULTS_READY, gResultBuffer, gMaxModes*sizeof(float)); + benchMode ++; + int testCount = rsAllocationGetDimX(rsGetAllocation(gTestScripts)); + if (benchMode == testCount) { + rsSendToClientBlocking(RS_MSG_RESULTS_READY, gResultBuffer, testCount*sizeof(float)); benchMode = 0; runningLoops++; if ((gMaxLoops > 0) && (runningLoops > gMaxLoops) && !sendMsgFlag) { @@ -844,14 +231,11 @@ static void benchmark() { sendMsgFlag = true; } } - } static void debug() { gDt = rsGetDt(); - - rsgFinish(); - runTest(benchMode); + runSubTest(benchMode); } int root(void) { @@ -859,6 +243,14 @@ int root(void) { gRenderSurfaceH = rsgGetHeight(); rsgClearColor(0.2f, 0.2f, 0.2f, 1.0f); rsgClearDepth(1.0f); + + if (!gLoadComplete) { + rsgFontColor(0.9f, 0.9f, 0.95f, 1.0f); + rsgBindFont(gFontSerif); + rsgDrawText("Loading", 50, 50); + return 0; + } + if(!checkInit()) { return 1; } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh index b635373b2afa..43658b15a2cf 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/subtest_def.rsh @@ -20,9 +20,5 @@ typedef struct TestData_s { int renderSurfaceW; int renderSurfaceH; float dt; - int user; - int user1; - int user2; - int user3; } TestData; diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs index 0df6b3590bd5..7f10019e9c29 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/text_test.rs @@ -22,6 +22,11 @@ rs_font gFontSans; rs_font gFontSerif; +typedef struct TextTestData_s { + int fillNum; +} TextTestData; +TextTestData *gData; + void init() { } @@ -78,5 +83,8 @@ void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32 TestData *testData = (TestData*)usrData; gRenderSurfaceW = testData->renderSurfaceW; gRenderSurfaceH = testData->renderSurfaceH; - displayFontSamples(testData->user); + + gData = (TextTestData*)v_in; + + displayFontSamples(gData->fillNum); } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs index 26d56807f6f2..853a05d1c874 100644 --- a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/torus_test.rs @@ -47,6 +47,13 @@ rs_program_vertex gProgVertexPixelLight; rs_program_vertex gProgVertexPixelLightMove; rs_program_fragment gProgFragmentPixelLight; +typedef struct TorusTestData_s { + int testId; + int user1; + int user2; +} TorusTestData; +TorusTestData *gData; + static float gDt = 0.0f; static int gRenderSurfaceW; @@ -269,15 +276,20 @@ void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32 gRenderSurfaceH = testData->renderSurfaceH; gDt = testData->dt; - switch(testData->user) { + gData = (TorusTestData*)v_in; + + switch(gData->testId) { case 0: - displaySimpleGeoSamples(testData->user1 == 1 ? true : false, testData->user2); + displaySimpleGeoSamples(gData->user1 == 1 ? true : false, gData->user2); break; case 1: - displayCustomShaderSamples(testData->user1); + displayCustomShaderSamples(gData->user1); break; case 2: - displayPixelLightSamples(testData->user1, testData->user2 == 1 ? true : false); + displayPixelLightSamples(gData->user1, gData->user2 == 1 ? true : false); + break; + default: + rsDebug("Wrong test number", gData->testId); break; } } diff --git a/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs new file mode 100644 index 000000000000..508909244edc --- /dev/null +++ b/tests/RenderScriptTests/PerfTest/src/com/android/perftest/ui_test.rs @@ -0,0 +1,444 @@ +// 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. + +#pragma version(1) + +#pragma rs java_package_name(com.android.perftest) + +#include "rs_graphics.rsh" +#include "shader_def.rsh" +#include "subtest_def.rsh" + +// Parameters for galaxy live wallpaper +rs_allocation gTSpace; +rs_allocation gTLight1; +rs_allocation gTFlares; +rs_mesh gParticlesMesh; + +rs_program_fragment gPFBackground; +rs_program_fragment gPFStars; +rs_program_vertex gPVStars; +rs_program_vertex gPVBkProj; +rs_program_store gPSLights; + +float gXOffset = 0.5f; + +#define ELLIPSE_RATIO 0.892f +#define PI 3.1415f +#define TWO_PI 6.283f +#define ELLIPSE_TWIST 0.023333333f + +static float angle = 50.f; +static int gOldWidth; +static int gOldHeight; +static int gWidth; +static int gHeight; +static float gSpeed[12000]; +static int gGalaxyRadius = 300; +static rs_allocation gParticlesBuffer; + +typedef struct __attribute__((packed, aligned(4))) Particle { + uchar4 color; + float3 position; +} Particle_t; +Particle_t *Particles; + +typedef struct VpConsts { + rs_matrix4x4 Proj; + rs_matrix4x4 MVP; +} VpConsts_t; +VpConsts_t *vpConstants; +// End of parameters for galaxy live wallpaper + +rs_program_vertex gProgVertex; +rs_program_fragment gProgFragmentColor; +rs_program_fragment gProgFragmentTexture; + +rs_program_store gProgStoreBlendAlpha; + +rs_allocation gTexOpaque; +rs_allocation gTexTorus; +rs_allocation gTexGlobe; + +typedef struct ListAllocs_s { + rs_allocation item; +} ListAllocs; + +ListAllocs *gTexList100; +ListAllocs *gSampleTextList100; +ListAllocs *gListViewText; + +rs_mesh gSingleMesh; + +rs_font gFontSans; + +rs_sampler gLinearClamp; + +typedef struct UiTestData_s { + int testId; + int user1; + int user2; + int user3; +} UiTestData; +UiTestData *gData; + +static float gDt = 0; + + +void init() { +} + +static int gRenderSurfaceW; +static int gRenderSurfaceH; + +static void bindProgramVertexOrtho() { + // Default vertex shader + rsgBindProgramVertex(gProgVertex); + // Setup the projection matrix + rs_matrix4x4 proj; + rsMatrixLoadOrtho(&proj, 0, gRenderSurfaceW, gRenderSurfaceH, 0, -500, 500); + rsgProgramVertexLoadProjectionMatrix(&proj); +} + +/** + * Methods to draw the galaxy live wall paper + */ +static float mapf(float minStart, float minStop, float maxStart, float maxStop, float value) { + return maxStart + (maxStart - maxStop) * ((value - minStart) / (minStop - minStart)); +} + +/** + * Helper function to generate the stars. + */ +static float randomGauss() { + float x1; + float x2; + float w = 2.f; + + while (w >= 1.0f) { + x1 = rsRand(2.0f) - 1.0f; + x2 = rsRand(2.0f) - 1.0f; + w = x1 * x1 + x2 * x2; + } + + w = sqrt(-2.0f * log(w) / w); + return x1 * w; +} + +/** + * Generates the properties for a given star. + */ +static void createParticle(Particle_t *part, int idx, float scale) { + float d = fabs(randomGauss()) * gGalaxyRadius * 0.5f + rsRand(64.0f); + float id = d / gGalaxyRadius; + float z = randomGauss() * 0.4f * (1.0f - id); + float p = -d * ELLIPSE_TWIST; + + if (d < gGalaxyRadius * 0.33f) { + part->color.x = (uchar) (220 + id * 35); + part->color.y = 220; + part->color.z = 220; + } else { + part->color.x = 180; + part->color.y = 180; + part->color.z = (uchar) clamp(140.f + id * 115.f, 140.f, 255.f); + } + // Stash point size * 10 in Alpha + part->color.w = (uchar) (rsRand(1.2f, 2.1f) * 60); + + if (d > gGalaxyRadius * 0.15f) { + z *= 0.6f * (1.0f - id); + } else { + z *= 0.72f; + } + + // Map to the projection coordinates (viewport.x = -1.0 -> 1.0) + d = mapf(-4.0f, gGalaxyRadius + 4.0f, 0.0f, scale, d); + + part->position.x = rsRand(TWO_PI); + part->position.y = d; + gSpeed[idx] = rsRand(0.0015f, 0.0025f) * (0.5f + (scale / d)) * 0.8f; + + part->position.z = z / 5.0f; +} + +/** + * Initialize all the starts, called from Java + */ +void initParticles() { + Particle_t *part = Particles; + float scale = gGalaxyRadius / (gWidth * 0.5f); + int count = rsAllocationGetDimX(gParticlesBuffer); + for (int i = 0; i < count; i ++) { + createParticle(part, i, scale); + part++; + } +} + +static void drawSpace() { + rsgBindProgramFragment(gPFBackground); + rsgBindTexture(gPFBackground, 0, gTSpace); + rsgDrawQuadTexCoords( + 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, + gWidth, 0.0f, 0.0f, 2.0f, 1.0f, + gWidth, gHeight, 0.0f, 2.0f, 0.0f, + 0.0f, gHeight, 0.0f, 0.0f, 0.0f); +} + +static void drawLights() { + rsgBindProgramVertex(gPVBkProj); + rsgBindProgramFragment(gPFBackground); + rsgBindTexture(gPFBackground, 0, gTLight1); + + float scale = 512.0f / gWidth; + float x = -scale - scale * 0.05f; + float y = -scale; + + scale *= 2.0f; + + rsgDrawQuad(x, y, 0.0f, + x + scale * 1.1f, y, 0.0f, + x + scale * 1.1f, y + scale, 0.0f, + x, y + scale, 0.0f); +} + +static void drawParticles(float offset) { + float a = offset * angle; + float absoluteAngle = fabs(a); + + rs_matrix4x4 matrix; + rsMatrixLoadTranslate(&matrix, 0.0f, 0.0f, 10.0f - 6.0f * absoluteAngle / 50.0f); + if (gHeight > gWidth) { + rsMatrixScale(&matrix, 6.6f, 6.0f, 1.0f); + } else { + rsMatrixScale(&matrix, 12.6f, 12.0f, 1.0f); + } + rsMatrixRotate(&matrix, absoluteAngle, 1.0f, 0.0f, 0.0f); + rsMatrixRotate(&matrix, a, 0.0f, 0.4f, 0.1f); + rsMatrixLoad(&vpConstants->MVP, &vpConstants->Proj); + rsMatrixMultiply(&vpConstants->MVP, &matrix); + rsgAllocationSyncAll(rsGetAllocation(vpConstants)); + + rsgBindProgramVertex(gPVStars); + rsgBindProgramFragment(gPFStars); + rsgBindProgramStore(gPSLights); + rsgBindTexture(gPFStars, 0, gTFlares); + + Particle_t *vtx = Particles; + int count = rsAllocationGetDimX(gParticlesBuffer); + for (int i = 0; i < count; i++) { + vtx->position.x = vtx->position.x + gSpeed[i]; + vtx++; + } + + rsgDrawMesh(gParticlesMesh); +} +/* end of methods for drawing galaxy */ + +// Display sample images in a mesh with different texture +static void displayIcons(int meshMode) { + bindProgramVertexOrtho(); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendAlpha); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + rsgBindTexture(gProgFragmentTexture, 0, gTexTorus); + rsgDrawQuadTexCoords( + 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, + 0.0f, gRenderSurfaceH, 0.0f, 0.0f, 1.0f, + gRenderSurfaceW, gRenderSurfaceH, 0.0f, 1.0f, 1.0f, + gRenderSurfaceW, 0.0f, 0.0f, 1.0f, 0.0f); + + int meshCount = (int)pow(10.0f, (float)(meshMode + 1)); + + float wSize = gRenderSurfaceW/(float)meshCount; + float hSize = gRenderSurfaceH/(float)meshCount; + rs_matrix4x4 matrix; + rsMatrixLoadScale(&matrix, wSize, hSize, 1.0); + + float yPos = 0; + float yPad = hSize / 2; + float xPad = wSize / 2; + for (int y = 0; y < meshCount; y++) { + yPos = y * hSize + yPad; + float xPos = 0; + for (int x = 0; x < meshCount; x++) { + xPos = x * wSize + xPad; + rs_matrix4x4 transMatrix; + rsMatrixLoadTranslate(&transMatrix, xPos, yPos, 0); + rsMatrixMultiply(&transMatrix, &matrix); + rsgProgramVertexLoadModelMatrix(&transMatrix); + int i = (x + y * meshCount) % 100; + rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item); + rsgDrawMesh(gSingleMesh); + } + } +} + +// Draw meshes in a single page with top left corner coordinates (xStart, yStart) +static void drawMeshInPage(float xStart, float yStart, int wResolution, int hResolution) { + // Draw wResolution * hResolution meshes in one page + float wMargin = 100.0f; + float hMargin = 100.0f; + float xPad = 50.0f; + float yPad = 20.0f; + float size = 100.0f; // size of images + + // font info + rs_font font = gFontSans; + rsgBindFont(font); + rsgFontColor(1.0f, 1.0f, 1.0f, 1.0f); + + // Measure text size + int left = 0, right = 0, top = 0, bottom = 0; + rsgMeasureText(gSampleTextList100[0].item, &left, &right, &top, &bottom); + float textHeight = (float)(top - bottom); + float textWidth = (float)(right - left); + + rs_matrix4x4 matrix; + rsMatrixLoadScale(&matrix, size, size, 1.0); + + for (int y = 0; y < hResolution; y++) { + float yPos = yStart + hMargin + y * size + y * yPad; + for (int x = 0; x < wResolution; x++) { + float xPos = xStart + wMargin + x * size + x * xPad; + + rs_matrix4x4 transMatrix; + rsMatrixLoadTranslate(&transMatrix, xPos + size/2, yPos + size/2, 0); + rsMatrixMultiply(&transMatrix, &matrix); // scale the mesh + rsgProgramVertexLoadModelMatrix(&transMatrix); + + int i = (y * wResolution + x) % 100; + rsgBindTexture(gProgFragmentTexture, 0, gTexList100[i].item); + rsgDrawMesh(gSingleMesh); + rsgDrawText(gSampleTextList100[i].item, xPos, yPos + size + yPad/2 + textHeight); + } + } +} + +// Display both images and text as shown in launcher and homepage +// meshMode will decide how many pages we draw +// meshMode = 0: draw 3 pages of meshes +// meshMode = 1: draw 5 pages of meshes +static void displayImageWithText(int wResolution, int hResolution, int meshMode) { + bindProgramVertexOrtho(); + + // Fragment shader with texture + rsgBindProgramStore(gProgStoreBlendAlpha); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + + drawMeshInPage(0, 0, wResolution, hResolution); + drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution); + if (meshMode == 1) { + // draw another two pages of meshes + drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution); + } +} + +// Display a list of text as the list view +static void displayListView() { + // set text color + rsgFontColor(0.9f, 0.9f, 0.9f, 1.0f); + rsgBindFont(gFontSans); + + // get the size of the list + rs_allocation textAlloc; + textAlloc = rsGetAllocation(gListViewText); + int allocSize = rsAllocationGetDimX(textAlloc); + + int listItemHeight = 80; + int yOffset = listItemHeight; + + // set the color for the list divider + rsgBindProgramFragment(gProgFragmentColor); + rsgProgramFragmentConstantColor(gProgFragmentColor, 1.0, 1.0, 1.0, 1); + + // draw the list with divider + for (int i = 0; i < allocSize; i++) { + if (yOffset - listItemHeight > gRenderSurfaceH) { + break; + } + rsgDrawRect(0, yOffset - 1, gRenderSurfaceW, yOffset, 0); + rsgDrawText(gListViewText[i].item, 20, yOffset - 10); + yOffset += listItemHeight; + } +} + +static void drawGalaxy() { + rsgClearColor(0.f, 0.f, 0.f, 1.f); + gParticlesBuffer = rsGetAllocation(Particles); + rsgBindProgramFragment(gPFBackground); + + gWidth = rsgGetWidth(); + gHeight = rsgGetHeight(); + if ((gWidth != gOldWidth) || (gHeight != gOldHeight)) { + initParticles(); + gOldWidth = gWidth; + gOldHeight = gHeight; + } + + float offset = mix(-1.0f, 1.0f, gXOffset); + drawSpace(); + drawParticles(offset); + drawLights(); +} + +// Display images and text with live wallpaper in the background +static void displayLiveWallPaper(int wResolution, int hResolution) { + bindProgramVertexOrtho(); + + drawGalaxy(); + + rsgBindProgramVertex(gProgVertex); + rsgBindProgramStore(gProgStoreBlendAlpha); + rsgBindProgramFragment(gProgFragmentTexture); + rsgBindSampler(gProgFragmentTexture, 0, gLinearClamp); + + drawMeshInPage(0, 0, wResolution, hResolution); + drawMeshInPage(-1.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(1.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(-2.0f*gRenderSurfaceW, 0, wResolution, hResolution); + drawMeshInPage(2.0f*gRenderSurfaceW, 0, wResolution, hResolution); +} + +void root(const void *v_in, void *v_out, const void *usrData, uint32_t x, uint32_t y) { + TestData *testData = (TestData*)usrData; + gRenderSurfaceW = testData->renderSurfaceW; + gRenderSurfaceH = testData->renderSurfaceH; + gDt = testData->dt; + + gData = (UiTestData*)v_in; + + switch(gData->testId) { + case 0: + displayIcons(gData->user1); + break; + case 1: + displayImageWithText(gData->user1, gData->user2, gData->user3); + break; + case 2: + displayListView(); + break; + case 3: + displayLiveWallPaper(gData->user1, gData->user2); + break; + default: + rsDebug("Wrong test number", 0); + break; + } +} diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java index 5aba7643a027..c2bbdf5d0d18 100644 --- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayCoercionTest.java @@ -411,9 +411,9 @@ public class JavaBridgeArrayCoercionTest extends JavaBridgeTestBase { executeJavaScript("testObject.setByteArray([\"+042.10\"]);"); assertEquals(0, mTestObject.waitForByteArray()[0]); - // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. + // LIVECONNECT_COMPLIANCE: Should decode and convert to numeric char value. executeJavaScript("testObject.setCharArray([\"+042.10\"]);"); - assertEquals('+', mTestObject.waitForCharArray()[0]); + assertEquals(0, mTestObject.waitForCharArray()[0]); // LIVECONNECT_COMPLIANCE: Should use valueOf() of appropriate type. executeJavaScript("testObject.setShortArray([\"+042.10\"]);"); diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java index 51dd80daeca4..2fd42a74d3ca 100644 --- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeArrayTest.java @@ -39,6 +39,9 @@ public class JavaBridgeArrayTest extends JavaBridgeTestBase { private String mStringValue; private int[] mIntArray; + private int[][] mIntIntArray; + + private boolean mWasArrayMethodCalled; public synchronized void setBooleanValue(boolean x) { mBooleanValue = x; @@ -70,17 +73,27 @@ public class JavaBridgeArrayTest extends JavaBridgeTestBase { mIntArray = x; notifyResultIsReady(); } + public synchronized void setIntIntArray(int[][] x) { + mIntIntArray = x; + notifyResultIsReady(); + } public synchronized int[] waitForIntArray() { waitForResult(); return mIntArray; } + public synchronized int[][] waitForIntIntArray() { + waitForResult(); + return mIntIntArray; + } - public int[] getIntArray() { + public synchronized int[] arrayMethod() { + mWasArrayMethodCalled = true; return new int[] {42, 43, 44}; } - public int[] getEmptyIntArray() { - return new int[] {}; + + public synchronized boolean wasArrayMethodCalled() { + return mWasArrayMethodCalled; } } @@ -148,6 +161,32 @@ public class JavaBridgeArrayTest extends JavaBridgeTestBase { assertEquals(0, result[2]); } + public void testNonNumericLengthProperty() throws Throwable { + // LIVECONNECT_COMPLIANCE: This should not count as an array, so we + // should raise a JavaScript exception. + executeJavaScript("testObject.setIntArray({length: \"foo\"});"); + assertNull(mTestObject.waitForIntArray()); + } + + public void testLengthOutOfBounds() throws Throwable { + // LIVECONNECT_COMPLIANCE: This should not count as an array, so we + // should raise a JavaScript exception. + executeJavaScript("testObject.setIntArray({length: -1});"); + assertNull(mTestObject.waitForIntArray()); + + // LIVECONNECT_COMPLIANCE: This should not count as an array, so we + // should raise a JavaScript exception. + long length = (long)Integer.MAX_VALUE + 1L; + executeJavaScript("testObject.setIntArray({length: " + length + "});"); + assertNull(mTestObject.waitForIntArray()); + + // LIVECONNECT_COMPLIANCE: This should not count as an array, so we + // should raise a JavaScript exception. + length = (long)Integer.MAX_VALUE + 1L - (long)Integer.MIN_VALUE + 1L; + executeJavaScript("testObject.setIntArray({length: " + length + "});"); + assertNull(mTestObject.waitForIntArray()); + } + public void testSparseArray() throws Throwable { executeJavaScript("var x = [42, 43]; x[3] = 45; testObject.setIntArray(x);"); int[] result = mTestObject.waitForIntArray(); @@ -160,18 +199,28 @@ public class JavaBridgeArrayTest extends JavaBridgeTestBase { // Note that this requires being able to pass a boolean from JavaScript to // Java. - public void testReturnArray() throws Throwable { - // LIVECONNECT_COMPLIANCE: Convert to JavaScript array. - executeJavaScript("testObject.setBooleanValue(undefined === testObject.getIntArray())"); + public void testMethodReturningArrayNotCalled() throws Throwable { + // We don't invoke methods which return arrays, but note that no + // exception is raised. + // LIVECONNECT_COMPLIANCE: Should call method and convert result to + // JavaScript array. + executeJavaScript("testObject.setBooleanValue(undefined === testObject.arrayMethod())"); assertTrue(mTestObject.waitForBooleanValue()); + assertFalse(mTestObject.wasArrayMethodCalled()); } - // Note that this requires being able to pass a boolean from JavaScript to - // Java. - public void testReturnEmptyArray() throws Throwable { - // LIVECONNECT_COMPLIANCE: Convert to JavaScript array. - executeJavaScript( - "testObject.setBooleanValue(undefined === testObject.getEmptyIntArray())"); - assertTrue(mTestObject.waitForBooleanValue()); + public void testMultiDimensionalArrayMethod() throws Throwable { + // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays. + executeJavaScript("testObject.setIntIntArray([ [42, 43], [44, 45] ]);"); + assertNull(mTestObject.waitForIntIntArray()); + } + + public void testPassMultiDimensionalArray() throws Throwable { + // LIVECONNECT_COMPLIANCE: Should handle multi-dimensional arrays. + executeJavaScript("testObject.setIntArray([ [42, 43], [44, 45] ]);"); + int[] result = mTestObject.waitForIntArray(); + assertEquals(2, result.length); + assertEquals(0, result[0]); + assertEquals(0, result[1]); } } diff --git a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java index 54865e9ab7d5..c9bbb77ecf14 100644 --- a/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java +++ b/tests/WebViewTests/src/com/android/webviewtests/JavaBridgeBasicsTest.java @@ -75,6 +75,12 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase { } } + private static class ObjectWithStaticMethod { + public static String staticMethod() { + return "foo"; + } + } + TestController mTestController; @Override @@ -101,6 +107,17 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase { mWebViewClient.waitForOnPageFinished(); } + // Note that this requires that we can pass a JavaScript boolean to Java. + private void assertRaisesException(String script) throws Throwable { + executeJavaScript("try {" + + script + ";" + + " testController.setBooleanValue(false);" + + "} catch (exception) {" + + " testController.setBooleanValue(true);" + + "}"); + assertTrue(mTestController.waitForBooleanValue()); + } + public void testTypeOfInjectedObject() throws Throwable { assertEquals("object", executeJavaScriptAndGetStringResult("typeof testController")); } @@ -161,6 +178,28 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase { executeJavaScriptAndGetStringResult("typeof testController.setStringValue")); } + public void testTypeOfInvalidMethod() throws Throwable { + assertEquals("undefined", executeJavaScriptAndGetStringResult("typeof testController.foo")); + } + + public void testCallingInvalidMethodRaisesException() throws Throwable { + assertRaisesException("testController.foo()"); + } + + // Note that this requires that we can pass a JavaScript string to Java. + public void testTypeOfStaticMethod() throws Throwable { + injectObjectAndReload(new ObjectWithStaticMethod(), "testObject"); + executeJavaScript("testController.setStringValue(typeof testObject.staticMethod)"); + assertEquals("function", mTestController.waitForStringValue()); + } + + // Note that this requires that we can pass a JavaScript string to Java. + public void testCallStaticMethod() throws Throwable { + injectObjectAndReload(new ObjectWithStaticMethod(), "testObject"); + executeJavaScript("testController.setStringValue(testObject.staticMethod())"); + assertEquals("foo", mTestController.waitForStringValue()); + } + public void testPrivateMethodNotExposed() throws Throwable { injectObjectAndReload(new Object() { private void method() {} @@ -214,20 +253,8 @@ public class JavaBridgeBasicsTest extends JavaBridgeTestBase { } public void testCallMethodWithWrongNumberOfArgumentsRaisesException() throws Throwable { - class Test { - public void run(String script) throws Throwable { - executeJavaScript("try {" + - script + ";" + - " testController.setBooleanValue(false);" + - "} catch (exception) {" + - " testController.setBooleanValue(true);" + - "}"); - assertTrue(mTestController.waitForBooleanValue()); - } - } - Test test = new Test(); - test.run("testController.setIntValue()"); - test.run("testController.setIntValue(42, 42)"); + assertRaisesException("testController.setIntValue()"); + assertRaisesException("testController.setIntValue(42, 42)"); } public void testObjectPersistsAcrossPageLoads() throws Throwable { 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()); } } diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index aadcaadaa663..3ed9bd5ccbbf 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -123,6 +123,8 @@ public class WifiStateMachine extends StateMachine { private final LruCache<String, ScanResult> mScanResultCache; private String mInterfaceName; + /* Tethering interface could be seperate from wlan interface */ + private String mTetherInterfaceName; private int mLastSignalLevel = -1; private String mLastBssid; @@ -156,6 +158,14 @@ public class WifiStateMachine extends StateMachine { /* Tracks sequence number on stop failure message */ private int mSupplicantStopFailureToken = 0; + /** + * Tether state change notification time out + */ + private static final int TETHER_NOTIFICATION_TIME_OUT_MSECS = 5000; + + /* Tracks sequence number on a tether notification time out */ + private int mTetherToken = 0; + private LinkProperties mLinkProperties; // Wakelock held during wifi start/stop and driver load/unload @@ -240,10 +250,12 @@ public class WifiStateMachine extends StateMachine { static final int CMD_REQUEST_AP_CONFIG = BASE + 27; /* Response to access point configuration request */ static final int CMD_RESPONSE_AP_CONFIG = BASE + 28; - /* Set configuration on tether interface */ - static final int CMD_TETHER_INTERFACE = BASE + 29; + /* Invoked when getting a tether state change notification */ + static final int CMD_TETHER_STATE_CHANGE = BASE + 29; + /* A delayed message sent to indicate tether state change failed to arrive */ + static final int CMD_TETHER_NOTIFICATION_TIMED_OUT = BASE + 30; - static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 30; + static final int CMD_BLUETOOTH_ADAPTER_STATE_CHANGE = BASE + 31; /* Supplicant commands */ /* Is supplicant alive ? */ @@ -455,12 +467,25 @@ public class WifiStateMachine extends StateMachine { private State mSoftApStartingState = new SoftApStartingState(); /* Soft ap is running */ private State mSoftApStartedState = new SoftApStartedState(); + /* Soft ap is running and we are waiting for tether notification */ + private State mTetheringState = new TetheringState(); /* Soft ap is running and we are tethered through connectivity service */ private State mTetheredState = new TetheredState(); + /* Waiting for untether confirmation to stop soft Ap */ + private State mSoftApStoppingState = new SoftApStoppingState(); /* Wait till p2p is disabled */ private State mWaitForP2pDisableState = new WaitForP2pDisableState(); + private class TetherStateChange { + ArrayList<String> available; + ArrayList<String> active; + TetherStateChange(ArrayList<String> av, ArrayList<String> ac) { + available = av; + active = ac; + } + } + /** * One of {@link WifiManager#WIFI_STATE_DISABLED}, @@ -562,7 +587,9 @@ public class WifiStateMachine extends StateMachine { public void onReceive(Context context, Intent intent) { ArrayList<String> available = intent.getStringArrayListExtra( ConnectivityManager.EXTRA_AVAILABLE_TETHER); - sendMessage(CMD_TETHER_INTERFACE, available); + ArrayList<String> active = intent.getStringArrayListExtra( + ConnectivityManager.EXTRA_ACTIVE_TETHER); + sendMessage(CMD_TETHER_STATE_CHANGE, new TetherStateChange(available, active)); } },new IntentFilter(ConnectivityManager.ACTION_TETHER_STATE_CHANGED)); @@ -603,7 +630,9 @@ public class WifiStateMachine extends StateMachine { addState(mSupplicantStoppingState, mDefaultState); addState(mSoftApStartingState, mDefaultState); addState(mSoftApStartedState, mDefaultState); + addState(mTetheringState, mSoftApStartedState); addState(mTetheredState, mSoftApStartedState); + addState(mSoftApStoppingState, mDefaultState); addState(mWaitForP2pDisableState, mDefaultState); setInitialState(mInitialState); @@ -1139,6 +1168,7 @@ public class WifiStateMachine extends StateMachine { loge("Error tethering on " + intf); return false; } + mTetherInterfaceName = intf; return true; } } @@ -1165,11 +1195,27 @@ public class WifiStateMachine extends StateMachine { loge("Error resetting interface " + mInterfaceName + ", :" + e); } - if (mCm.untether(mInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { + if (mCm.untether(mTetherInterfaceName) != ConnectivityManager.TETHER_ERROR_NO_ERROR) { loge("Untether initiate failed!"); } } + private boolean isWifiTethered(ArrayList<String> active) { + + checkAndSetConnectivityInstance(); + + String[] wifiRegexs = mCm.getTetherableWifiRegexs(); + for (String intf : active) { + for (String regex : wifiRegexs) { + if (intf.matches(regex)) { + return true; + } + } + } + // We found no interfaces that are tethered + return false; + } + /** * Set the country code from the system setting value, if any. */ @@ -1800,7 +1846,8 @@ public class WifiStateMachine extends StateMachine { case CMD_START_AP_SUCCESS: case CMD_START_AP_FAILURE: case CMD_STOP_AP: - case CMD_TETHER_INTERFACE: + case CMD_TETHER_STATE_CHANGE: + case CMD_TETHER_NOTIFICATION_TIMED_OUT: case CMD_START_SCAN: case CMD_DISCONNECT: case CMD_RECONNECT: @@ -3284,7 +3331,7 @@ public class WifiStateMachine extends StateMachine { case CMD_SET_FREQUENCY_BAND: case CMD_START_PACKET_FILTERING: case CMD_STOP_PACKET_FILTERING: - case CMD_TETHER_INTERFACE: + case CMD_TETHER_STATE_CHANGE: case WifiP2pService.P2P_ENABLE_PENDING: deferMessage(message); break; @@ -3326,7 +3373,8 @@ public class WifiStateMachine extends StateMachine { case CMD_STOP_AP: if (DBG) log("Stopping Soft AP"); setWifiApState(WIFI_AP_STATE_DISABLING); - stopTethering(); + + /* We have not tethered at this point, so we just shutdown soft Ap */ try { mNwService.stopAccessPoint(mInterfaceName); } catch(Exception e) { @@ -3342,10 +3390,10 @@ public class WifiStateMachine extends StateMachine { loge("Cannot start supplicant with a running soft AP"); setWifiState(WIFI_STATE_UNKNOWN); break; - case CMD_TETHER_INTERFACE: - ArrayList<String> available = (ArrayList<String>) message.obj; - if (startTethering(available)) { - transitionTo(mTetheredState); + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + if (startTethering(stateChange.available)) { + transitionTo(mTetheringState); } break; case WifiP2pService.P2P_ENABLE_PENDING: @@ -3405,6 +3453,58 @@ public class WifiStateMachine extends StateMachine { } } + class TetheringState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + /* Send ourselves a delayed message to shut down if tethering fails to notify */ + sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT, + ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS); + } + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch(message.what) { + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + if (isWifiTethered(stateChange.active)) { + transitionTo(mTetheredState); + } + return HANDLED; + case CMD_TETHER_NOTIFICATION_TIMED_OUT: + if (message.arg1 == mTetherToken) { + loge("Failed to get tether update, shutdown soft access point"); + setWifiApEnabled(null, false); + } + break; + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + case CMD_START_SUPPLICANT: + case CMD_STOP_SUPPLICANT: + case CMD_START_AP: + case CMD_STOP_AP: + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_HIGH_PERF_MODE: + case CMD_SET_COUNTRY_CODE: + case CMD_SET_FREQUENCY_BAND: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + case WifiP2pService.P2P_ENABLE_PENDING: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + class TetheredState extends State { @Override public void enter() { @@ -3415,13 +3515,89 @@ public class WifiStateMachine extends StateMachine { public boolean processMessage(Message message) { if (DBG) log(getName() + message.toString() + "\n"); switch(message.what) { - case CMD_TETHER_INTERFACE: - // Ignore any duplicate interface available notifications - // when in tethered state + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + if (!isWifiTethered(stateChange.active)) { + loge("Tethering reports wifi as untethered!, shut down soft Ap"); + setWifiApEnabled(null, false); + } return HANDLED; + case CMD_STOP_AP: + if (DBG) log("Untethering before stopping AP"); + setWifiApState(WIFI_AP_STATE_DISABLING); + stopTethering(); + transitionTo(mSoftApStoppingState); + break; default: return NOT_HANDLED; } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; + } + } + + class SoftApStoppingState extends State { + @Override + public void enter() { + if (DBG) log(getName() + "\n"); + EventLog.writeEvent(EVENTLOG_WIFI_STATE_CHANGED, getName()); + + /* Send ourselves a delayed message to shut down if tethering fails to notify */ + sendMessageDelayed(obtainMessage(CMD_TETHER_NOTIFICATION_TIMED_OUT, + ++mTetherToken, 0), TETHER_NOTIFICATION_TIME_OUT_MSECS); + + } + @Override + public boolean processMessage(Message message) { + if (DBG) log(getName() + message.toString() + "\n"); + switch(message.what) { + case CMD_TETHER_STATE_CHANGE: + TetherStateChange stateChange = (TetherStateChange) message.obj; + + /* Wait till wifi is untethered */ + if (isWifiTethered(stateChange.active)) break; + + try { + mNwService.stopAccessPoint(mInterfaceName); + } catch(Exception e) { + loge("Exception in stopAccessPoint()"); + } + transitionTo(mDriverLoadedState); + break; + case CMD_TETHER_NOTIFICATION_TIMED_OUT: + if (message.arg1 == mTetherToken) { + loge("Failed to get tether update, force stop access point"); + try { + mNwService.stopAccessPoint(mInterfaceName); + } catch(Exception e) { + loge("Exception in stopAccessPoint()"); + } + transitionTo(mDriverLoadedState); + } + break; + case CMD_LOAD_DRIVER: + case CMD_UNLOAD_DRIVER: + case CMD_START_SUPPLICANT: + case CMD_STOP_SUPPLICANT: + case CMD_START_AP: + case CMD_STOP_AP: + case CMD_START_DRIVER: + case CMD_STOP_DRIVER: + case CMD_SET_SCAN_MODE: + case CMD_SET_SCAN_TYPE: + case CMD_SET_HIGH_PERF_MODE: + case CMD_SET_COUNTRY_CODE: + case CMD_SET_FREQUENCY_BAND: + case CMD_START_PACKET_FILTERING: + case CMD_STOP_PACKET_FILTERING: + case WifiP2pService.P2P_ENABLE_PENDING: + deferMessage(message); + break; + default: + return NOT_HANDLED; + } + EventLog.writeEvent(EVENTLOG_WIFI_EVENT_HANDLED, message.what); + return HANDLED; } } |