diff options
519 files changed, 32578 insertions, 14189 deletions
diff --git a/api/current.txt b/api/current.txt index 8c0dc14a30c3..0a83fe00b0fc 100644 --- a/api/current.txt +++ b/api/current.txt @@ -14884,7 +14884,7 @@ package android.os { public class Looper { method public void dump(android.util.Printer, java.lang.String); - method public static synchronized android.os.Looper getMainLooper(); + method public static android.os.Looper getMainLooper(); method public java.lang.Thread getThread(); method public static void loop(); method public static android.os.Looper myLooper(); @@ -23310,6 +23310,9 @@ package android.view { method protected void onMeasure(int, int); method protected void onOverScrolled(int, int, boolean, boolean); method public void onPopulateAccessibilityEvent(android.view.accessibility.AccessibilityEvent); + method public void onResetResolvedTextDirection(); + method public void onResolvePadding(int); + method public void onResolveTextDirection(); method protected void onRestoreInstanceState(android.os.Parcelable); method protected android.os.Parcelable onSaveInstanceState(); method protected void onScrollChanged(int, int, int, int); @@ -23344,10 +23347,11 @@ package android.view { method public void requestLayout(); method public boolean requestRectangleOnScreen(android.graphics.Rect); method public boolean requestRectangleOnScreen(android.graphics.Rect, boolean); - method protected void resetResolvedTextDirection(); + method public void resetResolvedTextDirection(); + method public void resolvePadding(); method public static int resolveSize(int, int); method public static int resolveSizeAndState(int, int, int); - method protected void resolveTextDirection(); + method public void resolveTextDirection(); method public void restoreHierarchyState(android.util.SparseArray<android.os.Parcelable>); method public void saveHierarchyState(android.util.SparseArray<android.os.Parcelable>); method public void scheduleDrawable(android.graphics.drawable.Drawable, java.lang.Runnable, long); diff --git a/cmds/backup/Android.mk b/cmds/backup/Android.mk index 508aec073570..73af0bc0a6ca 100644 --- a/cmds/backup/Android.mk +++ b/cmds/backup/Android.mk @@ -5,7 +5,7 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= backup.cpp -LOCAL_SHARED_LIBRARIES := libcutils libutils +LOCAL_SHARED_LIBRARIES := libcutils libutils libandroidfw LOCAL_MODULE:= btool diff --git a/cmds/backup/backup.cpp b/cmds/backup/backup.cpp index d4e669b5d9e8..ea1888beea2a 100644 --- a/cmds/backup/backup.cpp +++ b/cmds/backup/backup.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> #include <utils/String8.h> #include <fcntl.h> diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index 7d3991279770..8c46b212814a 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -9,6 +9,7 @@ LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES LOCAL_SHARED_LIBRARIES := \ libcutils \ + libandroidfw \ libutils \ libbinder \ libui \ diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 0d5b4caa91b4..3545ace49179 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -25,12 +25,12 @@ #include <cutils/properties.h> +#include <androidfw/AssetManager.h> #include <binder/IPCThreadState.h> -#include <utils/threads.h> #include <utils/Atomic.h> #include <utils/Errors.h> #include <utils/Log.h> -#include <utils/AssetManager.h> +#include <utils/threads.h> #include <ui/PixelFormat.h> #include <ui/Rect.h> diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 8e28bba2de3c..c85d72c1a998 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -20,8 +20,8 @@ #include <stdint.h> #include <sys/types.h> +#include <androidfw/AssetManager.h> #include <utils/threads.h> -#include <utils/AssetManager.h> #include <surfaceflinger/ISurfaceComposer.h> #include <surfaceflinger/SurfaceComposerClient.h> diff --git a/cmds/content/Android.mk b/cmds/content/Android.mk new file mode 100644 index 000000000000..88c46f2792d5 --- /dev/null +++ b/cmds/content/Android.mk @@ -0,0 +1,33 @@ +# Copyright 2012 The Android Open Source Project + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := $(call all-subdir-java-files) + +LOCAL_MODULE := content + +include $(BUILD_JAVA_LIBRARY) + +include $(CLEAR_VARS) +ALL_PREBUILT += $(TARGET_OUT)/bin/content +$(TARGET_OUT)/bin/content : $(LOCAL_PATH)/content | $(ACP) + $(transform-prebuilt-to-target) + +NOTICE_FILE := NOTICE +files_noticed := bin/content + +# Generate rules for a single file. The argument is the file path relative to +# the installation root +define make-notice-file + +$(TARGET_OUT_NOTICE_FILES)/src/$(1).txt: $(LOCAL_PATH)/$(NOTICE_FILE) + @echo Notice file: $$< -- $$@ + @mkdir -p $$(dir $$@) + @cat $$< >> $$@ + +$(TARGET_OUT_NOTICE_FILES)/hash-timestamp: $(TARGET_OUT_NOTICE_FILES)/src/$(1).txt + +endef + +$(foreach file,$(files_noticed),$(eval $(call make-notice-file,$(file)))) diff --git a/cmds/content/MODULE_LICENSE_APACHE2 b/cmds/content/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/cmds/content/MODULE_LICENSE_APACHE2 diff --git a/cmds/content/NOTICE b/cmds/content/NOTICE new file mode 100644 index 000000000000..33ff96160b77 --- /dev/null +++ b/cmds/content/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2012, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/cmds/content/content b/cmds/content/content new file mode 100755 index 000000000000..a8e056d04d69 --- /dev/null +++ b/cmds/content/content @@ -0,0 +1,5 @@ +# Script to start "content" on the device, which has a very rudimentary shell. +base=/system +export CLASSPATH=$base/framework/content.jar +exec app_process $base/bin com.android.commands.content.Content "$@" + diff --git a/cmds/content/src/com/android/commands/content/Content.java b/cmds/content/src/com/android/commands/content/Content.java new file mode 100644 index 000000000000..1dcba70405fc --- /dev/null +++ b/cmds/content/src/com/android/commands/content/Content.java @@ -0,0 +1,442 @@ +/* +** Copyright 2012, 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.commands.content; + +import android.app.ActivityManagerNative; +import android.app.IActivityManager; +import android.app.IActivityManager.ContentProviderHolder; +import android.content.ContentValues; +import android.content.IContentProvider; +import android.database.Cursor; +import android.net.Uri; +import android.os.Binder; +import android.os.IBinder; +import android.text.TextUtils; + +/** + * This class is a command line utility for manipulating content. A client + * can insert, update, and remove records in a content provider. For example, + * some settings may be configured before running the CTS tests, etc. + * <p> + * Examples: + * <ul> + * <li> + * # Add "new_setting" secure setting with value "new_value".</br> + * adb shell content insert --uri content://settings/secure --bind name:s:new_setting + * --bind value:s:new_value + * </li> + * <li> + * # Change "new_setting" secure setting to "newer_value" (You have to escape single quotes in + * the where clause).</br> + * adb shell content update --uri content://settings/secure --bind value:s:newer_value + * --where "name=\'new_setting\'" + * </li> + * <li> + * # Remove "new_setting" secure setting.</br> + * adb shell content delete --uri content://settings/secure --where "name=\'new_setting\'" + * </li> + * <li> + * # Query \"name\" and \"value\" columns from secure settings where \"name\" is equal to" + * \"new_setting\" and sort the result by name in ascending order.\n" + * adb shell content query --uri content://settings/secure --projection name:value + * --where "name=\'new_setting\'" --sort \"name ASC\" + * </li> + * </ul> + * </p> + */ +public class Content { + + private static final String USAGE = + "usage: adb shell content [subcommand] [options]\n" + + "\n" + + "usage: adb shell content insert --uri <URI> --bind <BINDING> [--bind <BINDING>...]\n" + + " <URI> a content provider URI.\n" + + " <BINDING> binds a typed value to a column and is formatted:\n" + + " <COLUMN_NAME>:<TYPE>:<COLUMN_VALUE> where:\n" + + " <TYPE> specifies data type such as:\n" + + " b - boolean, s - string, i - integer, l - long, f - float, d - double\n" + + " Example:\n" + + " # Add \"new_setting\" secure setting with value \"new_value\".\n" + + " adb shell content insert --uri content://settings/secure --bind name:s:new_setting" + + " --bind value:s:new_value\n" + + "\n" + + "usage: adb shell content update --uri <URI> [--where <WHERE>]\n" + + " <WHERE> is a SQL style where clause in quotes (You have to escape single quotes" + + " - see example below).\n" + + " Example:\n" + + " # Change \"new_setting\" secure setting to \"newer_value\".\n" + + " adb shell content update --uri content://settings/secure --bind" + + " value:s:newer_value --where \"name=\'new_setting\'\"\n" + + "\n" + + "usage: adb shell content delete --uri <URI> --bind <BINDING>" + + " [--bind <BINDING>...] [--where <WHERE>]\n" + + " Example:\n" + + " # Remove \"new_setting\" secure setting.\n" + + " adb shell content delete --uri content://settings/secure " + + "--where \"name=\'new_setting\'\"\n" + + "\n" + + "usage: adb shell content query --uri <URI> [--projection <PROJECTION>]" + + " [--where <WHERE>] [--sort <SORT_ORDER>]\n" + + " <PROJECTION> is a list of colon separated column names and is formatted:\n" + + " <COLUMN_NAME>[:<COLUMN_NAME>...]\n" + + " <SORT_OREDER> is the order in which rows in the result should be sorted.\n" + + " Example:\n" + + " # Select \"name\" and \"value\" columns from secure settings where \"name\" is " + + "equal to \"new_setting\" and sort the result by name in ascending order.\n" + + " adb shell content query --uri content://settings/secure --projection name:value" + + " --where \"name=\'new_setting\'\" --sort \"name ASC\"\n" + + "\n"; + + private static class Parser { + private static final String ARGUMENT_INSERT = "insert"; + private static final String ARGUMENT_DELETE = "delete"; + private static final String ARGUMENT_UPDATE = "update"; + private static final String ARGUMENT_QUERY = "query"; + private static final String ARGUMENT_WHERE = "--where"; + private static final String ARGUMENT_BIND = "--bind"; + private static final String ARGUMENT_URI = "--uri"; + private static final String ARGUMENT_PROJECTION = "--projection"; + private static final String ARGUMENT_SORT = "--sort"; + private static final String TYPE_BOOLEAN = "b"; + private static final String TYPE_STRING = "s"; + private static final String TYPE_INTEGER = "i"; + private static final String TYPE_LONG = "l"; + private static final String TYPE_FLOAT = "f"; + private static final String TYPE_DOUBLE = "d"; + private static final String COLON = ":"; + private static final String ARGUMENT_PREFIX = "--"; + + private final Tokenizer mTokenizer; + + public Parser(String[] args) { + mTokenizer = new Tokenizer(args); + } + + public Command parseCommand() { + try { + String operation = mTokenizer.nextArg(); + if (ARGUMENT_INSERT.equals(operation)) { + return parseInsertCommand(); + } else if (ARGUMENT_DELETE.equals(operation)) { + return parseDeleteCommand(); + } else if (ARGUMENT_UPDATE.equals(operation)) { + return parseUpdateCommand(); + } else if (ARGUMENT_QUERY.equals(operation)) { + return parseQueryCommand(); + } else { + throw new IllegalArgumentException("Unsupported operation: " + operation); + } + } catch (IllegalArgumentException iae) { + System.out.println(USAGE); + System.out.println("[ERROR] " + iae.getMessage()); + return null; + } + } + + private InsertCommand parseInsertCommand() { + Uri uri = null; + ContentValues values = new ContentValues(); + for (String argument; (argument = mTokenizer.nextArg()) != null;) { + if (ARGUMENT_URI.equals(argument)) { + uri = Uri.parse(argumentValueRequired(argument)); + } else if (ARGUMENT_BIND.equals(argument)) { + parseBindValue(values); + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + if (uri == null) { + throw new IllegalArgumentException("Content provider URI not specified." + + " Did you specify --uri argument?"); + } + if (values.size() == 0) { + throw new IllegalArgumentException("Bindings not specified." + + " Did you specify --bind argument(s)?"); + } + return new InsertCommand(uri, values); + } + + private DeleteCommand parseDeleteCommand() { + Uri uri = null; + String where = null; + for (String argument; (argument = mTokenizer.nextArg())!= null;) { + if (ARGUMENT_URI.equals(argument)) { + uri = Uri.parse(argumentValueRequired(argument)); + } else if (ARGUMENT_WHERE.equals(argument)) { + where = argumentValueRequired(argument); + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + if (uri == null) { + throw new IllegalArgumentException("Content provider URI not specified." + + " Did you specify --uri argument?"); + } + return new DeleteCommand(uri, where); + } + + private UpdateCommand parseUpdateCommand() { + Uri uri = null; + String where = null; + ContentValues values = new ContentValues(); + for (String argument; (argument = mTokenizer.nextArg())!= null;) { + if (ARGUMENT_URI.equals(argument)) { + uri = Uri.parse(argumentValueRequired(argument)); + } else if (ARGUMENT_WHERE.equals(argument)) { + where = argumentValueRequired(argument); + } else if (ARGUMENT_BIND.equals(argument)) { + parseBindValue(values); + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + if (uri == null) { + throw new IllegalArgumentException("Content provider URI not specified." + + " Did you specify --uri argument?"); + } + if (values.size() == 0) { + throw new IllegalArgumentException("Bindings not specified." + + " Did you specify --bind argument(s)?"); + } + return new UpdateCommand(uri, values, where); + } + + public QueryCommand parseQueryCommand() { + Uri uri = null; + String[] projection = null; + String sort = null; + String where = null; + for (String argument; (argument = mTokenizer.nextArg())!= null;) { + if (ARGUMENT_URI.equals(argument)) { + uri = Uri.parse(argumentValueRequired(argument)); + } else if (ARGUMENT_WHERE.equals(argument)) { + where = argumentValueRequired(argument); + } else if (ARGUMENT_SORT.equals(argument)) { + sort = argumentValueRequired(argument); + } else if (ARGUMENT_PROJECTION.equals(argument)) { + projection = argumentValueRequired(argument).split("[\\s]*:[\\s]*"); + } else { + throw new IllegalArgumentException("Unsupported argument: " + argument); + } + } + if (uri == null) { + throw new IllegalArgumentException("Content provider URI not specified." + + " Did you specify --uri argument?"); + } + return new QueryCommand(uri, projection, where, sort); + } + + private void parseBindValue(ContentValues values) { + String argument = mTokenizer.nextArg(); + if (TextUtils.isEmpty(argument)) { + throw new IllegalArgumentException("Binding not well formed: " + argument); + } + String[] binding = argument.split(COLON); + if (binding.length != 3) { + throw new IllegalArgumentException("Binding not well formed: " + argument); + } + String column = binding[0]; + String type = binding[1]; + String value = binding[2]; + if (TYPE_STRING.equals(type)) { + values.put(column, value); + } else if (TYPE_BOOLEAN.equalsIgnoreCase(type)) { + values.put(column, Boolean.parseBoolean(value)); + } else if (TYPE_INTEGER.equalsIgnoreCase(type) || TYPE_LONG.equalsIgnoreCase(type)) { + values.put(column, Long.parseLong(value)); + } else if (TYPE_FLOAT.equalsIgnoreCase(type) || TYPE_DOUBLE.equalsIgnoreCase(type)) { + values.put(column, Double.parseDouble(value)); + } else { + throw new IllegalArgumentException("Unsupported type: " + type); + } + } + + private String argumentValueRequired(String argument) { + String value = mTokenizer.nextArg(); + if (TextUtils.isEmpty(value) || value.startsWith(ARGUMENT_PREFIX)) { + throw new IllegalArgumentException("No value for argument: " + argument); + } + return value; + } + } + + private static class Tokenizer { + private final String[] mArgs; + private int mNextArg; + + public Tokenizer(String[] args) { + mArgs = args; + } + + private String nextArg() { + if (mNextArg < mArgs.length) { + return mArgs[mNextArg++]; + } else { + return null; + } + } + } + + private static abstract class Command { + final Uri mUri; + + public Command(Uri uri) { + mUri = uri; + } + + public final void execute() { + String providerName = mUri.getAuthority(); + try { + IActivityManager activityManager = ActivityManagerNative.getDefault(); + IContentProvider provider = null; + IBinder token = new Binder(); + try { + ContentProviderHolder holder = activityManager.getContentProviderExternal( + providerName, token); + if (holder == null) { + throw new IllegalStateException("Could not find provider: " + providerName); + } + provider = holder.provider; + onExecute(provider); + } finally { + if (provider != null) { + activityManager.removeContentProviderExternal(providerName, token); + } + } + } catch (Exception e) { + System.err.println("Error while accessing provider:" + providerName); + e.printStackTrace(); + } + } + + protected abstract void onExecute(IContentProvider provider) throws Exception; + } + + private static class InsertCommand extends Command { + final ContentValues mContentValues; + + public InsertCommand(Uri uri, ContentValues contentValues) { + super(uri); + mContentValues = contentValues; + } + + @Override + public void onExecute(IContentProvider provider) throws Exception { + provider.insert(mUri, mContentValues); + } + } + + private static class DeleteCommand extends Command { + final String mWhere; + + public DeleteCommand(Uri uri, String where) { + super(uri); + mWhere = where; + } + + @Override + public void onExecute(IContentProvider provider) throws Exception { + provider.delete(mUri, mWhere, null); + } + } + + private static class QueryCommand extends DeleteCommand { + final String[] mProjection; + final String mSortOrder; + + public QueryCommand(Uri uri, String[] projection, String where, String sortOrder) { + super(uri, where); + mProjection = projection; + mSortOrder = sortOrder; + } + + @Override + public void onExecute(IContentProvider provider) throws Exception { + Cursor cursor = provider.query(mUri, mProjection, mWhere, null, mSortOrder, null); + if (cursor == null) { + System.out.println("No result found."); + return; + } + try { + if (cursor.moveToFirst()) { + int rowIndex = 0; + StringBuilder builder = new StringBuilder(); + do { + builder.setLength(0); + builder.append("Row: ").append(rowIndex).append(" "); + rowIndex++; + final int columnCount = cursor.getColumnCount(); + for (int i = 0; i < columnCount; i++) { + if (i > 0) { + builder.append(", "); + } + String columnName = cursor.getColumnName(i); + String columnValue = null; + final int columnIndex = cursor.getColumnIndex(columnName); + final int type = cursor.getType(columnIndex); + switch (type) { + case Cursor.FIELD_TYPE_FLOAT: + columnValue = String.valueOf(cursor.getFloat(columnIndex)); + break; + case Cursor.FIELD_TYPE_INTEGER: + columnValue = String.valueOf(cursor.getInt(columnIndex)); + break; + case Cursor.FIELD_TYPE_STRING: + columnValue = cursor.getString(columnIndex); + break; + case Cursor.FIELD_TYPE_BLOB: + columnValue = "BLOB"; + break; + case Cursor.FIELD_TYPE_NULL: + columnValue = "NULL"; + break; + } + builder.append(columnName).append("=").append(columnValue); + } + System.out.println(builder); + } while (cursor.moveToNext()); + } else { + System.out.println("No reuslt found."); + } + } finally { + cursor.close(); + } + } + } + + private static class UpdateCommand extends InsertCommand { + final String mWhere; + + public UpdateCommand(Uri uri, ContentValues contentValues, String where) { + super(uri, contentValues); + mWhere = where; + } + + @Override + public void onExecute(IContentProvider provider) throws Exception { + provider.update(mUri, mContentValues, mWhere, null); + } + } + + public static void main(String[] args) { + Parser parser = new Parser(args); + Command command = parser.parseCommand(); + if (command != null) { + command.execute(); + } + } +} diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index bee5880ac4cc..90dfe76f7fa3 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -49,7 +49,6 @@ static SkBitmap::Config flinger2skia(PixelFormat f) { switch (f) { case PIXEL_FORMAT_A_8: - case PIXEL_FORMAT_L_8: return SkBitmap::kA8_Config; case PIXEL_FORMAT_RGB_565: return SkBitmap::kRGB_565_Config; diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index cfc2d1611628..71c5622ec382 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -43,6 +43,8 @@ static struct { { AID_RADIO, "isms" }, { AID_RADIO, "iphonesubinfo" }, { AID_RADIO, "simphonebook" }, + { AID_MEDIA, "common_time.clock" }, + { AID_MEDIA, "common_time.config" }, }; void *svcmgr_handle; diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 11e94e83796b..f26747b6ead4 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -147,4 +147,28 @@ LOCAL_MODULE:= sf2 include $(BUILD_EXECUTABLE) +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + codec.cpp \ + SimplePlayer.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libstagefright liblog libutils libbinder libstagefright_foundation \ + libmedia libgui libcutils libui + +LOCAL_C_INCLUDES:= \ + $(JNI_H_INCLUDE) \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax + +LOCAL_CFLAGS += -Wno-multichar + +LOCAL_MODULE_TAGS := debug + +LOCAL_MODULE:= codec + +include $(BUILD_EXECUTABLE) diff --git a/cmds/stagefright/SimplePlayer.cpp b/cmds/stagefright/SimplePlayer.cpp new file mode 100644 index 000000000000..f850344b462d --- /dev/null +++ b/cmds/stagefright/SimplePlayer.cpp @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2012 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_NDEBUG 0 +#define LOG_TAG "SimplePlayer" +#include <utils/Log.h> + +#include "SimplePlayer.h" + +#include <gui/SurfaceTextureClient.h> +#include <media/AudioTrack.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaCodec.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/NativeWindowWrapper.h> +#include <media/stagefright/NuMediaExtractor.h> + +namespace android { + +SimplePlayer::SimplePlayer() + : mState(UNINITIALIZED), + mDoMoreStuffGeneration(0), + mStartTimeRealUs(-1ll) { +} + +SimplePlayer::~SimplePlayer() { +} + +// static +status_t PostAndAwaitResponse( + const sp<AMessage> &msg, sp<AMessage> *response) { + status_t err = msg->postAndAwaitResponse(response); + + if (err != OK) { + return err; + } + + if (!(*response)->findInt32("err", &err)) { + err = OK; + } + + return err; +} +status_t SimplePlayer::setDataSource(const char *path) { + sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); + msg->setString("path", path); + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t SimplePlayer::setSurface(const sp<ISurfaceTexture> &surfaceTexture) { + sp<AMessage> msg = new AMessage(kWhatSetSurface, id()); + + sp<SurfaceTextureClient> surfaceTextureClient; + if (surfaceTexture != NULL) { + surfaceTextureClient = new SurfaceTextureClient(surfaceTexture); + } + + msg->setObject( + "native-window", new NativeWindowWrapper(surfaceTextureClient)); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t SimplePlayer::prepare() { + sp<AMessage> msg = new AMessage(kWhatPrepare, id()); + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t SimplePlayer::start() { + sp<AMessage> msg = new AMessage(kWhatStart, id()); + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t SimplePlayer::stop() { + sp<AMessage> msg = new AMessage(kWhatStop, id()); + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t SimplePlayer::reset() { + sp<AMessage> msg = new AMessage(kWhatReset, id()); + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +void SimplePlayer::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatSetDataSource: + { + status_t err; + if (mState != UNINITIALIZED) { + err = INVALID_OPERATION; + } else { + CHECK(msg->findString("path", &mPath)); + mState = UNPREPARED; + } + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatSetSurface: + { + status_t err; + if (mState != UNPREPARED) { + err = INVALID_OPERATION; + } else { + sp<RefBase> obj; + CHECK(msg->findObject("native-window", &obj)); + + mNativeWindow = static_cast<NativeWindowWrapper *>(obj.get()); + + err = OK; + } + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatPrepare: + { + status_t err; + if (mState != UNPREPARED) { + err = INVALID_OPERATION; + } else { + err = onPrepare(); + + if (err == OK) { + mState = STOPPED; + } + } + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatStart: + { + status_t err = OK; + + if (mState == UNPREPARED) { + err = onPrepare(); + + if (err == OK) { + mState = STOPPED; + } + } + + if (err == OK) { + if (mState != STOPPED) { + err = INVALID_OPERATION; + } else { + err = onStart(); + + if (err == OK) { + mState = STARTED; + } + } + } + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatStop: + { + status_t err; + + if (mState != STARTED) { + err = INVALID_OPERATION; + } else { + err = onStop(); + + if (err == OK) { + mState = STOPPED; + } + } + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatReset: + { + status_t err = OK; + + if (mState == STARTED) { + CHECK_EQ(onStop(), (status_t)OK); + mState = STOPPED; + } + + if (mState == STOPPED) { + err = onReset(); + mState = UNINITIALIZED; + } + + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatDoMoreStuff: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mDoMoreStuffGeneration) { + break; + } + + status_t err = onDoMoreStuff(); + + if (err == OK) { + msg->post(5000ll); + } + break; + } + + default: + TRESPASS(); + } +} + +status_t SimplePlayer::onPrepare() { + CHECK_EQ(mState, UNPREPARED); + + mExtractor = new NuMediaExtractor; + + status_t err = mExtractor->setDataSource(mPath.c_str()); + + if (err != OK) { + mExtractor.clear(); + return err; + } + + if (mCodecLooper == NULL) { + mCodecLooper = new ALooper; + mCodecLooper->start(); + } + + bool haveAudio = false; + bool haveVideo = false; + for (size_t i = 0; i < mExtractor->countTracks(); ++i) { + sp<AMessage> format; + status_t err = mExtractor->getTrackFormat(i, &format); + CHECK_EQ(err, (status_t)OK); + + AString mime; + CHECK(format->findString("mime", &mime)); + + if (!haveAudio && !strncasecmp(mime.c_str(), "audio/", 6)) { + haveAudio = true; + } else if (!haveVideo && !strncasecmp(mime.c_str(), "video/", 6)) { + haveVideo = true; + } else { + continue; + } + + err = mExtractor->selectTrack(i); + CHECK_EQ(err, (status_t)OK); + + CodecState *state = + &mStateByTrackIndex.editValueAt( + mStateByTrackIndex.add(i, CodecState())); + + state->mNumFramesWritten = 0; + state->mCodec = MediaCodec::CreateByType( + mCodecLooper, mime.c_str(), false /* encoder */); + + CHECK(state->mCodec != NULL); + + err = state->mCodec->configure( + format, mNativeWindow->getSurfaceTextureClient(), + 0 /* flags */); + + CHECK_EQ(err, (status_t)OK); + + size_t j = 0; + sp<ABuffer> buffer; + while (format->findBuffer(StringPrintf("csd-%d", j).c_str(), &buffer)) { + state->mCSD.push_back(buffer); + + ++j; + } + } + + for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) { + CodecState *state = &mStateByTrackIndex.editValueAt(i); + + status_t err = state->mCodec->start(); + CHECK_EQ(err, (status_t)OK); + + err = state->mCodec->getInputBuffers(&state->mBuffers[0]); + CHECK_EQ(err, (status_t)OK); + + err = state->mCodec->getOutputBuffers(&state->mBuffers[1]); + CHECK_EQ(err, (status_t)OK); + + for (size_t j = 0; j < state->mCSD.size(); ++j) { + const sp<ABuffer> &srcBuffer = state->mCSD.itemAt(j); + + size_t index; + err = state->mCodec->dequeueInputBuffer(&index, -1ll); + CHECK_EQ(err, (status_t)OK); + + const sp<ABuffer> &dstBuffer = state->mBuffers[0].itemAt(index); + + CHECK_LE(srcBuffer->size(), dstBuffer->capacity()); + dstBuffer->setRange(0, srcBuffer->size()); + memcpy(dstBuffer->data(), srcBuffer->data(), srcBuffer->size()); + + err = state->mCodec->queueInputBuffer( + index, + 0, + dstBuffer->size(), + 0ll, + MediaCodec::BUFFER_FLAG_CODECCONFIG); + CHECK_EQ(err, (status_t)OK); + } + } + + return OK; +} + +status_t SimplePlayer::onStart() { + CHECK_EQ(mState, STOPPED); + + mStartTimeRealUs = -1ll; + + sp<AMessage> msg = new AMessage(kWhatDoMoreStuff, id()); + msg->setInt32("generation", ++mDoMoreStuffGeneration); + msg->post(); + + return OK; +} + +status_t SimplePlayer::onStop() { + CHECK_EQ(mState, STARTED); + + ++mDoMoreStuffGeneration; + + return OK; +} + +status_t SimplePlayer::onReset() { + CHECK_EQ(mState, STOPPED); + + for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) { + CodecState *state = &mStateByTrackIndex.editValueAt(i); + + CHECK_EQ(state->mCodec->stop(), (status_t)OK); + } + + mStartTimeRealUs = -1ll; + + mStateByTrackIndex.clear(); + mCodecLooper.clear(); + mExtractor.clear(); + mNativeWindow.clear(); + mPath.clear(); + + return OK; +} + +status_t SimplePlayer::onDoMoreStuff() { + for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) { + CodecState *state = &mStateByTrackIndex.editValueAt(i); + + size_t index; + status_t err = state->mCodec->dequeueInputBuffer(&index); + + if (err == OK) { + state->mAvailInputBufferIndices.push_back(index); + } + + BufferInfo info; + err = state->mCodec->dequeueOutputBuffer( + &info.mIndex, + &info.mOffset, + &info.mSize, + &info.mPresentationTimeUs, + &info.mFlags); + + if (err == OK) { + state->mAvailOutputBufferInfos.push_back(info); + } else if (err == INFO_FORMAT_CHANGED) { + err = onOutputFormatChanged(mStateByTrackIndex.keyAt(i), state); + CHECK_EQ(err, (status_t)OK); + } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { + err = state->mCodec->getOutputBuffers(&state->mBuffers[1]); + CHECK_EQ(err, (status_t)OK); + } + } + + for (;;) { + size_t trackIndex; + status_t err = mExtractor->getSampleTrackIndex(&trackIndex); + + if (err != OK) { + ALOGI("encountered input EOS."); + break; + } else { + CodecState *state = &mStateByTrackIndex.editValueFor(trackIndex); + + if (state->mAvailInputBufferIndices.empty()) { + break; + } + + size_t index = *state->mAvailInputBufferIndices.begin(); + state->mAvailInputBufferIndices.erase( + state->mAvailInputBufferIndices.begin()); + + const sp<ABuffer> &dstBuffer = + state->mBuffers[0].itemAt(index); + + err = mExtractor->readSampleData(dstBuffer); + CHECK_EQ(err, (status_t)OK); + + int64_t timeUs; + CHECK_EQ(mExtractor->getSampleTime(&timeUs), (status_t)OK); + + err = state->mCodec->queueInputBuffer( + index, + dstBuffer->offset(), + dstBuffer->size(), + timeUs, + 0); + CHECK_EQ(err, (status_t)OK); + + err = mExtractor->advance(); + CHECK_EQ(err, (status_t)OK); + } + } + + int64_t nowUs = ALooper::GetNowUs(); + + if (mStartTimeRealUs < 0ll) { + mStartTimeRealUs = nowUs + 1000000ll; + } + + for (size_t i = 0; i < mStateByTrackIndex.size(); ++i) { + CodecState *state = &mStateByTrackIndex.editValueAt(i); + + while (!state->mAvailOutputBufferInfos.empty()) { + BufferInfo *info = &*state->mAvailOutputBufferInfos.begin(); + + int64_t whenRealUs = info->mPresentationTimeUs + mStartTimeRealUs; + int64_t lateByUs = nowUs - whenRealUs; + + if (lateByUs > -10000ll) { + bool release = true; + + if (lateByUs > 30000ll) { + ALOGI("track %d buffer late by %lld us, dropping.", + i, lateByUs); + state->mCodec->releaseOutputBuffer(info->mIndex); + } else { + if (state->mAudioTrack != NULL) { + const sp<ABuffer> &srcBuffer = + state->mBuffers[1].itemAt(info->mIndex); + + renderAudio(state, info, srcBuffer); + + if (info->mSize > 0) { + release = false; + } + } + + if (release) { + state->mCodec->renderOutputBufferAndRelease( + info->mIndex); + } + } + + if (release) { + state->mAvailOutputBufferInfos.erase( + state->mAvailOutputBufferInfos.begin()); + + info = NULL; + } else { + break; + } + } else { + ALOGV("track %d buffer early by %lld us.", i, -lateByUs); + break; + } + } + } + + return OK; +} + +status_t SimplePlayer::onOutputFormatChanged( + size_t trackIndex, CodecState *state) { + sp<AMessage> format; + status_t err = state->mCodec->getOutputFormat(&format); + + if (err != OK) { + return err; + } + + AString mime; + CHECK(format->findString("mime", &mime)); + + if (!strncasecmp(mime.c_str(), "audio/", 6)) { + int32_t channelCount; + int32_t sampleRate; + CHECK(format->findInt32("channel-count", &channelCount)); + CHECK(format->findInt32("sample-rate", &sampleRate)); + + state->mAudioTrack = new AudioTrack( + AUDIO_STREAM_MUSIC, + sampleRate, + AUDIO_FORMAT_PCM_16_BIT, + (channelCount == 1) + ? AUDIO_CHANNEL_OUT_MONO : AUDIO_CHANNEL_OUT_STEREO, + 0); + + state->mNumFramesWritten = 0; + } + + return OK; +} + +void SimplePlayer::renderAudio( + CodecState *state, BufferInfo *info, const sp<ABuffer> &buffer) { + CHECK(state->mAudioTrack != NULL); + + if (state->mAudioTrack->stopped()) { + state->mAudioTrack->start(); + } + + uint32_t numFramesPlayed; + CHECK_EQ(state->mAudioTrack->getPosition(&numFramesPlayed), (status_t)OK); + + uint32_t numFramesAvailableToWrite = + state->mAudioTrack->frameCount() + - (state->mNumFramesWritten - numFramesPlayed); + + size_t numBytesAvailableToWrite = + numFramesAvailableToWrite * state->mAudioTrack->frameSize(); + + size_t copy = info->mSize; + if (copy > numBytesAvailableToWrite) { + copy = numBytesAvailableToWrite; + } + + if (copy == 0) { + return; + } + + int64_t startTimeUs = ALooper::GetNowUs(); + + ssize_t nbytes = state->mAudioTrack->write( + buffer->base() + info->mOffset, copy); + + CHECK_EQ(nbytes, (ssize_t)copy); + + int64_t delayUs = ALooper::GetNowUs() - startTimeUs; + + uint32_t numFramesWritten = nbytes / state->mAudioTrack->frameSize(); + + if (delayUs > 2000ll) { + ALOGW("AudioTrack::write took %lld us, numFramesAvailableToWrite=%u, " + "numFramesWritten=%u", + delayUs, numFramesAvailableToWrite, numFramesWritten); + } + + info->mOffset += nbytes; + info->mSize -= nbytes; + + state->mNumFramesWritten += numFramesWritten; +} + +} // namespace android diff --git a/cmds/stagefright/SimplePlayer.h b/cmds/stagefright/SimplePlayer.h new file mode 100644 index 000000000000..2548252304c2 --- /dev/null +++ b/cmds/stagefright/SimplePlayer.h @@ -0,0 +1,109 @@ +/* + * Copyright (C) 2012 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 <media/stagefright/foundation/AHandler.h> +#include <media/stagefright/foundation/AString.h> +#include <utils/KeyedVector.h> + +namespace android { + +struct ABuffer; +struct ALooper; +struct AudioTrack; +struct ISurfaceTexture; +struct MediaCodec; +struct NativeWindowWrapper; +struct NuMediaExtractor; + +struct SimplePlayer : public AHandler { + SimplePlayer(); + + status_t setDataSource(const char *path); + status_t setSurface(const sp<ISurfaceTexture> &surfaceTexture); + status_t prepare(); + status_t start(); + status_t stop(); + status_t reset(); + +protected: + virtual ~SimplePlayer(); + + virtual void onMessageReceived(const sp<AMessage> &msg); + +private: + enum State { + UNINITIALIZED, + UNPREPARED, + STOPPED, + STARTED + }; + + enum { + kWhatSetDataSource, + kWhatSetSurface, + kWhatPrepare, + kWhatStart, + kWhatStop, + kWhatReset, + kWhatDoMoreStuff, + }; + + struct BufferInfo { + size_t mIndex; + size_t mOffset; + size_t mSize; + int64_t mPresentationTimeUs; + uint32_t mFlags; + }; + + struct CodecState + { + sp<MediaCodec> mCodec; + Vector<sp<ABuffer> > mCSD; + Vector<sp<ABuffer> > mBuffers[2]; + + List<size_t> mAvailInputBufferIndices; + List<BufferInfo> mAvailOutputBufferInfos; + + sp<AudioTrack> mAudioTrack; + uint32_t mNumFramesWritten; + }; + + State mState; + AString mPath; + sp<NativeWindowWrapper> mNativeWindow; + + sp<NuMediaExtractor> mExtractor; + sp<ALooper> mCodecLooper; + KeyedVector<size_t, CodecState> mStateByTrackIndex; + int32_t mDoMoreStuffGeneration; + + int64_t mStartTimeRealUs; + + status_t onPrepare(); + status_t onStart(); + status_t onStop(); + status_t onReset(); + status_t onDoMoreStuff(); + status_t onOutputFormatChanged(size_t trackIndex, CodecState *state); + + void renderAudio( + CodecState *state, BufferInfo *info, const sp<ABuffer> &buffer); + + DISALLOW_EVIL_CONSTRUCTORS(SimplePlayer); +}; + +} // namespace android diff --git a/cmds/stagefright/codec.cpp b/cmds/stagefright/codec.cpp new file mode 100644 index 000000000000..02dc8ddcb2f2 --- /dev/null +++ b/cmds/stagefright/codec.cpp @@ -0,0 +1,409 @@ +/* + * Copyright (C) 2012 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_NDEBUG 0 +#define LOG_TAG "codec" +#include <utils/Log.h> + +#include "SimplePlayer.h" + +#include <binder/ProcessState.h> + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaCodec.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/NuMediaExtractor.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +static void usage(const char *me) { + fprintf(stderr, "usage: %s [-a] use audio\n" + "\t\t[-v] use video\n" + "\t\t[-p] playback\n", me); + + exit(1); +} + +namespace android { + +struct CodecState { + sp<MediaCodec> mCodec; + Vector<sp<ABuffer> > mCSD; + size_t mCSDIndex; + Vector<sp<ABuffer> > mInBuffers; + Vector<sp<ABuffer> > mOutBuffers; + bool mSawOutputEOS; +}; + +} // namespace android + +static int decode( + const android::sp<android::ALooper> &looper, + const char *path, + bool useAudio, + bool useVideo) { + using namespace android; + + sp<NuMediaExtractor> extractor = new NuMediaExtractor; + if (extractor->setDataSource(path) != OK) { + fprintf(stderr, "unable to instantiate extractor.\n"); + return 1; + } + + KeyedVector<size_t, CodecState> stateByTrack; + + bool haveAudio = false; + bool haveVideo = false; + for (size_t i = 0; i < extractor->countTracks(); ++i) { + sp<AMessage> format; + status_t err = extractor->getTrackFormat(i, &format); + CHECK_EQ(err, (status_t)OK); + + AString mime; + CHECK(format->findString("mime", &mime)); + + if (useAudio && !haveAudio + && !strncasecmp(mime.c_str(), "audio/", 6)) { + haveAudio = true; + } else if (useVideo && !haveVideo + && !strncasecmp(mime.c_str(), "video/", 6)) { + haveVideo = true; + } else { + continue; + } + + ALOGV("selecting track %d", i); + + err = extractor->selectTrack(i); + CHECK_EQ(err, (status_t)OK); + + CodecState *state = + &stateByTrack.editValueAt(stateByTrack.add(i, CodecState())); + + state->mCodec = MediaCodec::CreateByType( + looper, mime.c_str(), false /* encoder */); + + CHECK(state->mCodec != NULL); + + err = state->mCodec->configure( + format, NULL /* surfaceTexture */, 0 /* flags */); + + CHECK_EQ(err, (status_t)OK); + + size_t j = 0; + sp<RefBase> obj; + while (format->findObject(StringPrintf("csd-%d", j).c_str(), &obj)) { + sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + state->mCSD.push_back(buffer); + + ++j; + } + + state->mCSDIndex = 0; + state->mSawOutputEOS = false; + + ALOGV("got %d pieces of codec specific data.", state->mCSD.size()); + } + + CHECK(!stateByTrack.isEmpty()); + + for (size_t i = 0; i < stateByTrack.size(); ++i) { + CodecState *state = &stateByTrack.editValueAt(i); + + sp<MediaCodec> codec = state->mCodec; + + CHECK_EQ((status_t)OK, codec->start()); + + CHECK_EQ((status_t)OK, codec->getInputBuffers(&state->mInBuffers)); + CHECK_EQ((status_t)OK, codec->getOutputBuffers(&state->mOutBuffers)); + + ALOGV("got %d input and %d output buffers", + state->mInBuffers.size(), state->mOutBuffers.size()); + + while (state->mCSDIndex < state->mCSD.size()) { + size_t index; + status_t err = codec->dequeueInputBuffer(&index); + + if (err == -EAGAIN) { + usleep(10000); + continue; + } + + CHECK_EQ(err, (status_t)OK); + + const sp<ABuffer> &srcBuffer = + state->mCSD.itemAt(state->mCSDIndex++); + + const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index); + + memcpy(buffer->data(), srcBuffer->data(), srcBuffer->size()); + + err = codec->queueInputBuffer( + index, + 0 /* offset */, + srcBuffer->size(), + 0ll /* timeUs */, + MediaCodec::BUFFER_FLAG_CODECCONFIG); + + CHECK_EQ(err, (status_t)OK); + } + } + + bool sawInputEOS = false; + + for (;;) { + if (!sawInputEOS) { + size_t trackIndex; + status_t err = extractor->getSampleTrackIndex(&trackIndex); + + if (err != OK) { + ALOGV("signalling EOS."); + + for (size_t i = 0; i < stateByTrack.size(); ++i) { + CodecState *state = &stateByTrack.editValueAt(i); + + for (;;) { + size_t index; + err = state->mCodec->dequeueInputBuffer(&index); + + if (err == -EAGAIN) { + continue; + } + + CHECK_EQ(err, (status_t)OK); + + err = state->mCodec->queueInputBuffer( + index, + 0 /* offset */, + 0 /* size */, + 0ll /* timeUs */, + MediaCodec::BUFFER_FLAG_EOS); + + CHECK_EQ(err, (status_t)OK); + break; + } + } + + sawInputEOS = true; + } else { + CodecState *state = &stateByTrack.editValueFor(trackIndex); + + size_t index; + err = state->mCodec->dequeueInputBuffer(&index); + + if (err == OK) { + ALOGV("filling input buffer %d", index); + + const sp<ABuffer> &buffer = state->mInBuffers.itemAt(index); + + err = extractor->readSampleData(buffer); + CHECK_EQ(err, (status_t)OK); + + int64_t timeUs; + err = extractor->getSampleTime(&timeUs); + CHECK_EQ(err, (status_t)OK); + + err = state->mCodec->queueInputBuffer( + index, + 0 /* offset */, + buffer->size(), + timeUs, + 0 /* flags */); + + CHECK_EQ(err, (status_t)OK); + + extractor->advance(); + } else { + CHECK_EQ(err, -EAGAIN); + } + } + } + + bool sawOutputEOSOnAllTracks = true; + for (size_t i = 0; i < stateByTrack.size(); ++i) { + CodecState *state = &stateByTrack.editValueAt(i); + if (!state->mSawOutputEOS) { + sawOutputEOSOnAllTracks = false; + break; + } + } + + if (sawOutputEOSOnAllTracks) { + break; + } + + for (size_t i = 0; i < stateByTrack.size(); ++i) { + CodecState *state = &stateByTrack.editValueAt(i); + + if (state->mSawOutputEOS) { + continue; + } + + size_t index; + size_t offset; + size_t size; + int64_t presentationTimeUs; + uint32_t flags; + status_t err = state->mCodec->dequeueOutputBuffer( + &index, &offset, &size, &presentationTimeUs, &flags, + 10000ll); + + if (err == OK) { + ALOGV("draining output buffer %d, time = %lld us", + index, presentationTimeUs); + + err = state->mCodec->releaseOutputBuffer(index); + CHECK_EQ(err, (status_t)OK); + + if (flags & MediaCodec::BUFFER_FLAG_EOS) { + ALOGV("reached EOS on output."); + + state->mSawOutputEOS = true; + } + } else if (err == INFO_OUTPUT_BUFFERS_CHANGED) { + ALOGV("INFO_OUTPUT_BUFFERS_CHANGED"); + CHECK_EQ((status_t)OK, + state->mCodec->getOutputBuffers(&state->mOutBuffers)); + + ALOGV("got %d output buffers", state->mOutBuffers.size()); + } else if (err == INFO_FORMAT_CHANGED) { + sp<AMessage> format; + CHECK_EQ((status_t)OK, state->mCodec->getOutputFormat(&format)); + + ALOGV("INFO_FORMAT_CHANGED: %s", format->debugString().c_str()); + } else { + CHECK_EQ(err, -EAGAIN); + } + } + } + + for (size_t i = 0; i < stateByTrack.size(); ++i) { + CodecState *state = &stateByTrack.editValueAt(i); + + CHECK_EQ((status_t)OK, state->mCodec->stop()); + } + + return 0; +} + +int main(int argc, char **argv) { + using namespace android; + + const char *me = argv[0]; + + bool useAudio = false; + bool useVideo = false; + bool playback = false; + + int res; + while ((res = getopt(argc, argv, "havp")) >= 0) { + switch (res) { + case 'a': + { + useAudio = true; + break; + } + + case 'v': + { + useVideo = true; + break; + } + + case 'p': + { + playback = true; + break; + } + + case '?': + case 'h': + default: + { + usage(me); + } + } + } + + argc -= optind; + argv += optind; + + if (argc != 1) { + usage(me); + } + + if (!useAudio && !useVideo) { + useAudio = useVideo = true; + } + + ProcessState::self()->startThreadPool(); + + DataSource::RegisterDefaultSniffers(); + + sp<ALooper> looper = new ALooper; + looper->start(); + + if (playback) { + sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient; + CHECK_EQ(composerClient->initCheck(), (status_t)OK); + + ssize_t displayWidth = composerClient->getDisplayWidth(0); + ssize_t displayHeight = composerClient->getDisplayHeight(0); + + ALOGV("display is %ld x %ld\n", displayWidth, displayHeight); + + sp<SurfaceControl> control = + composerClient->createSurface( + String8("A Surface"), + 0, + displayWidth, + displayHeight, + PIXEL_FORMAT_RGB_565, + 0); + + CHECK(control != NULL); + CHECK(control->isValid()); + + SurfaceComposerClient::openGlobalTransaction(); + CHECK_EQ(control->setLayer(INT_MAX), (status_t)OK); + CHECK_EQ(control->show(), (status_t)OK); + SurfaceComposerClient::closeGlobalTransaction(); + + sp<Surface> surface = control->getSurface(); + CHECK(surface != NULL); + + sp<SimplePlayer> player = new SimplePlayer; + looper->registerHandler(player); + + player->setDataSource(argv[0]); + player->setSurface(surface->getSurfaceTexture()); + player->start(); + sleep(10); + player->stop(); + player->reset(); + + composerClient->dispose(); + } else { + decode(looper, argv[0], useAudio, useVideo); + } + + looper->stop(); + + return 0; +} diff --git a/cmds/stagefright/sf2.cpp b/cmds/stagefright/sf2.cpp index ae80f8804992..18e2532b463e 100644 --- a/cmds/stagefright/sf2.cpp +++ b/cmds/stagefright/sf2.cpp @@ -198,9 +198,7 @@ protected: (new AMessage(kWhatSeek, id()))->post(5000000ll); } else if (what == ACodec::kWhatOutputFormatChanged) { - } else { - CHECK_EQ(what, (int32_t)ACodec::kWhatShutdownCompleted); - + } else if (what == ACodec::kWhatShutdownCompleted) { mDecodeLooper->unregisterHandler(mCodec->id()); if (mDecodeLooper != looper()) { diff --git a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl index c9468eb11f23..882dd6bd5503 100644 --- a/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl +++ b/core/java/android/accessibilityservice/IAccessibilityServiceConnection.aidl @@ -30,14 +30,14 @@ interface IAccessibilityServiceConnection { void setServiceInfo(in AccessibilityServiceInfo info); /** - * Finds an {@link AccessibilityNodeInfo} by accessibility id. + * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} by accessibility id. * * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. * @param interactionId The id of the interaction for matching with the callback result. * @param callback Callback which to receive the result. @@ -49,17 +49,16 @@ interface IAccessibilityServiceConnection { IAccessibilityInteractionConnectionCallback callback, long threadId); /** - * Finds {@link AccessibilityNodeInfo}s by View text. The match is case - * insensitive containment. The search is performed in the window whose - * id is specified and starts from the node whose accessibility id is - * specified. + * Finds {@link android.view.accessibility.AccessibilityNodeInfo}s by View text. + * The match is case insensitive containment. The search is performed in the window + * whose id is specified and starts from the node whose accessibility id is specified. * * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. * @param text The searched text. * @param interactionId The id of the interaction for matching with the callback result. @@ -72,16 +71,16 @@ interface IAccessibilityServiceConnection { long threadId); /** - * Finds an {@link AccessibilityNodeInfo} by View id. The search is performed in - * the window whose id is specified and starts from the node whose accessibility - * id is specified. + * Finds an {@link android.view.accessibility.AccessibilityNodeInfo} by View id. The search + * is performed in the window whose id is specified and starts from the node whose + * accessibility id is specified. * * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. * @param id The id of the node. * @param interactionId The id of the interaction for matching with the callback result. @@ -94,14 +93,15 @@ interface IAccessibilityServiceConnection { long threadId); /** - * Performs an accessibility action on an {@link AccessibilityNodeInfo}. + * Performs an accessibility action on an + * {@link android.view.accessibility.AccessibilityNodeInfo}. * * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. * @param action The action to perform. * @param interactionId The id of the interaction for matching with the callback result. diff --git a/core/java/android/accessibilityservice/UiTestAutomationBridge.java b/core/java/android/accessibilityservice/UiTestAutomationBridge.java index 616b7966eab6..334981a10d19 100644 --- a/core/java/android/accessibilityservice/UiTestAutomationBridge.java +++ b/core/java/android/accessibilityservice/UiTestAutomationBridge.java @@ -49,11 +49,13 @@ public class UiTestAutomationBridge { private static final String LOG_TAG = UiTestAutomationBridge.class.getSimpleName(); - public static final int ACTIVE_WINDOW_ID = -1; + private static final int TIMEOUT_REGISTER_SERVICE = 5000; - public static final long ROOT_NODE_ID = -1; + public static final int ACTIVE_WINDOW_ID = AccessibilityNodeInfo.ACTIVE_WINDOW_ID; - private static final int TIMEOUT_REGISTER_SERVICE = 5000; + public static final long ROOT_NODE_ID = AccessibilityNodeInfo.ROOT_NODE_ID; + + public static final int UNDEFINED = -1; private final Object mLock = new Object(); @@ -63,8 +65,6 @@ public class UiTestAutomationBridge { private AccessibilityEvent mLastEvent; - private AccessibilityEvent mLastWindowStateChangeEvent; - private volatile boolean mWaitingForEventDelivery; private volatile boolean mUnprocessedEventAvailable; @@ -141,17 +141,8 @@ public class UiTestAutomationBridge { synchronized (mLock) { while (true) { mLastEvent = AccessibilityEvent.obtain(event); - - final int eventType = event.getEventType(); - if (eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED - || eventType == AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED) { - if (mLastWindowStateChangeEvent != null) { - mLastWindowStateChangeEvent.recycle(); - } - mLastWindowStateChangeEvent = mLastEvent; - } - if (!mWaitingForEventDelivery) { + mLock.notifyAll(); break; } if (!mUnprocessedEventAvailable) { @@ -295,6 +286,43 @@ public class UiTestAutomationBridge { } /** + * Waits for the accessibility event stream to become idle, which is not to + * have received a new accessibility event within <code>idleTimeout</code>, + * and do so within a maximal global timeout as specified by + * <code>globalTimeout</code>. + * + * @param idleTimeout The timeout between two event to consider the device idle. + * @param globalTimeout The maximal global timeout in which to wait for idle. + */ + public void waitForIdle(long idleTimeout, long globalTimeout) { + final long startTimeMillis = SystemClock.uptimeMillis(); + long lastEventTime = (mLastEvent != null) + ? mLastEvent.getEventTime() : SystemClock.uptimeMillis(); + synchronized (mLock) { + while (true) { + final long currentTimeMillis = SystemClock.uptimeMillis(); + final long sinceLastEventTimeMillis = currentTimeMillis - lastEventTime; + if (sinceLastEventTimeMillis > idleTimeout) { + return; + } + if (mLastEvent != null) { + lastEventTime = mLastEvent.getEventTime(); + } + final long elapsedTimeMillis = SystemClock.uptimeMillis() - startTimeMillis; + final long remainingTimeMillis = globalTimeout - elapsedTimeMillis; + if (remainingTimeMillis <= 0) { + return; + } + try { + mLock.wait(idleTimeout); + } catch (InterruptedException e) { + /* ignore */ + } + } + } + } + + /** * Finds an {@link AccessibilityNodeInfo} by accessibility id in the active * window. The search is performed from the root node. * @@ -310,8 +338,8 @@ public class UiTestAutomationBridge { /** * Finds an {@link AccessibilityNodeInfo} by accessibility id. * - * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} - * to query the currently active window. + * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} to query + * the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id for * which to search. * @return The current window scale, where zero means a failure. @@ -341,10 +369,10 @@ public class UiTestAutomationBridge { * the window whose id is specified and starts from the node whose accessibility * id is specified. * - * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} - * to query the currently active window. + * @param accessibilityWindowId A unique window id. Use + * {@link #ACTIVE_WINDOW_ID} to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from - * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. + * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. * @return The current window scale, where zero means a failure. */ public AccessibilityNodeInfo findAccessibilityNodeInfoByViewId(int accessibilityWindowId, @@ -374,8 +402,8 @@ public class UiTestAutomationBridge { * id is specified and starts from the node whose accessibility id is * specified. * - * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} - * to query the currently active window. + * @param accessibilityWindowId A unique window id. Use + * {@link #ACTIVE_WINDOW_ID} to query the currently active window. * @param accessibilityNodeId A unique view id or virtual descendant id from * where to start the search. Use {@link #ROOT_NODE_ID} to start from the root. * @param text The searched text. @@ -406,8 +434,8 @@ public class UiTestAutomationBridge { /** * Performs an accessibility action on an {@link AccessibilityNodeInfo}. * - * @param accessibilityWindowId A unique window id. Use {@link #ACTIVE_WINDOW_ID} - * to query the currently active window. + * @param accessibilityWindowId A unique window id. Use + * {@link #ACTIVE_WINDOW_ID} to query the currently active window. * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). * @param action The action to perform. * @return Whether the action was performed. @@ -427,16 +455,16 @@ public class UiTestAutomationBridge { * @return The root info. */ public AccessibilityNodeInfo getRootAccessibilityNodeInfoInActiveWindow() { - synchronized (mLock) { - if (mLastWindowStateChangeEvent != null) { - return mLastWindowStateChangeEvent.getSource(); - } - } - return null; + // Cache the id to avoid locking + final int connectionId = mConnectionId; + ensureValidConnection(connectionId); + return AccessibilityInteractionClient.getInstance() + .findAccessibilityNodeInfoByAccessibilityId(connectionId, ACTIVE_WINDOW_ID, + ROOT_NODE_ID); } private void ensureValidConnection(int connectionId) { - if (connectionId == AccessibilityInteractionClient.NO_ID) { + if (connectionId == UNDEFINED) { throw new IllegalStateException("UiAutomationService not connected." + " Did you call #register()?"); } diff --git a/core/java/android/app/ActivityManagerNative.java b/core/java/android/app/ActivityManagerNative.java index 5a364665768d..24079a5d8a2f 100644 --- a/core/java/android/app/ActivityManagerNative.java +++ b/core/java/android/app/ActivityManagerNative.java @@ -581,6 +581,21 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM return true; } + case GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String name = data.readString(); + IBinder token = data.readStrongBinder(); + ContentProviderHolder cph = getContentProviderExternal(name, token); + reply.writeNoException(); + if (cph != null) { + reply.writeInt(1); + cph.writeToParcel(reply, 0); + } else { + reply.writeInt(0); + } + return true; + } + case PUBLISH_CONTENT_PROVIDERS_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); IBinder b = data.readStrongBinder(); @@ -601,7 +616,16 @@ public abstract class ActivityManagerNative extends Binder implements IActivityM reply.writeNoException(); return true; } - + + case REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION: { + data.enforceInterface(IActivityManager.descriptor); + String name = data.readString(); + IBinder token = data.readStrongBinder(); + removeContentProviderExternal(name, token); + reply.writeNoException(); + return true; + } + case GET_RUNNING_SERVICE_CONTROL_PANEL_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); ComponentName comp = ComponentName.CREATOR.createFromParcel(data); @@ -2178,6 +2202,25 @@ class ActivityManagerProxy implements IActivityManager reply.recycle(); return cph; } + public ContentProviderHolder getContentProviderExternal(String name, IBinder token) + throws RemoteException + { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(name); + data.writeStrongBinder(token); + mRemote.transact(GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION, data, reply, 0); + reply.readException(); + int res = reply.readInt(); + ContentProviderHolder cph = null; + if (res != 0) { + cph = ContentProviderHolder.CREATOR.createFromParcel(reply); + } + data.recycle(); + reply.recycle(); + return cph; + } public void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) throws RemoteException { @@ -2204,7 +2247,19 @@ class ActivityManagerProxy implements IActivityManager data.recycle(); reply.recycle(); } - + + public void removeContentProviderExternal(String name, IBinder token) throws RemoteException { + Parcel data = Parcel.obtain(); + Parcel reply = Parcel.obtain(); + data.writeInterfaceToken(IActivityManager.descriptor); + data.writeString(name); + data.writeStrongBinder(token); + mRemote.transact(REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION, data, reply, 0); + reply.readException(); + data.recycle(); + reply.recycle(); + } + public PendingIntent getRunningServiceControlPanel(ComponentName service) throws RemoteException { diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java index ebf692a99080..6d5cce5efdc3 100644 --- a/core/java/android/app/ContextImpl.java +++ b/core/java/android/app/ContextImpl.java @@ -911,6 +911,19 @@ class ContextImpl extends Context { } } + /** @hide */ + @Override + public void sendBroadcast(Intent intent, int userId) { + String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); + try { + intent.setAllowFds(false); + ActivityManagerNative.getDefault().broadcastIntent(mMainThread.getApplicationThread(), + intent, resolvedType, null, Activity.RESULT_OK, null, null, null, false, false, + userId); + } catch (RemoteException e) { + } + } + @Override public void sendBroadcast(Intent intent, String receiverPermission) { String resolvedType = intent.resolveTypeIfNeeded(getContentResolver()); diff --git a/core/java/android/app/IActivityManager.java b/core/java/android/app/IActivityManager.java index 7deb615f6f9f..53a71db82b14 100644 --- a/core/java/android/app/IActivityManager.java +++ b/core/java/android/app/IActivityManager.java @@ -150,8 +150,11 @@ public interface IActivityManager extends IInterface { Bitmap thumbnail, CharSequence description) throws RemoteException; public ContentProviderHolder getContentProvider(IApplicationThread caller, String name) throws RemoteException; + public ContentProviderHolder getContentProviderExternal(String name, IBinder token) + throws RemoteException; public void removeContentProvider(IApplicationThread caller, String name) throws RemoteException; + public void removeContentProviderExternal(String name, IBinder token) throws RemoteException; public void publishContentProviders(IApplicationThread caller, List<ContentProviderHolder> providers) throws RemoteException; public PendingIntent getRunningServiceControlPanel(ComponentName service) @@ -415,7 +418,7 @@ public interface IActivityManager extends IInterface { source.readStrongBinder()); noReleaseNeeded = source.readInt() != 0; } - }; + } /** Information returned after waiting for an activity start. */ public static class WaitResult implements Parcelable { @@ -601,4 +604,6 @@ public interface IActivityManager extends IInterface { int SHOW_BOOT_MESSAGE_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+137; int DISMISS_KEYGUARD_ON_NEXT_ACTIVITY_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+138; int KILL_ALL_BACKGROUND_PROCESSES_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+139; + int GET_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+140; + int REMOVE_CONTENT_PROVIDER_EXTERNAL_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION+141; } diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 6d4cdaee1060..a1198dedef13 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -905,6 +905,16 @@ public abstract class Context { public abstract void sendBroadcast(Intent intent); /** + * Same as #sendBroadcast(Intent intent), but for a specific user. Used by the system only. + * @param intent the intent to broadcast + * @param userId user to send the intent to + * @hide + */ + public void sendBroadcast(Intent intent, int userId) { + throw new RuntimeException("Not implemented. Must override in a subclass."); + } + + /** * Broadcast the given intent to all interested BroadcastReceivers, allowing * an optional required permission to be enforced. This * call is asynchronous; it returns immediately, and you will continue diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java index cd8d87f9d057..5ba9dccd36b3 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -294,6 +294,12 @@ public class ContextWrapper extends Context { mBase.sendBroadcast(intent); } + /** @hide */ + @Override + public void sendBroadcast(Intent intent, int userId) { + mBase.sendBroadcast(intent, userId); + } + @Override public void sendBroadcast(Intent intent, String receiverPermission) { mBase.sendBroadcast(intent, receiverPermission); diff --git a/core/java/android/hardware/SensorManager.java b/core/java/android/hardware/SensorManager.java index aeb5d92386ea..3fdf2469a96e 100644 --- a/core/java/android/hardware/SensorManager.java +++ b/core/java/android/hardware/SensorManager.java @@ -616,6 +616,7 @@ public class SensorManager Message msg = Message.obtain(); msg.what = 0; msg.obj = t; + msg.setAsynchronous(true); mHandler.sendMessage(msg); } } diff --git a/core/java/android/net/http/CertificateChainValidator.java b/core/java/android/net/http/CertificateChainValidator.java index f94d3207f23d..06c6c6ec248f 100644 --- a/core/java/android/net/http/CertificateChainValidator.java +++ b/core/java/android/net/http/CertificateChainValidator.java @@ -25,15 +25,17 @@ import javax.net.ssl.DefaultHostnameVerifier; import javax.net.ssl.SSLHandshakeException; import javax.net.ssl.SSLSession; import javax.net.ssl.SSLSocket; +import javax.net.ssl.X509TrustManager; import org.apache.harmony.security.provider.cert.X509CertImpl; import org.apache.harmony.xnet.provider.jsse.SSLParametersImpl; +import org.apache.harmony.xnet.provider.jsse.TrustManagerImpl; /** * Class responsible for all server certificate validation functionality * * {@hide} */ -class CertificateChainValidator { +public class CertificateChainValidator { /** * The singleton instance of the certificate chain validator @@ -122,6 +124,18 @@ class CertificateChainValidator { } /** + * Handles updates to credential storage. + */ + public static void handleTrustStorageUpdate() { + + X509TrustManager x509TrustManager = SSLParametersImpl.getDefaultTrustManager(); + if( x509TrustManager instanceof TrustManagerImpl ) { + TrustManagerImpl trustManager = (TrustManagerImpl) x509TrustManager; + trustManager.handleTrustStorageUpdate(); + } + } + + /** * Common code of doHandshakeAndValidateServerCertificates and verifyServerCertificates. * Calls DomainNamevalidator to verify the domain, and TrustManager to verify the certs. * @param chain the cert chain in X509 cert format. diff --git a/core/java/android/os/CommonClock.java b/core/java/android/os/CommonClock.java new file mode 100644 index 000000000000..3a1da9761b6b --- /dev/null +++ b/core/java/android/os/CommonClock.java @@ -0,0 +1,414 @@ +/* + * Copyright (C) 2012 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.os; + +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import java.util.NoSuchElementException; +import static libcore.io.OsConstants.*; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.os.Binder; +import android.os.CommonTimeUtils; +import android.os.IBinder; +import android.os.Parcel; +import android.os.RemoteException; +import android.os.ServiceManager; + +/** + * Used for accessing the android common time service's common clock and receiving notifications + * about common time synchronization status changes. + * @hide + */ +public class CommonClock { + /** + * Sentinel value returned by {@link #getTime()} and {@link #getEstimatedError()} when the + * common time service is not able to determine the current common time due to a lack of + * synchronization. + */ + public static final long TIME_NOT_SYNCED = -1; + + /** + * Sentinel value returned by {@link #getTimelineId()} when the common time service is not + * currently synced to any timeline. + */ + public static final long INVALID_TIMELINE_ID = 0; + + /** + * Sentinel value returned by {@link #getEstimatedError()} when the common time service is not + * currently synced to any timeline. + */ + public static final int ERROR_ESTIMATE_UNKNOWN = 0x7FFFFFFF; + + /** + * Value used by {@link #getState()} to indicate that there was an internal error while + * attempting to determine the state of the common time service. + */ + public static final int STATE_INVALID = -1; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its initial + * state and attempting to find the current timeline master, if any. The service will + * transition to either {@link #STATE_CLIENT} if it finds an active master, or to + * {@link #STATE_MASTER} if no active master is found and this client becomes the master of a + * new timeline. + */ + public static final int STATE_INITIAL = 0; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its client + * state and is synchronizing its time to a different timeline master on the network. + */ + public static final int STATE_CLIENT = 1; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its master + * state and is serving as the timeline master for other common time service clients on the + * network. + */ + public static final int STATE_MASTER = 2; + + /** + * Value used by {@link #getState()} to indicate that the common time service is in its Ronin + * state. Common time service instances in the client state enter the Ronin state after their + * timeline master becomes unreachable on the network. Common time services who enter the Ronin + * state will begin a new master election for the timeline they were recently clients of. As + * clients detect they are not the winner and drop out of the election, they will transition to + * the {@link #STATE_WAIT_FOR_ELECTION} state. When there is only one client remaining in the + * election, it will assume ownership of the timeline and transition to the + * {@link #STATE_MASTER} state. During the election, all clients will allow their timeline to + * drift without applying correction. + */ + public static final int STATE_RONIN = 3; + + /** + * Value used by {@link #getState()} to indicate that the common time service is waiting for a + * master election to conclude and for the new master to announce itself before transitioning to + * the {@link #STATE_CLIENT} state. If no new master announces itself within the timeout + * threshold, the time service will transition back to the {@link #STATE_RONIN} state in order + * to restart the election. + */ + public static final int STATE_WAIT_FOR_ELECTION = 4; + + /** + * Name of the underlying native binder service + */ + public static final String SERVICE_NAME = "common_time.clock"; + + /** + * Class constructor. + * @throws android.os.RemoteException + */ + public CommonClock() + throws RemoteException { + mRemote = ServiceManager.getService(SERVICE_NAME); + if (null == mRemote) + throw new RemoteException(); + + mInterfaceDesc = mRemote.getInterfaceDescriptor(); + mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc); + mRemote.linkToDeath(mDeathHandler, 0); + registerTimelineChangeListener(); + } + + /** + * Handy class factory method. + */ + static public CommonClock create() { + CommonClock retVal; + + try { + retVal = new CommonClock(); + } + catch (RemoteException e) { + retVal = null; + } + + return retVal; + } + + /** + * Release all native resources held by this {@link android.os.CommonClock} instance. Once + * resources have been released, the {@link android.os.CommonClock} instance is disconnected from + * the native service and will throw a {@link android.os.RemoteException} if any of its + * methods are called. Clients should always call release on their client instances before + * releasing their last Java reference to the instance. Failure to do this will cause + * non-deterministic native resource reclamation and may cause the common time service to remain + * active on the network for longer than it should. + */ + public void release() { + unregisterTimelineChangeListener(); + if (null != mRemote) { + try { + mRemote.unlinkToDeath(mDeathHandler, 0); + } + catch (NoSuchElementException e) { } + mRemote = null; + } + mUtils = null; + } + + /** + * Gets the common clock's current time. + * + * @return a signed 64-bit value representing the current common time in microseconds, or the + * special value {@link #TIME_NOT_SYNCED} if the common time service is currently not + * synchronized. + * @throws android.os.RemoteException + */ + public long getTime() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetLong(METHOD_GET_COMMON_TIME, TIME_NOT_SYNCED); + } + + /** + * Gets the current estimation of common clock's synchronization accuracy from the common time + * service. + * + * @return a signed 32-bit value representing the common time service's estimation of + * synchronization accuracy in microseconds, or the special value + * {@link #ERROR_ESTIMATE_UNKNOWN} if the common time service is currently not synchronized. + * Negative values indicate that the local server estimates that the nominal common time is + * behind the local server's time (in other words, the local clock is running fast) Positive + * values indicate that the local server estimates that the nominal common time is ahead of the + * local server's time (in other words, the local clock is running slow) + * @throws android.os.RemoteException + */ + public int getEstimatedError() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_ESTIMATED_ERROR, ERROR_ESTIMATE_UNKNOWN); + } + + /** + * Gets the ID of the timeline the common time service is currently synchronizing its clock to. + * + * @return a long representing the unique ID of the timeline the common time service is + * currently synchronizing with, or {@link #INVALID_TIMELINE_ID} if the common time service is + * currently not synchronized. + * @throws android.os.RemoteException + */ + public long getTimelineId() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetLong(METHOD_GET_TIMELINE_ID, INVALID_TIMELINE_ID); + } + + /** + * Gets the current state of this clock's common time service in the the master election + * algorithm. + * + * @return a integer indicating the current state of the this clock's common time service in the + * master election algorithm or {@link #STATE_INVALID} if there is an internal error. + * @throws android.os.RemoteException + */ + public int getState() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_STATE, STATE_INVALID); + } + + /** + * Gets the IP address and UDP port of the current timeline master. + * + * @return an InetSocketAddress containing the IP address and UDP port of the current timeline + * master, or null if there is no current master. + * @throws android.os.RemoteException + */ + public InetSocketAddress getMasterAddr() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ADDRESS); + } + + /** + * The OnTimelineChangedListener interface defines a method called by the + * {@link android.os.CommonClock} instance to indicate that the time synchronization service has + * either synchronized with a new timeline, or is no longer a member of any timeline. The + * client application can implement this interface and register the listener with the + * {@link #setTimelineChangedListener(OnTimelineChangedListener)} method. + */ + public interface OnTimelineChangedListener { + /** + * Method called when the time service's timeline has changed. + * + * @param newTimelineId a long which uniquely identifies the timeline the time + * synchronization service is now a member of, or {@link #INVALID_TIMELINE_ID} if the the + * service is not synchronized to any timeline. + */ + void onTimelineChanged(long newTimelineId); + } + + /** + * Registers an OnTimelineChangedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + */ + public void setTimelineChangedListener(OnTimelineChangedListener listener) { + synchronized (mListenerLock) { + mTimelineChangedListener = listener; + } + } + + /** + * The OnServerDiedListener interface defines a method called by the + * {@link android.os.CommonClock} instance to indicate that the connection to the native media + * server has been broken and that the {@link android.os.CommonClock} instance will need to be + * released and re-created. The client application can implement this interface and register + * the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method. + */ + public interface OnServerDiedListener { + /** + * Method called when the native media server has died. <p>If the native common time + * service encounters a fatal error and needs to restart, the binder connection from the + * {@link android.os.CommonClock} instance to the common time service will be broken. To + * restore functionality, clients should {@link #release()} their old visualizer and create + * a new instance. + */ + void onServerDied(); + } + + /** + * Registers an OnServerDiedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + */ + public void setServerDiedListener(OnServerDiedListener listener) { + synchronized (mListenerLock) { + mServerDiedListener = listener; + } + } + + protected void finalize() throws Throwable { release(); } + + private void throwOnDeadServer() throws RemoteException { + if ((null == mRemote) || (null == mUtils)) + throw new RemoteException(); + } + + private final Object mListenerLock = new Object(); + private OnTimelineChangedListener mTimelineChangedListener = null; + private OnServerDiedListener mServerDiedListener = null; + + private IBinder mRemote = null; + private String mInterfaceDesc = ""; + private CommonTimeUtils mUtils; + + private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() { + public void binderDied() { + synchronized (mListenerLock) { + if (null != mServerDiedListener) + mServerDiedListener.onServerDied(); + } + } + }; + + private class TimelineChangedListener extends Binder { + @Override + protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) + throws RemoteException { + switch (code) { + case METHOD_CBK_ON_TIMELINE_CHANGED: + data.enforceInterface(DESCRIPTOR); + long timelineId = data.readLong(); + synchronized (mListenerLock) { + if (null != mTimelineChangedListener) + mTimelineChangedListener.onTimelineChanged(timelineId); + } + return true; + } + + return super.onTransact(code, data, reply, flags); + } + + private static final String DESCRIPTOR = "android.os.ICommonClockListener"; + }; + + private TimelineChangedListener mCallbackTgt = null; + + private void registerTimelineChangeListener() throws RemoteException { + if (null != mCallbackTgt) + return; + + boolean success = false; + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + mCallbackTgt = new TimelineChangedListener(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeStrongBinder(mCallbackTgt); + mRemote.transact(METHOD_REGISTER_LISTENER, data, reply, 0); + success = (0 == reply.readInt()); + } + catch (RemoteException e) { + success = false; + } + finally { + reply.recycle(); + data.recycle(); + } + + // Did we catch a remote exception or fail to register our callback target? If so, our + // object must already be dead (or be as good as dead). Clear out all of our state so that + // our other methods will properly indicate a dead object. + if (!success) { + mCallbackTgt = null; + mRemote = null; + mUtils = null; + } + } + + private void unregisterTimelineChangeListener() { + if (null == mCallbackTgt) + return; + + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeStrongBinder(mCallbackTgt); + mRemote.transact(METHOD_UNREGISTER_LISTENER, data, reply, 0); + } + catch (RemoteException e) { } + finally { + reply.recycle(); + data.recycle(); + mCallbackTgt = null; + } + } + + private static final int METHOD_IS_COMMON_TIME_VALID = IBinder.FIRST_CALL_TRANSACTION; + private static final int METHOD_COMMON_TIME_TO_LOCAL_TIME = METHOD_IS_COMMON_TIME_VALID + 1; + private static final int METHOD_LOCAL_TIME_TO_COMMON_TIME = METHOD_COMMON_TIME_TO_LOCAL_TIME + 1; + private static final int METHOD_GET_COMMON_TIME = METHOD_LOCAL_TIME_TO_COMMON_TIME + 1; + private static final int METHOD_GET_COMMON_FREQ = METHOD_GET_COMMON_TIME + 1; + private static final int METHOD_GET_LOCAL_TIME = METHOD_GET_COMMON_FREQ + 1; + private static final int METHOD_GET_LOCAL_FREQ = METHOD_GET_LOCAL_TIME + 1; + private static final int METHOD_GET_ESTIMATED_ERROR = METHOD_GET_LOCAL_FREQ + 1; + private static final int METHOD_GET_TIMELINE_ID = METHOD_GET_ESTIMATED_ERROR + 1; + private static final int METHOD_GET_STATE = METHOD_GET_TIMELINE_ID + 1; + private static final int METHOD_GET_MASTER_ADDRESS = METHOD_GET_STATE + 1; + private static final int METHOD_REGISTER_LISTENER = METHOD_GET_MASTER_ADDRESS + 1; + private static final int METHOD_UNREGISTER_LISTENER = METHOD_REGISTER_LISTENER + 1; + + private static final int METHOD_CBK_ON_TIMELINE_CHANGED = IBinder.FIRST_CALL_TRANSACTION; +} diff --git a/core/java/android/os/CommonTimeConfig.java b/core/java/android/os/CommonTimeConfig.java new file mode 100644 index 000000000000..3355ee33d486 --- /dev/null +++ b/core/java/android/os/CommonTimeConfig.java @@ -0,0 +1,448 @@ +/* + * Copyright (C) 2012 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.os; + +import java.net.InetAddress; +import java.net.InetSocketAddress; +import java.util.NoSuchElementException; + +import android.os.CommonTimeUtils; +import android.os.IBinder; +import android.os.RemoteException; +import android.os.ServiceManager; + +/** + * Used for configuring and controlling the status of the android common time service. + * @hide + */ +public class CommonTimeConfig { + /** + * Successful operation. + */ + public static final int SUCCESS = 0; + /** + * Unspecified error. + */ + public static final int ERROR = -1; + /** + * Operation failed due to bad parameter value. + */ + public static final int ERROR_BAD_VALUE = -4; + /** + * Operation failed due to dead remote object. + */ + public static final int ERROR_DEAD_OBJECT = -7; + + /** + * Sentinel value returned by {@link #getMasterElectionGroupId()} when an error occurs trying to + * fetch the master election group. + */ + public static final long INVALID_GROUP_ID = -1; + + /** + * Name of the underlying native binder service + */ + public static final String SERVICE_NAME = "common_time.config"; + + /** + * Class constructor. + * @throws android.os.RemoteException + */ + public CommonTimeConfig() + throws RemoteException { + mRemote = ServiceManager.getService(SERVICE_NAME); + if (null == mRemote) + throw new RemoteException(); + + mInterfaceDesc = mRemote.getInterfaceDescriptor(); + mUtils = new CommonTimeUtils(mRemote, mInterfaceDesc); + mRemote.linkToDeath(mDeathHandler, 0); + } + + /** + * Handy class factory method. + */ + static public CommonTimeConfig create() { + CommonTimeConfig retVal; + + try { + retVal = new CommonTimeConfig(); + } + catch (RemoteException e) { + retVal = null; + } + + return retVal; + } + + /** + * Release all native resources held by this {@link android.os.CommonTimeConfig} instance. Once + * resources have been released, the {@link android.os.CommonTimeConfig} instance is + * disconnected from the native service and will throw a {@link android.os.RemoteException} if + * any of its methods are called. Clients should always call release on their client instances + * before releasing their last Java reference to the instance. Failure to do this will cause + * non-deterministic native resource reclamation and may cause the common time service to remain + * active on the network for longer than it should. + */ + public void release() { + if (null != mRemote) { + try { + mRemote.unlinkToDeath(mDeathHandler, 0); + } + catch (NoSuchElementException e) { } + mRemote = null; + } + mUtils = null; + } + + /** + * Gets the current priority of the common time service used in the master election protocol. + * + * @return an 8 bit value indicating the priority of this common time service relative to other + * common time services operating in the same domain. + * @throws android.os.RemoteException + */ + public byte getMasterElectionPriority() + throws RemoteException { + throwOnDeadServer(); + return (byte)mUtils.transactGetInt(METHOD_GET_MASTER_ELECTION_PRIORITY, -1); + } + + /** + * Sets the current priority of the common time service used in the master election protocol. + * + * @param priority priority of the common time service used in the master election protocol. + * Lower numbers are lower priority. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterElectionPriority(byte priority) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_MASTER_ELECTION_PRIORITY, priority); + } + + /** + * Gets the IP endpoint used by the time service to participate in the master election protocol. + * + * @return an InetSocketAddress containing the IP address and UDP port being used by the + * system's common time service to participate in the master election protocol. + * @throws android.os.RemoteException + */ + public InetSocketAddress getMasterElectionEndpoint() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetSockaddr(METHOD_GET_MASTER_ELECTION_ENDPOINT); + } + + /** + * Sets the IP endpoint used by the common time service to participate in the master election + * protocol. + * + * @param ep The IP address and UDP port to be used by the common time service to participate in + * the master election protocol. The supplied IP address must be either the broadcast or + * multicast address, unicast addresses are considered to be illegal values. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterElectionEndpoint(InetSocketAddress ep) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetSockaddr(METHOD_SET_MASTER_ELECTION_ENDPOINT, ep); + } + + /** + * Gets the current group ID used by the common time service in the master election protocol. + * + * @return The 64-bit group ID of the common time service. + * @throws android.os.RemoteException + */ + public long getMasterElectionGroupId() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetLong(METHOD_GET_MASTER_ELECTION_GROUP_ID, INVALID_GROUP_ID); + } + + /** + * Sets the current group ID used by the common time service in the master election protocol. + * + * @param id The 64-bit group ID of the common time service. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterElectionGroupId(long id) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetLong(METHOD_SET_MASTER_ELECTION_GROUP_ID, id); + } + + /** + * Gets the name of the network interface which the common time service attempts to bind to. + * + * @return a string with the network interface name which the common time service is bound to, + * or null if the service is currently unbound. Examples of interface names are things like + * "eth0", or "wlan0". + * @throws android.os.RemoteException + */ + public String getInterfaceBinding() + throws RemoteException { + throwOnDeadServer(); + + String ifaceName = mUtils.transactGetString(METHOD_GET_INTERFACE_BINDING, null); + + if ((null != ifaceName) && (0 == ifaceName.length())) + return null; + + return ifaceName; + } + + /** + * Sets the name of the network interface which the common time service should attempt to bind + * to. + * + * @param ifaceName The name of the network interface ("eth0", "wlan0", etc...) wich the common + * time service should attempt to bind to, or null to force the common time service to unbind + * from the network and run in networkless mode. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setNetworkBinding(String ifaceName) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + + return mUtils.transactSetString(METHOD_SET_INTERFACE_BINDING, + (null == ifaceName) ? "" : ifaceName); + } + + /** + * Gets the amount of time the common time service will wait between master announcements when + * it is the timeline master. + * + * @return The time (in milliseconds) between master announcements. + * @throws android.os.RemoteException + */ + public int getMasterAnnounceInterval() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_MASTER_ANNOUNCE_INTERVAL, -1); + } + + /** + * Sets the amount of time the common time service will wait between master announcements when + * it is the timeline master. + * + * @param interval The time (in milliseconds) between master announcements. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setMasterAnnounceInterval(int interval) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_MASTER_ANNOUNCE_INTERVAL, interval); + } + + /** + * Gets the amount of time the common time service will wait between time synchronization + * requests when it is the client of another common time service on the network. + * + * @return The time (in milliseconds) between time sync requests. + * @throws android.os.RemoteException + */ + public int getClientSyncInterval() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_CLIENT_SYNC_INTERVAL, -1); + } + + /** + * Sets the amount of time the common time service will wait between time synchronization + * requests when it is the client of another common time service on the network. + * + * @param interval The time (in milliseconds) between time sync requests. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setClientSyncInterval(int interval) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_CLIENT_SYNC_INTERVAL, interval); + } + + /** + * Gets the panic threshold for the estimated error level of the common time service. When the + * common time service's estimated error rises above this level, the service will panic and + * reset, causing a discontinuity in the currently synchronized timeline. + * + * @return The threshold (in microseconds) past which the common time service will panic. + * @throws android.os.RemoteException + */ + public int getPanicThreshold() + throws RemoteException { + throwOnDeadServer(); + return mUtils.transactGetInt(METHOD_GET_PANIC_THRESHOLD, -1); + } + + /** + * Sets the panic threshold for the estimated error level of the common time service. When the + * common time service's estimated error rises above this level, the service will panic and + * reset, causing a discontinuity in the currently synchronized timeline. + * + * @param threshold The threshold (in microseconds) past which the common time service will + * panic. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR}, {@link #ERROR_BAD_VALUE} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setPanicThreshold(int threshold) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + return mUtils.transactSetInt(METHOD_SET_PANIC_THRESHOLD, threshold); + } + + /** + * Gets the current state of the common time service's auto disable flag. + * + * @return The current state of the common time service's auto disable flag. + * @throws android.os.RemoteException + */ + public boolean getAutoDisable() + throws RemoteException { + throwOnDeadServer(); + return (1 == mUtils.transactGetInt(METHOD_GET_AUTO_DISABLE, 1)); + } + + /** + * Sets the current state of the common time service's auto disable flag. When the time + * service's auto disable flag is set, it will automatically cease all network activity when + * it has no active local clients, resuming activity the next time the service has interested + * local clients. When the auto disabled flag is cleared, the common time service will continue + * to participate the time synchronization group even when it has no active local clients. + * + * @param autoDisable The desired state of the common time service's auto disable flag. + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int setAutoDisable(boolean autoDisable) { + if (checkDeadServer()) + return ERROR_DEAD_OBJECT; + + return mUtils.transactSetInt(METHOD_SET_AUTO_DISABLE, autoDisable ? 1 : 0); + } + + /** + * At startup, the time service enters the initial state and remains there until it is given a + * network interface to bind to. Common time will be unavailable to clients of the common time + * service until the service joins a network (even an empty network). Devices may use the + * {@link #forceNetworklessMasterMode()} method to force a time service in the INITIAL state + * with no network configuration to assume MASTER status for a brand new timeline in order to + * allow clients of the common time service to operate, even though the device is isolated and + * not on any network. When a networkless master does join a network, it will defer to any + * masters already on the network, or continue to maintain the timeline it made up during its + * networkless state if no other masters are detected. Attempting to force a client into master + * mode while it is actively bound to a network will fail with the status code {@link #ERROR} + * + * @return {@link #SUCCESS} in case of success, + * {@link #ERROR} or {@link #ERROR_DEAD_OBJECT} in case of failure. + */ + public int forceNetworklessMasterMode() { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(METHOD_FORCE_NETWORKLESS_MASTER_MODE, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + /** + * The OnServerDiedListener interface defines a method called by the + * {@link android.os.CommonTimeConfig} instance to indicate that the connection to the native + * media server has been broken and that the {@link android.os.CommonTimeConfig} instance will + * need to be released and re-created. The client application can implement this interface and + * register the listener with the {@link #setServerDiedListener(OnServerDiedListener)} method. + */ + public interface OnServerDiedListener { + /** + * Method called when the native common time service has died. <p>If the native common time + * service encounters a fatal error and needs to restart, the binder connection from the + * {@link android.os.CommonTimeConfig} instance to the common time service will be broken. + */ + void onServerDied(); + } + + /** + * Registers an OnServerDiedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + */ + public void setServerDiedListener(OnServerDiedListener listener) { + synchronized (mListenerLock) { + mServerDiedListener = listener; + } + } + + protected void finalize() throws Throwable { release(); } + + private boolean checkDeadServer() { + return ((null == mRemote) || (null == mUtils)); + } + + private void throwOnDeadServer() throws RemoteException { + if (checkDeadServer()) + throw new RemoteException(); + } + + private final Object mListenerLock = new Object(); + private OnServerDiedListener mServerDiedListener = null; + + private IBinder mRemote = null; + private String mInterfaceDesc = ""; + private CommonTimeUtils mUtils; + + private IBinder.DeathRecipient mDeathHandler = new IBinder.DeathRecipient() { + public void binderDied() { + synchronized (mListenerLock) { + if (null != mServerDiedListener) + mServerDiedListener.onServerDied(); + } + } + }; + + private static final int METHOD_GET_MASTER_ELECTION_PRIORITY = IBinder.FIRST_CALL_TRANSACTION; + private static final int METHOD_SET_MASTER_ELECTION_PRIORITY = METHOD_GET_MASTER_ELECTION_PRIORITY + 1; + private static final int METHOD_GET_MASTER_ELECTION_ENDPOINT = METHOD_SET_MASTER_ELECTION_PRIORITY + 1; + private static final int METHOD_SET_MASTER_ELECTION_ENDPOINT = METHOD_GET_MASTER_ELECTION_ENDPOINT + 1; + private static final int METHOD_GET_MASTER_ELECTION_GROUP_ID = METHOD_SET_MASTER_ELECTION_ENDPOINT + 1; + private static final int METHOD_SET_MASTER_ELECTION_GROUP_ID = METHOD_GET_MASTER_ELECTION_GROUP_ID + 1; + private static final int METHOD_GET_INTERFACE_BINDING = METHOD_SET_MASTER_ELECTION_GROUP_ID + 1; + private static final int METHOD_SET_INTERFACE_BINDING = METHOD_GET_INTERFACE_BINDING + 1; + private static final int METHOD_GET_MASTER_ANNOUNCE_INTERVAL = METHOD_SET_INTERFACE_BINDING + 1; + private static final int METHOD_SET_MASTER_ANNOUNCE_INTERVAL = METHOD_GET_MASTER_ANNOUNCE_INTERVAL + 1; + private static final int METHOD_GET_CLIENT_SYNC_INTERVAL = METHOD_SET_MASTER_ANNOUNCE_INTERVAL + 1; + private static final int METHOD_SET_CLIENT_SYNC_INTERVAL = METHOD_GET_CLIENT_SYNC_INTERVAL + 1; + private static final int METHOD_GET_PANIC_THRESHOLD = METHOD_SET_CLIENT_SYNC_INTERVAL + 1; + private static final int METHOD_SET_PANIC_THRESHOLD = METHOD_GET_PANIC_THRESHOLD + 1; + private static final int METHOD_GET_AUTO_DISABLE = METHOD_SET_PANIC_THRESHOLD + 1; + private static final int METHOD_SET_AUTO_DISABLE = METHOD_GET_AUTO_DISABLE + 1; + private static final int METHOD_FORCE_NETWORKLESS_MASTER_MODE = METHOD_SET_AUTO_DISABLE + 1; +} diff --git a/core/java/android/os/CommonTimeUtils.java b/core/java/android/os/CommonTimeUtils.java new file mode 100644 index 000000000000..9081ee411d61 --- /dev/null +++ b/core/java/android/os/CommonTimeUtils.java @@ -0,0 +1,291 @@ +/* + * Copyright (C) 2012 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.os; + +import java.net.InetAddress; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetSocketAddress; +import static libcore.io.OsConstants.*; + +class CommonTimeUtils { + /** + * Successful operation. + */ + public static final int SUCCESS = 0; + /** + * Unspecified error. + */ + public static final int ERROR = -1; + /** + * Operation failed due to bad parameter value. + */ + public static final int ERROR_BAD_VALUE = -4; + /** + * Operation failed due to dead remote object. + */ + public static final int ERROR_DEAD_OBJECT = -7; + + public CommonTimeUtils(IBinder remote, String interfaceDesc) { + mRemote = remote; + mInterfaceDesc = interfaceDesc; + } + + public int transactGetInt(int method_code, int error_ret_val) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + int ret_val; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + ret_val = (0 == res) ? reply.readInt() : error_ret_val; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetInt(int method_code, int val) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeInt(val); + mRemote.transact(method_code, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + public long transactGetLong(int method_code, long error_ret_val) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + long ret_val; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + ret_val = (0 == res) ? reply.readLong() : error_ret_val; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetLong(int method_code, long val) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeLong(val); + mRemote.transact(method_code, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + public String transactGetString(int method_code, String error_ret_val) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + String ret_val; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + ret_val = (0 == res) ? reply.readString() : error_ret_val; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetString(int method_code, String val) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + + try { + data.writeInterfaceToken(mInterfaceDesc); + data.writeString(val); + mRemote.transact(method_code, data, reply, 0); + + return reply.readInt(); + } + catch (RemoteException e) { + return ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + } + + public InetSocketAddress transactGetSockaddr(int method_code) + throws RemoteException { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + InetSocketAddress ret_val = null; + + try { + int res; + data.writeInterfaceToken(mInterfaceDesc); + mRemote.transact(method_code, data, reply, 0); + + res = reply.readInt(); + if (0 == res) { + int type; + int port = 0; + String addrStr = null; + + type = reply.readInt(); + + if (AF_INET == type) { + int addr = reply.readInt(); + port = reply.readInt(); + addrStr = String.format("%d.%d.%d.%d", (addr >> 24) & 0xFF, + (addr >> 16) & 0xFF, + (addr >> 8) & 0xFF, + addr & 0xFF); + } else if (AF_INET6 == type) { + int addr1 = reply.readInt(); + int addr2 = reply.readInt(); + int addr3 = reply.readInt(); + int addr4 = reply.readInt(); + + port = reply.readInt(); + + int flowinfo = reply.readInt(); + int scope_id = reply.readInt(); + + addrStr = String.format("[%04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X]", + (addr1 >> 16) & 0xFFFF, addr1 & 0xFFFF, + (addr2 >> 16) & 0xFFFF, addr2 & 0xFFFF, + (addr3 >> 16) & 0xFFFF, addr3 & 0xFFFF, + (addr4 >> 16) & 0xFFFF, addr4 & 0xFFFF); + } + + if (null != addrStr) { + ret_val = new InetSocketAddress(addrStr, port); + } + } + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + public int transactSetSockaddr(int method_code, InetSocketAddress addr) { + android.os.Parcel data = android.os.Parcel.obtain(); + android.os.Parcel reply = android.os.Parcel.obtain(); + int ret_val = ERROR; + + try { + data.writeInterfaceToken(mInterfaceDesc); + + if (null == addr) { + data.writeInt(0); + } else { + data.writeInt(1); + final InetAddress a = addr.getAddress(); + final byte[] b = a.getAddress(); + final int p = addr.getPort(); + + if (a instanceof Inet4Address) { + int v4addr = (((int)b[0] & 0xFF) << 24) | + (((int)b[1] & 0xFF) << 16) | + (((int)b[2] & 0xFF) << 8) | + ((int)b[3] & 0xFF); + + data.writeInt(AF_INET); + data.writeInt(v4addr); + data.writeInt(p); + } else + if (a instanceof Inet6Address) { + int i; + Inet6Address v6 = (Inet6Address)a; + data.writeInt(AF_INET6); + for (i = 0; i < 4; ++i) { + int aword = (((int)b[(i*4) + 0] & 0xFF) << 24) | + (((int)b[(i*4) + 1] & 0xFF) << 16) | + (((int)b[(i*4) + 2] & 0xFF) << 8) | + ((int)b[(i*4) + 3] & 0xFF); + data.writeInt(aword); + } + data.writeInt(p); + data.writeInt(0); // flow info + data.writeInt(v6.getScopeId()); + } else { + return ERROR_BAD_VALUE; + } + } + + mRemote.transact(method_code, data, reply, 0); + ret_val = reply.readInt(); + } + catch (RemoteException e) { + ret_val = ERROR_DEAD_OBJECT; + } + finally { + reply.recycle(); + data.recycle(); + } + + return ret_val; + } + + private IBinder mRemote; + private String mInterfaceDesc; +}; diff --git a/core/java/android/os/Handler.java b/core/java/android/os/Handler.java index af2fa9bf1ffa..610b3550402a 100644 --- a/core/java/android/os/Handler.java +++ b/core/java/android/os/Handler.java @@ -513,7 +513,7 @@ public class Handler { * message queue. */ public final void removeMessages(int what) { - mQueue.removeMessages(this, what, null, true); + mQueue.removeMessages(this, what, null); } /** @@ -522,7 +522,7 @@ public class Handler { * all messages will be removed. */ public final void removeMessages(int what, Object object) { - mQueue.removeMessages(this, what, object, true); + mQueue.removeMessages(this, what, object); } /** @@ -539,7 +539,7 @@ public class Handler { * the message queue. */ public final boolean hasMessages(int what) { - return mQueue.removeMessages(this, what, null, false); + return mQueue.hasMessages(this, what, null); } /** @@ -547,7 +547,7 @@ public class Handler { * whose obj is 'object' in the message queue. */ public final boolean hasMessages(int what, Object object) { - return mQueue.removeMessages(this, what, object, false); + return mQueue.hasMessages(this, what, object); } // if we can get rid of this method, the handler need not remember its loop diff --git a/core/java/android/os/Looper.java b/core/java/android/os/Looper.java index 5607f7fc7917..a06aadb6d131 100644 --- a/core/java/android/os/Looper.java +++ b/core/java/android/os/Looper.java @@ -55,13 +55,13 @@ public class Looper { // sThreadLocal.get() will return null unless you've called prepare(). static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); + private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; volatile boolean mRun; - private Printer mLogging = null; - private static Looper mMainLooper = null; // guarded by Looper.class + private Printer mLogging; /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference @@ -70,10 +70,14 @@ public class Looper { * {@link #quit()}. */ public static void prepare() { + prepare(true); + } + + private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } - sThreadLocal.set(new Looper()); + sThreadLocal.set(new Looper(quitAllowed)); } /** @@ -83,19 +87,21 @@ public class Looper { * to call this function yourself. See also: {@link #prepare()} */ public static void prepareMainLooper() { - prepare(); - setMainLooper(myLooper()); - myLooper().mQueue.mQuitAllowed = false; - } - - private synchronized static void setMainLooper(Looper looper) { - mMainLooper = looper; + prepare(false); + synchronized (Looper.class) { + if (sMainLooper != null) { + throw new IllegalStateException("The main Looper has already been prepared."); + } + sMainLooper = myLooper(); + } } /** Returns the application's main looper, which lives in the main thread of the application. */ - public synchronized static Looper getMainLooper() { - return mMainLooper; + public static Looper getMainLooper() { + synchronized (Looper.class) { + return sMainLooper; + } } /** @@ -103,63 +109,61 @@ public class Looper { * {@link #quit()} to end the loop. */ public static void loop() { - Looper me = myLooper(); + final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } - MessageQueue queue = me.mQueue; - + final MessageQueue queue = me.mQueue; + // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); - - while (true) { + + for (;;) { Message msg = queue.next(); // might block - if (msg != null) { - if (msg.target == null) { - // No target is a magic identifier for the quit message. - return; - } + if (msg == null) { + // No message indicates that the message queue is quitting. + return; + } - long wallStart = 0; - long threadStart = 0; + long wallStart = 0; + long threadStart = 0; - // This must be in a local variable, in case a UI event sets the logger - Printer logging = me.mLogging; - if (logging != null) { - logging.println(">>>>> Dispatching to " + msg.target + " " + - msg.callback + ": " + msg.what); - wallStart = SystemClock.currentTimeMicro(); - threadStart = SystemClock.currentThreadTimeMicro(); - } + // This must be in a local variable, in case a UI event sets the logger + Printer logging = me.mLogging; + if (logging != null) { + logging.println(">>>>> Dispatching to " + msg.target + " " + + msg.callback + ": " + msg.what); + wallStart = SystemClock.currentTimeMicro(); + threadStart = SystemClock.currentThreadTimeMicro(); + } - msg.target.dispatchMessage(msg); + msg.target.dispatchMessage(msg); - if (logging != null) { - long wallTime = SystemClock.currentTimeMicro() - wallStart; - long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; + if (logging != null) { + long wallTime = SystemClock.currentTimeMicro() - wallStart; + long threadTime = SystemClock.currentThreadTimeMicro() - threadStart; - logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); - if (logging instanceof Profiler) { - ((Profiler) logging).profile(msg, wallStart, wallTime, - threadStart, threadTime); - } + logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); + if (logging instanceof Profiler) { + ((Profiler) logging).profile(msg, wallStart, wallTime, + threadStart, threadTime); } + } - // Make sure that during the course of dispatching the - // identity of the thread wasn't corrupted. - final long newIdent = Binder.clearCallingIdentity(); - if (ident != newIdent) { - Log.wtf(TAG, "Thread identity changed from 0x" - + Long.toHexString(ident) + " to 0x" - + Long.toHexString(newIdent) + " while dispatching to " - + msg.target.getClass().getName() + " " - + msg.callback + " what=" + msg.what); - } - - msg.recycle(); + // Make sure that during the course of dispatching the + // identity of the thread wasn't corrupted. + final long newIdent = Binder.clearCallingIdentity(); + if (ident != newIdent) { + Log.wtf(TAG, "Thread identity changed from 0x" + + Long.toHexString(ident) + " to 0x" + + Long.toHexString(newIdent) + " while dispatching to " + + msg.target.getClass().getName() + " " + + msg.callback + " what=" + msg.what); } + + msg.recycle(); } } @@ -193,18 +197,61 @@ public class Looper { return myLooper().mQueue; } - private Looper() { - mQueue = new MessageQueue(); + private Looper(boolean quitAllowed) { + mQueue = new MessageQueue(quitAllowed); mRun = true; mThread = Thread.currentThread(); } + /** + * Quits the looper. + * + * Causes the {@link #loop} method to terminate as soon as possible. + */ public void quit() { - Message msg = Message.obtain(); - // NOTE: By enqueueing directly into the message queue, the - // message is left with a null target. This is how we know it is - // a quit message. - mQueue.enqueueMessage(msg, 0); + mQueue.quit(); + } + + /** + * Posts a synchronization barrier to the Looper's message queue. + * + * Message processing occurs as usual until the message queue encounters the + * synchronization barrier that has been posted. When the barrier is encountered, + * later synchronous messages in the queue are stalled (prevented from being executed) + * until the barrier is released by calling {@link #removeSyncBarrier} and specifying + * the token that identifies the synchronization barrier. + * + * This method is used to immediately postpone execution of all subsequently posted + * synchronous messages until a condition is met that releases the barrier. + * Asynchronous messages (see {@link Message#isAsynchronous} are exempt from the barrier + * and continue to be processed as usual. + * + * This call must be always matched by a call to {@link #removeSyncBarrier} with + * the same token to ensure that the message queue resumes normal operation. + * Otherwise the application will probably hang! + * + * @return A token that uniquely identifies the barrier. This token must be + * passed to {@link #removeSyncBarrier} to release the barrier. + * + * @hide + */ + public final int postSyncBarrier() { + return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis()); + } + + + /** + * Removes a synchronization barrier. + * + * @param token The synchronization barrier token that was returned by + * {@link #postSyncBarrier}. + * + * @throws IllegalStateException if the barrier was not found. + * + * @hide + */ + public final void removeSyncBarrier(int token) { + mQueue.removeSyncBarrier(token); } /** diff --git a/core/java/android/os/MessageQueue.java b/core/java/android/os/MessageQueue.java index 11dc1248c979..64027ef7fc81 100644 --- a/core/java/android/os/MessageQueue.java +++ b/core/java/android/os/MessageQueue.java @@ -30,21 +30,24 @@ import java.util.ArrayList; * {@link Looper#myQueue() Looper.myQueue()}. */ public class MessageQueue { + // True if the message queue can be quit. + private final boolean mQuitAllowed; + + @SuppressWarnings("unused") + private int mPtr; // used by native code + Message mMessages; private final ArrayList<IdleHandler> mIdleHandlers = new ArrayList<IdleHandler>(); private IdleHandler[] mPendingIdleHandlers; private boolean mQuiting; - boolean mQuitAllowed = true; // Indicates whether next() is blocked waiting in pollOnce() with a non-zero timeout. private boolean mBlocked; - // Indicates the barrier nesting level. - private int mBarrierNestCount; + // The next barrier token. + // Barriers are indicated by messages with a null target whose arg1 field carries the token. + private int mNextBarrierToken; - @SuppressWarnings("unused") - private int mPtr; // used by native code - private native void nativeInit(); private native void nativeDestroy(); private native void nativePollOnce(int ptr, int timeoutMillis); @@ -97,56 +100,11 @@ public class MessageQueue { } } - /** - * Acquires a synchronization barrier. - * - * While a synchronization barrier is active, only asynchronous messages are - * permitted to execute. Synchronous messages are retained but are not executed - * until the synchronization barrier is released. - * - * This method is used to immediately postpone execution of all synchronous messages - * until a condition is met that releases the barrier. Asynchronous messages are - * exempt from the barrier and continue to be executed as usual. - * - * This call nests and must be matched by an equal number of calls to - * {@link #releaseSyncBarrier}. - * - * @hide - */ - public final void acquireSyncBarrier() { - synchronized (this) { - mBarrierNestCount += 1; - } - } - - /** - * Releases a synchronization barrier. - * - * This class undoes one invocation of {@link #acquireSyncBarrier}. - * - * @throws IllegalStateException if the barrier is not acquired. - * - * @hide - */ - public final void releaseSyncBarrier() { - synchronized (this) { - if (mBarrierNestCount == 0) { - throw new IllegalStateException("The message queue synchronization barrier " - + "has not been acquired."); - } - - mBarrierNestCount -= 1; - if (!mBlocked || mMessages == null) { - return; - } - } - nativeWake(mPtr); - } - - MessageQueue() { + MessageQueue(boolean quitAllowed) { + mQuitAllowed = quitAllowed; nativeInit(); } - + @Override protected void finalize() throws Throwable { try { @@ -167,26 +125,26 @@ public class MessageQueue { nativePollOnce(mPtr, nextPollTimeoutMillis); synchronized (this) { + if (mQuiting) { + return null; + } + // Try to retrieve the next message. Return if found. final long now = SystemClock.uptimeMillis(); - Message prevMsg = null; Message msg = mMessages; - for (;;) { - if (msg == null) { - // No more messages. - nextPollTimeoutMillis = -1; - break; - } - - final long when = msg.when; - if (now < when) { + if (msg != null && msg.target == null) { + // Stalled by a barrier. Find the next asynchronous message in the queue. + do { + prevMsg = msg; + msg = msg.next; + } while (msg != null && !msg.isAsynchronous()); + } + if (msg != null) { + if (now < msg.when) { // Next message is not ready. Set a timeout to wake up when it is ready. - nextPollTimeoutMillis = (int) Math.min(when - now, Integer.MAX_VALUE); - break; - } - - if (mBarrierNestCount == 0 || msg.isAsynchronous()) { + nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE); + } else { // Got a message. mBlocked = false; if (prevMsg != null) { @@ -199,16 +157,16 @@ public class MessageQueue { msg.markInUse(); return msg; } - - // We have a message that we could return except that it is - // blocked by the sync barrier. In particular, this means that - // we are not idle yet, so we do not want to run the idle handlers. - prevMsg = msg; - msg = msg.next; + } else { + // No more messages. + nextPollTimeoutMillis = -1; } // If first time idle, then get the number of idlers to run. - if (pendingIdleHandlerCount < 0 && msg == mMessages) { + // Idle handles only run if the queue is empty or if the first message + // in the queue (possibly a barrier) is due to be handled in the future. + if (pendingIdleHandlerCount < 0 + && (mMessages == null || now < mMessages.when)) { pendingIdleHandlerCount = mIdleHandlers.size(); } if (pendingIdleHandlerCount <= 0) { @@ -252,27 +210,94 @@ public class MessageQueue { } } + final void quit() { + if (!mQuitAllowed) { + throw new RuntimeException("Main thread not allowed to quit."); + } + + synchronized (this) { + if (mQuiting) { + return; + } + mQuiting = true; + } + nativeWake(mPtr); + } + + final int enqueueSyncBarrier(long when) { + // Enqueue a new sync barrier token. + // We don't need to wake the queue because the purpose of a barrier is to stall it. + synchronized (this) { + final int token = mNextBarrierToken++; + final Message msg = Message.obtain(); + msg.arg1 = token; + + Message prev = null; + Message p = mMessages; + if (when != 0) { + while (p != null && p.when <= when) { + prev = p; + p = p.next; + } + } + if (prev != null) { // invariant: p == prev.next + msg.next = p; + prev.next = msg; + } else { + msg.next = p; + mMessages = msg; + } + return token; + } + } + + final void removeSyncBarrier(int token) { + // Remove a sync barrier token from the queue. + // If the queue is no longer stalled by a barrier then wake it. + final boolean needWake; + synchronized (this) { + Message prev = null; + Message p = mMessages; + while (p != null && (p.target != null || p.arg1 != token)) { + prev = p; + p = p.next; + } + if (p == null) { + throw new IllegalStateException("The specified message queue synchronization " + + " barrier token has not been posted or has already been removed."); + } + if (prev != null) { + prev.next = p.next; + needWake = false; + } else { + mMessages = p.next; + needWake = mMessages == null || mMessages.target != null; + } + p.recycle(); + } + if (needWake) { + nativeWake(mPtr); + } + } + final boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { - throw new AndroidRuntimeException(msg - + " This message is already in use."); + throw new AndroidRuntimeException(msg + " This message is already in use."); } - if (msg.target == null && !mQuitAllowed) { - throw new RuntimeException("Main thread not allowed to quit"); + if (msg.target == null) { + throw new AndroidRuntimeException("Message must have a target."); } - final boolean needWake; + + boolean needWake; synchronized (this) { if (mQuiting) { RuntimeException e = new RuntimeException( - msg.target + " sending message to a Handler on a dead thread"); + msg.target + " sending message to a Handler on a dead thread"); Log.w("MessageQueue", e.getMessage(), e); return false; - } else if (msg.target == null) { - mQuiting = true; } msg.when = when; - //Log.d("MessageQueue", "Enqueing: " + msg); Message p = mMessages; if (p == null || when == 0 || when < p.when) { // New head, wake up the event queue if blocked. @@ -281,18 +306,22 @@ public class MessageQueue { needWake = mBlocked; } else { // Inserted within the middle of the queue. Usually we don't have to wake - // up the event queue unless the message is asynchronous and it might be - // possible for it to be returned out of sequence relative to an earlier - // synchronous message at the head of the queue. - Message prev = null; - while (p != null && p.when <= when) { + // up the event queue unless there is a barrier at the head of the queue + // and the message is the earliest asynchronous message in the queue. + needWake = mBlocked && p.target == null && msg.isAsynchronous(); + Message prev; + for (;;) { prev = p; p = p.next; + if (p == null || when < p.when) { + break; + } + if (needWake && p.isAsynchronous()) { + needWake = false; + } } - msg.next = prev.next; + msg.next = p; // invariant: p == prev.next prev.next = msg; - needWake = mBlocked && mBarrierNestCount != 0 && msg.isAsynchronous() - && !mMessages.isAsynchronous(); } } if (needWake) { @@ -301,17 +330,34 @@ public class MessageQueue { return true; } - final boolean removeMessages(Handler h, int what, Object object, - boolean doRemove) { + final boolean hasMessages(Handler h, int what, Object object) { + if (h == null) { + return false; + } + + synchronized (this) { + Message p = mMessages; + while (p != null) { + if (p.target == h && p.what == what && (object == null || p.obj == object)) { + return true; + } + p = p.next; + } + return false; + } + } + + final void removeMessages(Handler h, int what, Object object) { + if (h == null) { + return; + } + synchronized (this) { Message p = mMessages; - boolean found = false; // Remove all messages at front. while (p != null && p.target == h && p.what == what && (object == null || p.obj == object)) { - if (!doRemove) return true; - found = true; Message n = p.next; mMessages = n; p.recycle(); @@ -324,8 +370,6 @@ public class MessageQueue { if (n != null) { if (n.target == h && n.what == what && (object == null || n.obj == object)) { - if (!doRemove) return true; - found = true; Message nn = n.next; n.recycle(); p.next = nn; @@ -334,13 +378,11 @@ public class MessageQueue { } p = n; } - - return found; } } final void removeMessages(Handler h, Runnable r, Object object) { - if (r == null) { + if (h == null || r == null) { return; } @@ -374,6 +416,10 @@ public class MessageQueue { } final void removeCallbacksAndMessages(Handler h, Object object) { + if (h == null) { + return; + } + synchronized (this) { Message p = mMessages; @@ -401,16 +447,4 @@ public class MessageQueue { } } } - - /* - private void dumpQueue_l() - { - Message p = mMessages; - System.out.println(this + " queue is:"); - while (p != null) { - System.out.println(" " + p); - p = p.next; - } - } - */ } diff --git a/core/java/android/os/Power.java b/core/java/android/os/Power.java index 5a7921590156..58df940ca6b6 100644 --- a/core/java/android/os/Power.java +++ b/core/java/android/os/Power.java @@ -104,4 +104,6 @@ public class Power } private static native void rebootNative(String reason) throws IOException ; + + public static native int powerInitNative(); } diff --git a/core/java/android/pim/ContactsAsyncHelper.java b/core/java/android/pim/ContactsAsyncHelper.java deleted file mode 100644 index 21fc5940d1f0..000000000000 --- a/core/java/android/pim/ContactsAsyncHelper.java +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2008 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.pim; - -import com.android.internal.telephony.CallerInfo; -import com.android.internal.telephony.Connection; - -import android.content.ContentUris; -import android.content.Context; -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; -import android.provider.ContactsContract.Contacts; -import android.util.Log; -import android.view.View; -import android.widget.ImageView; - -import java.io.InputStream; - -/** - * Helper class for async access of images. - */ -public class ContactsAsyncHelper extends Handler { - - private static final boolean DBG = false; - private static final String LOG_TAG = "ContactsAsyncHelper"; - - /** - * Interface for a WorkerHandler result return. - */ - public interface OnImageLoadCompleteListener { - /** - * Called when the image load is complete. - * - * @param imagePresent true if an image was found - */ - public void onImageLoadComplete(int token, Object cookie, ImageView iView, - boolean imagePresent); - } - - // constants - private static final int EVENT_LOAD_IMAGE = 1; - private static final int DEFAULT_TOKEN = -1; - - // static objects - private static Handler sThreadHandler; - private static ContactsAsyncHelper sInstance; - - static { - sInstance = new ContactsAsyncHelper(); - } - - private static final class WorkerArgs { - public Context context; - public ImageView view; - public Uri uri; - public int defaultResource; - public Object result; - public Object cookie; - public OnImageLoadCompleteListener listener; - public CallerInfo info; - } - - /** - * public inner class to help out the ContactsAsyncHelper callers - * with tracking the state of the CallerInfo Queries and image - * loading. - * - * Logic contained herein is used to remove the race conditions - * that exist as the CallerInfo queries run and mix with the image - * loads, which then mix with the Phone state changes. - */ - public static class ImageTracker { - - // Image display states - public static final int DISPLAY_UNDEFINED = 0; - public static final int DISPLAY_IMAGE = -1; - public static final int DISPLAY_DEFAULT = -2; - - // State of the image on the imageview. - private CallerInfo mCurrentCallerInfo; - private int displayMode; - - public ImageTracker() { - mCurrentCallerInfo = null; - displayMode = DISPLAY_UNDEFINED; - } - - /** - * Used to see if the requested call / connection has a - * different caller attached to it than the one we currently - * have in the CallCard. - */ - public boolean isDifferentImageRequest(CallerInfo ci) { - // note, since the connections are around for the lifetime of the - // call, and the CallerInfo-related items as well, we can - // definitely use a simple != comparison. - return (mCurrentCallerInfo != ci); - } - - public boolean isDifferentImageRequest(Connection connection) { - // if the connection does not exist, see if the - // mCurrentCallerInfo is also null to match. - if (connection == null) { - if (DBG) Log.d(LOG_TAG, "isDifferentImageRequest: connection is null"); - return (mCurrentCallerInfo != null); - } - Object o = connection.getUserData(); - - // if the call does NOT have a callerInfo attached - // then it is ok to query. - boolean runQuery = true; - if (o instanceof CallerInfo) { - runQuery = isDifferentImageRequest((CallerInfo) o); - } - return runQuery; - } - - /** - * Simple setter for the CallerInfo object. - */ - public void setPhotoRequest(CallerInfo ci) { - mCurrentCallerInfo = ci; - } - - /** - * Convenience method used to retrieve the URI - * representing the Photo file recorded in the attached - * CallerInfo Object. - */ - public Uri getPhotoUri() { - if (mCurrentCallerInfo != null) { - return ContentUris.withAppendedId(Contacts.CONTENT_URI, - mCurrentCallerInfo.person_id); - } - return null; - } - - /** - * Simple setter for the Photo state. - */ - public void setPhotoState(int state) { - displayMode = state; - } - - /** - * Simple getter for the Photo state. - */ - public int getPhotoState() { - return displayMode; - } - } - - /** - * Thread worker class that handles the task of opening the stream and loading - * the images. - */ - private class WorkerHandler extends Handler { - public WorkerHandler(Looper looper) { - super(looper); - } - - @Override - public void handleMessage(Message msg) { - WorkerArgs args = (WorkerArgs) msg.obj; - - switch (msg.arg1) { - case EVENT_LOAD_IMAGE: - InputStream inputStream = null; - try { - inputStream = Contacts.openContactPhotoInputStream( - args.context.getContentResolver(), args.uri, true); - } catch (Exception e) { - Log.e(LOG_TAG, "Error opening photo input stream", e); - } - - if (inputStream != null) { - args.result = Drawable.createFromStream(inputStream, args.uri.toString()); - - if (DBG) Log.d(LOG_TAG, "Loading image: " + msg.arg1 + - " token: " + msg.what + " image URI: " + args.uri); - } else { - args.result = null; - if (DBG) Log.d(LOG_TAG, "Problem with image: " + msg.arg1 + - " token: " + msg.what + " image URI: " + args.uri + - ", using default image."); - } - break; - default: - } - - // send the reply to the enclosing class. - Message reply = ContactsAsyncHelper.this.obtainMessage(msg.what); - reply.arg1 = msg.arg1; - reply.obj = msg.obj; - reply.sendToTarget(); - } - } - - /** - * Private constructor for static class - */ - private ContactsAsyncHelper() { - HandlerThread thread = new HandlerThread("ContactsAsyncWorker"); - thread.start(); - sThreadHandler = new WorkerHandler(thread.getLooper()); - } - - /** - * Convenience method for calls that do not want to deal with listeners and tokens. - */ - public static final void updateImageViewWithContactPhotoAsync(Context context, - ImageView imageView, Uri person, int placeholderImageResource) { - // Added additional Cookie field in the callee. - updateImageViewWithContactPhotoAsync (null, DEFAULT_TOKEN, null, null, context, - imageView, person, placeholderImageResource); - } - - /** - * Convenience method for calls that do not want to deal with listeners and tokens, but have - * a CallerInfo object to cache the image to. - */ - public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, Context context, - ImageView imageView, Uri person, int placeholderImageResource) { - // Added additional Cookie field in the callee. - updateImageViewWithContactPhotoAsync (info, DEFAULT_TOKEN, null, null, context, - imageView, person, placeholderImageResource); - } - - - /** - * Start an image load, attach the result to the specified CallerInfo object. - * Note, when the query is started, we make the ImageView INVISIBLE if the - * placeholderImageResource value is -1. When we're given a valid (!= -1) - * placeholderImageResource value, we make sure the image is visible. - */ - public static final void updateImageViewWithContactPhotoAsync(CallerInfo info, int token, - OnImageLoadCompleteListener listener, Object cookie, Context context, - ImageView imageView, Uri person, int placeholderImageResource) { - - // in case the source caller info is null, the URI will be null as well. - // just update using the placeholder image in this case. - if (person == null) { - if (DBG) Log.d(LOG_TAG, "target image is null, just display placeholder."); - imageView.setVisibility(View.VISIBLE); - imageView.setImageResource(placeholderImageResource); - return; - } - - // Added additional Cookie field in the callee to handle arguments - // sent to the callback function. - - // setup arguments - WorkerArgs args = new WorkerArgs(); - args.cookie = cookie; - args.context = context; - args.view = imageView; - args.uri = person; - args.defaultResource = placeholderImageResource; - args.listener = listener; - args.info = info; - - // setup message arguments - Message msg = sThreadHandler.obtainMessage(token); - msg.arg1 = EVENT_LOAD_IMAGE; - msg.obj = args; - - if (DBG) Log.d(LOG_TAG, "Begin loading image: " + args.uri + - ", displaying default image for now."); - - // set the default image first, when the query is complete, we will - // replace the image with the correct one. - if (placeholderImageResource != -1) { - imageView.setVisibility(View.VISIBLE); - imageView.setImageResource(placeholderImageResource); - } else { - imageView.setVisibility(View.INVISIBLE); - } - - // notify the thread to begin working - sThreadHandler.sendMessage(msg); - } - - /** - * Called when loading is done. - */ - @Override - public void handleMessage(Message msg) { - WorkerArgs args = (WorkerArgs) msg.obj; - switch (msg.arg1) { - case EVENT_LOAD_IMAGE: - boolean imagePresent = false; - - // if the image has been loaded then display it, otherwise set default. - // in either case, make sure the image is visible. - if (args.result != null) { - args.view.setVisibility(View.VISIBLE); - args.view.setImageDrawable((Drawable) args.result); - // make sure the cached photo data is updated. - if (args.info != null) { - args.info.cachedPhoto = (Drawable) args.result; - } - imagePresent = true; - } else if (args.defaultResource != -1) { - args.view.setVisibility(View.VISIBLE); - args.view.setImageResource(args.defaultResource); - } - - // Note that the data is cached. - if (args.info != null) { - args.info.isCachedPhotoCurrent = true; - } - - // notify the listener if it is there. - if (args.listener != null) { - if (DBG) Log.d(LOG_TAG, "Notifying listener: " + args.listener.toString() + - " image: " + args.uri + " completed"); - args.listener.onImageLoadComplete(msg.what, args.cookie, args.view, - imagePresent); - } - break; - default: - } - } -} diff --git a/core/java/android/pim/package.html b/core/java/android/pim/package.html deleted file mode 100644 index 75237c902131..000000000000 --- a/core/java/android/pim/package.html +++ /dev/null @@ -1,7 +0,0 @@ -<HTML> -<BODY> -{@hide} -Provides helpers for working with PIM (Personal Information Manager) data used -by contact lists and calendars. -</BODY> -</HTML>
\ No newline at end of file diff --git a/core/java/android/service/textservice/SpellCheckerService.java b/core/java/android/service/textservice/SpellCheckerService.java index 2b8a4582896e..cac449d9d3a8 100644 --- a/core/java/android/service/textservice/SpellCheckerService.java +++ b/core/java/android/service/textservice/SpellCheckerService.java @@ -27,6 +27,7 @@ import android.os.IBinder; import android.os.Process; import android.os.RemoteException; import android.util.Log; +import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; import android.view.textservice.TextInfo; @@ -140,19 +141,21 @@ public abstract class SpellCheckerService extends Service { /** * @hide - * The default implementation returns an array of SuggestionsInfo by simply calling + * The default implementation returns an array of SentenceSuggestionsInfo 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, + public SentenceSuggestionsInfo[] onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { final int length = textInfos.length; - final SuggestionsInfo[] retval = new SuggestionsInfo[length]; + final SentenceSuggestionsInfo[] retval = new SentenceSuggestionsInfo[length]; for (int i = 0; i < length; ++i) { - retval[i] = onGetSuggestions(textInfos[i], suggestionsLimit); - retval[i].setCookieAndSequence( - textInfos[i].getCookie(), textInfos[i].getSequence()); + final SuggestionsInfo si = onGetSuggestions(textInfos[i], suggestionsLimit); + si.setCookieAndSequence(textInfos[i].getCookie(), textInfos[i].getSequence()); + final int N = textInfos[i].getText().length(); + retval[i] = new SentenceSuggestionsInfo( + new SuggestionsInfo[] {si}, new int[]{0}, new int[]{N}); } return retval; } @@ -220,11 +223,10 @@ public abstract class SpellCheckerService extends Service { } @Override - public void onGetSuggestionsMultipleForSentence( - TextInfo[] textInfos, int suggestionsLimit) { + public void onGetSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { try { - mListener.onGetSuggestionsForSentence( - mSession.onGetSuggestionsMultipleForSentence(textInfos, suggestionsLimit)); + mListener.onGetSentenceSuggestions( + mSession.onGetSentenceSuggestionsMultiple(textInfos, suggestionsLimit)); } catch (RemoteException e) { } } diff --git a/core/java/android/view/AccessibilityNodeInfoCache.java b/core/java/android/view/AccessibilityNodeInfoCache.java index 244a49191f74..84b510d8c7ef 100644 --- a/core/java/android/view/AccessibilityNodeInfoCache.java +++ b/core/java/android/view/AccessibilityNodeInfoCache.java @@ -16,6 +16,8 @@ package android.view; +import android.os.Process; +import android.util.Log; import android.util.LongSparseArray; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityNodeInfo; @@ -30,7 +32,11 @@ import android.view.accessibility.AccessibilityNodeInfo; */ public class AccessibilityNodeInfoCache { - private final boolean ENABLED = true; + private static final String LOG_TAG = AccessibilityNodeInfoCache.class.getSimpleName(); + + private static final boolean ENABLED = true; + + private static final boolean DEBUG = false; /** * @return A new <strong>not synchronized</strong> AccessibilityNodeInfoCache. @@ -95,6 +101,7 @@ public class AccessibilityNodeInfoCache { public void onAccessibilityEvent(AccessibilityEvent event) { final int eventType = event.getEventType(); switch (eventType) { + case AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED: case AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED: case AccessibilityEvent.TYPE_VIEW_SCROLLED: clear(); @@ -117,7 +124,14 @@ public class AccessibilityNodeInfoCache { */ public AccessibilityNodeInfo get(long accessibilityNodeId) { if (ENABLED) { - return mCacheImpl.get(accessibilityNodeId); + if (DEBUG) { + AccessibilityNodeInfo info = mCacheImpl.get(accessibilityNodeId); + Log.i(LOG_TAG, "Process: " + Process.myPid() + + " get(" + accessibilityNodeId + ") = " + info); + return info; + } else { + return mCacheImpl.get(accessibilityNodeId); + } } else { return null; } @@ -131,6 +145,10 @@ public class AccessibilityNodeInfoCache { */ public void put(long accessibilityNodeId, AccessibilityNodeInfo info) { if (ENABLED) { + if (DEBUG) { + Log.i(LOG_TAG, "Process: " + Process.myPid() + + " put(" + accessibilityNodeId + ", " + info + ")"); + } mCacheImpl.put(accessibilityNodeId, info); } } @@ -156,6 +174,10 @@ public class AccessibilityNodeInfoCache { */ public void remove(long accessibilityNodeId) { if (ENABLED) { + if (DEBUG) { + Log.i(LOG_TAG, "Process: " + Process.myPid() + + " remove(" + accessibilityNodeId + ")"); + } mCacheImpl.remove(accessibilityNodeId); } } @@ -165,6 +187,9 @@ public class AccessibilityNodeInfoCache { */ public void clear() { if (ENABLED) { + if (DEBUG) { + Log.i(LOG_TAG, "Process: " + Process.myPid() + "clear()"); + } mCacheImpl.clear(); } } diff --git a/core/java/android/view/Choreographer.java b/core/java/android/view/Choreographer.java index a74b73754bac..42c391309396 100644 --- a/core/java/android/view/Choreographer.java +++ b/core/java/android/view/Choreographer.java @@ -235,8 +235,9 @@ public final class Choreographer { if (isRunningOnLooperThreadLocked()) { doScheduleVsyncLocked(); } else { - mHandler.sendMessageAtFrontOfQueue( - mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC)); + Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); + msg.setAsynchronous(true); + mHandler.sendMessageAtFrontOfQueue(msg); } } else { final long now = SystemClock.uptimeMillis(); @@ -244,7 +245,9 @@ public final class Choreographer { if (DEBUG) { Log.d(TAG, "Scheduling animation in " + (nextAnimationTime - now) + " ms."); } - mHandler.sendEmptyMessageAtTime(MSG_DO_ANIMATION, nextAnimationTime); + Message msg = mHandler.obtainMessage(MSG_DO_ANIMATION); + msg.setAsynchronous(true); + mHandler.sendMessageAtTime(msg, nextAnimationTime); } } } @@ -258,7 +261,9 @@ public final class Choreographer { if (DEBUG) { Log.d(TAG, "Scheduling draw immediately."); } - mHandler.sendEmptyMessage(MSG_DO_DRAW); + Message msg = mHandler.obtainMessage(MSG_DO_DRAW); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); } } } diff --git a/core/java/android/view/DisplayList.java b/core/java/android/view/DisplayList.java index fec0d4b94e8f..f60c8f0f5f54 100644 --- a/core/java/android/view/DisplayList.java +++ b/core/java/android/view/DisplayList.java @@ -27,6 +27,15 @@ package android.view; */ public abstract class DisplayList { /** + * Flag used when calling + * {@link HardwareCanvas#drawDisplayList(DisplayList, int, int, android.graphics.Rect, int)}. + * When this flag is set, draw operations lying outside of the bounds of the + * display list will be culled early. It is recommeneded to always set this + * flag. + */ + public static final int FLAG_CLIP_CHILDREN = 0x1; + + /** * Starts recording the display list. All operations performed on the * returned canvas are recorded and stored in this display list. * diff --git a/core/java/android/view/GLES20Canvas.java b/core/java/android/view/GLES20Canvas.java index 1e92b43a3203..f5fc708c0c9c 100644 --- a/core/java/android/view/GLES20Canvas.java +++ b/core/java/android/view/GLES20Canvas.java @@ -385,13 +385,14 @@ class GLES20Canvas extends HardwareCanvas { private static native void nSetDisplayListName(int displayList, String name); @Override - public boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty) { - return nDrawDisplayList(mRenderer, - ((GLES20DisplayList) displayList).getNativeDisplayList(), width, height, dirty); + public boolean drawDisplayList(DisplayList displayList, int width, int height, + Rect dirty, int flags) { + return nDrawDisplayList(mRenderer, ((GLES20DisplayList) displayList).getNativeDisplayList(), + width, height, dirty, flags); } private static native boolean nDrawDisplayList(int renderer, int displayList, - int width, int height, Rect dirty); + int width, int height, Rect dirty, int flags); @Override void outputDisplayList(DisplayList displayList) { @@ -407,9 +408,12 @@ class GLES20Canvas extends HardwareCanvas { void drawHardwareLayer(HardwareLayer layer, float x, float y, Paint paint) { final GLES20Layer glLayer = (GLES20Layer) layer; int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint); - if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + nDrawLayer(mRenderer, glLayer.getLayer(), x, y, nativePaint); + } finally { + if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + } } private static native void nDrawLayer(int renderer, int layer, float x, float y, int paint); @@ -607,10 +611,14 @@ class GLES20Canvas extends HardwareCanvas { return saveLayer(bounds.left, bounds.top, bounds.right, bounds.bottom, paint, saveFlags); } + int count; int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - int count = nSaveLayer(mRenderer, nativePaint, saveFlags); - if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + count = nSaveLayer(mRenderer, nativePaint, saveFlags); + } finally { + if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + } return count; } @@ -620,10 +628,14 @@ class GLES20Canvas extends HardwareCanvas { public int saveLayer(float left, float top, float right, float bottom, Paint paint, int saveFlags) { if (left < right && top < bottom) { + int count; int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - int count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags); - if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + count = nSaveLayer(mRenderer, left, top, right, bottom, nativePaint, saveFlags); + } finally { + if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + } return count; } return save(saveFlags); @@ -707,9 +719,12 @@ class GLES20Canvas extends HardwareCanvas { public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint) { int modifiers = setupModifiers(paint); - nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, - useCenter, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawArc(mRenderer, oval.left, oval.top, oval.right, oval.bottom, + startAngle, sweepAngle, useCenter, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawArc(int renderer, float left, float top, @@ -726,10 +741,13 @@ class GLES20Canvas extends HardwareCanvas { if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps"); // Shaders are ignored when drawing patches int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks, - dst.left, dst.top, dst.right, dst.bottom, nativePaint); - if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + nDrawPatch(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, chunks, + dst.left, dst.top, dst.right, dst.bottom, nativePaint); + } finally { + if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + } } private static native void nDrawPatch(int renderer, int bitmap, byte[] buffer, byte[] chunks, @@ -740,9 +758,12 @@ class GLES20Canvas extends HardwareCanvas { if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps"); // Shaders are ignored when drawing bitmaps int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, nativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawBitmap( @@ -753,10 +774,13 @@ class GLES20Canvas extends HardwareCanvas { if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps"); // Shaders are ignored when drawing bitmaps int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, - matrix.native_instance, nativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, + matrix.native_instance, nativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawBitmap(int renderer, int bitmap, byte[] buff, @@ -767,23 +791,26 @@ class GLES20Canvas extends HardwareCanvas { if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps"); // Shaders are ignored when drawing bitmaps int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; - int left, top, right, bottom; - if (src == null) { - left = top = 0; - right = bitmap.getWidth(); - bottom = bitmap.getHeight(); - } else { - left = src.left; - right = src.right; - top = src.top; - bottom = src.bottom; - } + int left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } @Override @@ -791,23 +818,26 @@ class GLES20Canvas extends HardwareCanvas { if (bitmap.isRecycled()) throw new IllegalArgumentException("Cannot draw recycled bitmaps"); // Shaders are ignored when drawing bitmaps int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - - float left, top, right, bottom; - if (src == null) { - left = top = 0; - right = bitmap.getWidth(); - bottom = bitmap.getHeight(); - } else { - left = src.left; - right = src.right; - top = src.top; - bottom = src.bottom; + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + + float left, top, right, bottom; + if (src == null) { + left = top = 0; + right = bitmap.getWidth(); + bottom = bitmap.getHeight(); + } else { + left = src.left; + right = src.right; + top = src.top; + bottom = src.bottom; + } + + nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom, + dst.left, dst.top, dst.right, dst.bottom, nativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } - - nDrawBitmap(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, left, top, right, bottom, - dst.left, dst.top, dst.right, dst.bottom, nativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } private static native void nDrawBitmap(int renderer, int bitmap, byte[] buffer, @@ -819,12 +849,15 @@ class GLES20Canvas extends HardwareCanvas { int width, int height, boolean hasAlpha, Paint paint) { // Shaders are ignored when drawing bitmaps int modifier = paint != null ? setupColorFilter(paint) : MODIFIER_NONE; - final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; - final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config); - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint); - b.recycle(); - if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + try { + final Bitmap.Config config = hasAlpha ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565; + final Bitmap b = Bitmap.createBitmap(colors, offset, stride, width, height, config); + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + nDrawBitmap(mRenderer, b.mNativeBitmap, b.mBuffer, x, y, nativePaint); + b.recycle(); + } finally { + if (modifier != MODIFIER_NONE) nResetModifiers(mRenderer, modifier); + } } @Override @@ -854,10 +887,13 @@ class GLES20Canvas extends HardwareCanvas { colorOffset = 0; int modifiers = paint != null ? setupModifiers(bitmap, paint) : MODIFIER_NONE; - final int nativePaint = paint == null ? 0 : paint.mNativePaint; - nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight, - verts, vertOffset, colors, colorOffset, nativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + final int nativePaint = paint == null ? 0 : paint.mNativePaint; + nDrawBitmapMesh(mRenderer, bitmap.mNativeBitmap, bitmap.mBuffer, meshWidth, meshHeight, + verts, vertOffset, colors, colorOffset, nativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawBitmapMesh(int renderer, int bitmap, byte[] buffer, @@ -867,8 +903,11 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawCircle(float cx, float cy, float radius, Paint paint) { int modifiers = setupModifiers(paint); - nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawCircle(mRenderer, cx, cy, radius, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawCircle(int renderer, float cx, float cy, @@ -901,8 +940,11 @@ class GLES20Canvas extends HardwareCanvas { throw new IllegalArgumentException("The lines array must contain 4 elements per line."); } int modifiers = setupModifiers(paint); - nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawLines(mRenderer, pts, offset, count, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawLines(int renderer, float[] points, @@ -916,8 +958,11 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawOval(RectF oval, Paint paint) { int modifiers = setupModifiers(paint); - nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawOval(mRenderer, oval.left, oval.top, oval.right, oval.bottom, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawOval(int renderer, float left, float top, @@ -933,14 +978,17 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPath(Path path, Paint paint) { int modifiers = setupModifiers(paint); - if (path.isSimplePath) { - if (path.rects != null) { - nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint); + try { + if (path.isSimplePath) { + if (path.rects != null) { + nDrawRects(mRenderer, path.rects.mNativeRegion, paint.mNativePaint); + } + } else { + nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint); } - } else { - nDrawPath(mRenderer, path.mNativePath, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); } private static native void nDrawPath(int renderer, int path, int paint); @@ -1001,8 +1049,11 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawPoints(float[] pts, int offset, int count, Paint paint) { int modifiers = setupModifiers(paint); - nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawPoints(mRenderer, pts, offset, count, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawPoints(int renderer, float[] points, @@ -1047,8 +1098,11 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawRect(float left, float top, float right, float bottom, Paint paint) { int modifiers = setupModifiers(paint); - nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawRect(mRenderer, left, top, right, bottom, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawRect(int renderer, float left, float top, @@ -1072,9 +1126,12 @@ class GLES20Canvas extends HardwareCanvas { @Override public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { int modifiers = setupModifiers(paint); - nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, - rx, ry, paint.mNativePaint); - if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + try { + nDrawRoundRect(mRenderer, rect.left, rect.top, rect.right, rect.bottom, + rx, ry, paint.mNativePaint); + } finally { + if (modifiers != MODIFIER_NONE) nResetModifiers(mRenderer, modifiers); + } } private static native void nDrawRoundRect(int renderer, float left, float top, @@ -1223,17 +1280,17 @@ class GLES20Canvas extends HardwareCanvas { } private int setupModifiers(Bitmap b, Paint paint) { - if (b.getConfig() == Bitmap.Config.ALPHA_8) { - return setupModifiers(paint); - } + if (b.getConfig() != Bitmap.Config.ALPHA_8) { + final ColorFilter filter = paint.getColorFilter(); + if (filter != null) { + nSetupColorFilter(mRenderer, filter.nativeColorFilter); + return MODIFIER_COLOR_FILTER; + } - final ColorFilter filter = paint.getColorFilter(); - if (filter != null) { - nSetupColorFilter(mRenderer, filter.nativeColorFilter); - return MODIFIER_COLOR_FILTER; + return MODIFIER_NONE; + } else { + return setupModifiers(paint); } - - return MODIFIER_NONE; } private int setupModifiers(Paint paint) { diff --git a/core/java/android/view/GLES20RecordingCanvas.java b/core/java/android/view/GLES20RecordingCanvas.java index c987f487d30b..c9ba65f3ab40 100644 --- a/core/java/android/view/GLES20RecordingCanvas.java +++ b/core/java/android/view/GLES20RecordingCanvas.java @@ -143,8 +143,8 @@ class GLES20RecordingCanvas extends GLES20Canvas implements Poolable<GLES20Recor @Override public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint) { - super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, colors, colorOffset, - paint); + super.drawBitmapMesh(bitmap, meshWidth, meshHeight, verts, vertOffset, + colors, colorOffset, paint); mDisplayList.mBitmaps.add(bitmap); // Shaders in the Paint are ignored when drawing a Bitmap } diff --git a/core/java/android/view/HardwareCanvas.java b/core/java/android/view/HardwareCanvas.java index cbdbbde45b70..838c03c96dd7 100644 --- a/core/java/android/view/HardwareCanvas.java +++ b/core/java/android/view/HardwareCanvas.java @@ -57,11 +57,14 @@ public abstract class HardwareCanvas extends Canvas { * @param height The height of the display list. * @param dirty The dirty region to redraw in the next pass, matters only * if this method returns true, can be null. + * @param flags Optional flags about drawing, see {@link DisplayList} for + * the possible flags. * * @return True if the content of the display list requires another * drawing pass (invalidate()), false otherwise */ - public abstract boolean drawDisplayList(DisplayList displayList, int width, int height, Rect dirty); + public abstract boolean drawDisplayList(DisplayList displayList, int width, int height, + Rect dirty, int flags); /** * Outputs the specified display list to the log. This method exists for use by diff --git a/core/java/android/view/HardwareRenderer.java b/core/java/android/view/HardwareRenderer.java index 67466a4f60b9..ec9586308568 100644 --- a/core/java/android/view/HardwareRenderer.java +++ b/core/java/android/view/HardwareRenderer.java @@ -969,7 +969,8 @@ public abstract class HardwareRenderer { } boolean invalidateNeeded = canvas.drawDisplayList(displayList, - view.getWidth(), view.getHeight(), mRedrawClip); + view.getWidth(), view.getHeight(), mRedrawClip, + DisplayList.FLAG_CLIP_CHILDREN); if (mProfileEnabled) { long now = System.nanoTime(); diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java index 6726c56eb581..c658a80323d7 100644 --- a/core/java/android/view/SurfaceView.java +++ b/core/java/android/view/SurfaceView.java @@ -384,7 +384,7 @@ public class SurfaceView extends View { if (!mHaveFrame) { return; } - ViewRootImpl viewRoot = (ViewRootImpl) getRootView().getParent(); + ViewRootImpl viewRoot = getViewRootImpl(); if (viewRoot != null) { mTranslator = viewRoot.mTranslator; } diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java index 363c30d233fa..0675a7493e94 100644 --- a/core/java/android/view/View.java +++ b/core/java/android/view/View.java @@ -601,6 +601,8 @@ import java.util.concurrent.CopyOnWriteArrayList; * @attr ref android.R.styleable#View_paddingLeft * @attr ref android.R.styleable#View_paddingRight * @attr ref android.R.styleable#View_paddingTop + * @attr ref android.R.styleable#View_paddingStart + * @attr ref android.R.styleable#View_paddingEnd * @attr ref android.R.styleable#View_saveEnabled * @attr ref android.R.styleable#View_rotation * @attr ref android.R.styleable#View_rotationX @@ -1757,6 +1759,16 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal static final int LAYOUT_DIRECTION_RESOLVED = 0x00000008; + /** + * Indicates that the view is tracking some sort of transient state + * that the app should not need to be aware of, but that the framework + * should take special care to preserve. + * + * @hide + */ + static final int HAS_TRANSIENT_STATE = 0x00000010; + + /* End of masks for mPrivateFlags2 */ static final int DRAG_MASK = DRAG_CAN_ACCEPT | DRAG_HOVERED; @@ -3619,7 +3631,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @see ActionMode */ public ActionMode startActionMode(ActionMode.Callback callback) { - return getParent().startActionModeForChild(this, callback); + ViewParent parent = getParent(); + if (parent == null) return null; + return parent.startActionModeForChild(this, callback); } /** @@ -4887,6 +4901,43 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Indicates whether the view is currently tracking transient state that the + * app should not need to concern itself with saving and restoring, but that + * the framework should take special note to preserve when possible. + * + * @return true if the view has transient state + * + * @hide + */ + @ViewDebug.ExportedProperty(category = "layout") + public boolean hasTransientState() { + return (mPrivateFlags2 & HAS_TRANSIENT_STATE) == HAS_TRANSIENT_STATE; + } + + /** + * Set whether this view is currently tracking transient state that the + * framework should attempt to preserve when possible. + * + * @param hasTransientState true if this view has transient state + * + * @hide + */ + public void setHasTransientState(boolean hasTransientState) { + if (hasTransientState() == hasTransientState) return; + + mPrivateFlags2 = (mPrivateFlags2 & ~HAS_TRANSIENT_STATE) | + (hasTransientState ? HAS_TRANSIENT_STATE : 0); + if (mParent != null) { + try { + mParent.childHasTransientStateChanged(this, hasTransientState); + } catch (AbstractMethodError e) { + Log.e(VIEW_LOG_TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } + } + } + + /** * If this view doesn't do any drawing on its own, set this flag to * allow further optimizations. By default, this flag is not set on * View, but could be set on some View subclasses such as ViewGroup. @@ -4997,6 +5048,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * the View's internal state from a previously set "pressed" state. */ public void setPressed(boolean pressed) { + if (pressed == ((mPrivateFlags & PRESSED) == PRESSED)) { + return; + } + if (pressed) { mPrivateFlags |= PRESSED; } else { @@ -5440,12 +5495,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal return true; } - /** Gets the ViewAncestor, or null if not attached. */ - /*package*/ ViewRootImpl getViewRootImpl() { - View root = getRootView(); - return root != null ? (ViewRootImpl)root.getParent() : null; - } - /** * Call this to try to give focus to a specific view or to one of its descendants. This is a * special variant of {@link #requestFocus() } that will allow views that are not focuable in @@ -6505,8 +6554,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal if ((viewFlags & ENABLED_MASK) == DISABLED) { if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PRESSED) != 0) { - mPrivateFlags &= ~PRESSED; - refreshDrawableState(); + setPressed(false); } // A disabled view that is clickable still consumes the touch // events, it just doesn't respond to them. @@ -6538,8 +6586,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal // showed it as pressed. Make it show the pressed // state now (before scheduling the click) to ensure // the user sees it. - mPrivateFlags |= PRESSED; - refreshDrawableState(); + setPressed(true); } if (!mHasPerformedLongPress) { @@ -6595,15 +6642,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout()); } else { // Not inside a scrolling container, so show the feedback right away - mPrivateFlags |= PRESSED; - refreshDrawableState(); + setPressed(true); checkForLongClick(0); } break; case MotionEvent.ACTION_CANCEL: - mPrivateFlags &= ~PRESSED; - refreshDrawableState(); + setPressed(false); removeTapCallback(); break; @@ -6619,9 +6664,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal // Remove any future long press/tap checks removeLongPressCallback(); - // Need to switch from pressed to not pressed - mPrivateFlags &= ~PRESSED; - refreshDrawableState(); + setPressed(false); } } break; @@ -8683,6 +8726,18 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** + * Gets the view root associated with the View. + * @return The view root, or null if none. + * @hide + */ + public ViewRootImpl getViewRootImpl() { + if (mAttachInfo != null) { + return mAttachInfo.mViewRootImpl; + } + return null; + } + + /** * <p>Causes the Runnable to be added to the message queue. * The runnable will be run on the user interface thread.</p> * @@ -8696,17 +8751,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * looper processing the message queue is exiting. */ public boolean post(Runnable action) { - Handler handler; - AttachInfo attachInfo = mAttachInfo; + final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - handler = attachInfo.mHandler; - } else { - // Assume that post will succeed later - ViewRootImpl.getRunQueue().post(action); - return true; + return attachInfo.mHandler.post(action); } - - return handler.post(action); + // Assume that post will succeed later + ViewRootImpl.getRunQueue().post(action); + return true; } /** @@ -8729,17 +8780,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * occurs then the message will be dropped. */ public boolean postDelayed(Runnable action, long delayMillis) { - Handler handler; - AttachInfo attachInfo = mAttachInfo; + final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - handler = attachInfo.mHandler; - } else { - // Assume that post will succeed later - ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); - return true; + return attachInfo.mHandler.postDelayed(action, delayMillis); } - - return handler.postDelayed(action, delayMillis); + // Assume that post will succeed later + ViewRootImpl.getRunQueue().postDelayed(action, delayMillis); + return true; } /** @@ -8756,17 +8803,13 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * (for instance, if the Runnable was not in the queue already.) */ public boolean removeCallbacks(Runnable action) { - Handler handler; - AttachInfo attachInfo = mAttachInfo; + final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - handler = attachInfo.mHandler; + attachInfo.mHandler.removeCallbacks(action); } else { // Assume that post will succeed later ViewRootImpl.getRunQueue().removeCallbacks(action); - return true; } - - handler.removeCallbacks(action); return true; } @@ -8815,12 +8858,9 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal public void postInvalidateDelayed(long delayMilliseconds) { // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window - AttachInfo attachInfo = mAttachInfo; + final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { - Message msg = Message.obtain(); - msg.what = AttachInfo.INVALIDATE_MSG; - msg.obj = this; - attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); + attachInfo.mViewRootImpl.dispatchInvalidateDelayed(this, delayMilliseconds); } } @@ -8843,7 +8883,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal // We try only with the AttachInfo because there's no point in invalidating // if we are not attached to our window - AttachInfo attachInfo = mAttachInfo; + final AttachInfo attachInfo = mAttachInfo; if (attachInfo != null) { final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire(); info.target = this; @@ -8852,10 +8892,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal info.right = right; info.bottom = bottom; - final Message msg = Message.obtain(); - msg.what = AttachInfo.INVALIDATE_RECT_MSG; - msg.obj = info; - attachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds); + attachInfo.mViewRootImpl.dispatchInvalidateRectDelayed(info, delayMilliseconds); } } @@ -9540,10 +9577,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal // Clear any previous layout direction resolution mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED_RTL; - // Reset also TextDirection as a change into LayoutDirection may impact the selected - // TextDirectionHeuristic - resetResolvedTextDirection(); - // Set resolved depending on layout direction switch (getLayoutDirection()) { case LAYOUT_DIRECTION_INHERIT: @@ -9580,14 +9613,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** - * @hide + * Force padding depending on layout direction. */ - protected void resolvePadding() { + public void resolvePadding() { // If the user specified the absolute padding (either with android:padding or // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise // use the default padding or the padding from the background drawable // (stored at this point in mPadding*) - switch (getResolvedLayoutDirection()) { + int resolvedLayoutDirection = getResolvedLayoutDirection(); + switch (resolvedLayoutDirection) { case LAYOUT_DIRECTION_RTL: // Start user padding override Right user padding. Otherwise, if Right user // padding is not defined, use the default Right padding. If Right user padding @@ -9623,6 +9657,18 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal mUserPaddingBottom = (mUserPaddingBottom >= 0) ? mUserPaddingBottom : mPaddingBottom; recomputePadding(); + onResolvePadding(resolvedLayoutDirection); + } + + /** + * Resolve padding depending on the layout direction. Subclasses that care about + * padding resolution should override this method. The default implementation does + * nothing. + * + * @param layoutDirection the direction of the layout + * + */ + public void onResolvePadding(int layoutDirection) { } /** @@ -9649,8 +9695,10 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @hide */ protected void resetResolvedLayoutDirection() { - // Reset the current View resolution + // Reset the layout direction resolution mPrivateFlags2 &= ~LAYOUT_DIRECTION_RESOLVED; + // Reset also the text direction + resetResolvedTextDirection(); } /** @@ -9689,13 +9737,12 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } if (mAttachInfo != null) { - mAttachInfo.mHandler.removeMessages(AttachInfo.INVALIDATE_MSG, this); + mAttachInfo.mViewRootImpl.cancelInvalidate(this); } mCurrentAnimation = null; resetResolvedLayoutDirection(); - resetResolvedTextDirection(); } /** @@ -11209,7 +11256,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } else { mPrivateFlags &= ~DIRTY_MASK; ((HardwareCanvas) canvas).drawDisplayList(displayList, - mRight - mLeft, mBottom - mTop, null); + mRight - mLeft, mBottom - mTop, null, flags); } } } else if (cache != null) { @@ -13008,11 +13055,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT); } - if (getAccessibilityNodeProvider() != null) { - throw new IllegalStateException("Views with AccessibilityNodeProvider" - + " can't have children."); - } - mPrivateFlags |= FORCE_LAYOUT; mPrivateFlags |= INVALIDATED; @@ -14070,7 +14112,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE}, - * */ public int getTextDirection() { return mTextDirection; @@ -14087,7 +14128,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE}, - * */ public void setTextDirection(int textDirection) { if (textDirection != mTextDirection) { @@ -14107,7 +14147,6 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * {@link #TEXT_DIRECTION_LTR}, * {@link #TEXT_DIRECTION_RTL}, * {@link #TEXT_DIRECTION_LOCALE}, - * */ public int getResolvedTextDirection() { if (mResolvedTextDirection == TEXT_DIRECTION_INHERIT) { @@ -14117,27 +14156,47 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal } /** - * Resolve the text direction. - * + * Resolve the text direction. Will call {@link View#onResolveTextDirection()} when resolution + * is done. */ - protected void resolveTextDirection() { - if (mTextDirection != TEXT_DIRECTION_INHERIT) { - mResolvedTextDirection = mTextDirection; + public void resolveTextDirection() { + if (mResolvedTextDirection != TEXT_DIRECTION_INHERIT) { + // Resolution has already been done. return; } - if (mParent != null && mParent instanceof ViewGroup) { + if (mTextDirection != TEXT_DIRECTION_INHERIT) { + mResolvedTextDirection = mTextDirection; + } else if (mParent != null && mParent instanceof ViewGroup) { mResolvedTextDirection = ((ViewGroup) mParent).getResolvedTextDirection(); - return; + } else { + mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG; } - mResolvedTextDirection = TEXT_DIRECTION_FIRST_STRONG; + onResolveTextDirection(); } /** - * Reset resolved text direction. Will be resolved during a call to getResolvedTextDirection(). - * + * Called when text direction has been resolved. Subclasses that care about text direction + * resolution should override this method. The default implementation does nothing. */ - protected void resetResolvedTextDirection() { + public void onResolveTextDirection() { + } + + /** + * Reset resolved text direction. Text direction can be resolved with a call to + * getResolvedTextDirection(). Will call {@link View#onResetResolvedTextDirection()} when + * reset is done. + */ + public void resetResolvedTextDirection() { mResolvedTextDirection = TEXT_DIRECTION_INHERIT; + onResetResolvedTextDirection(); + } + + /** + * Called when text direction is reset. Subclasses that care about text direction reset should + * override this method and do a reset of the text direction of their children. The default + * implementation does nothing. + */ + public void onResetResolvedTextDirection() { } // @@ -14441,8 +14500,7 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal private final class CheckForTap implements Runnable { public void run() { mPrivateFlags &= ~PREPRESSED; - mPrivateFlags |= PRESSED; - refreshDrawableState(); + setPressed(true); checkForLongClick(ViewConfiguration.getTapTimeout()); } } @@ -14962,22 +15020,15 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal Canvas mCanvas; /** - * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This - * handler can be used to pump events in the UI events queue. - */ - final Handler mHandler; - - /** - * Identifier for messages requesting the view to be invalidated. - * Such messages should be sent to {@link #mHandler}. + * The view root impl. */ - static final int INVALIDATE_MSG = 0x1; + final ViewRootImpl mViewRootImpl; /** - * Identifier for messages requesting the view to invalidate a region. - * Such messages should be sent to {@link #mHandler}. + * A Handler supplied by a view's {@link android.view.ViewRootImpl}. This + * handler can be used to pump events in the UI events queue. */ - static final int INVALIDATE_RECT_MSG = 0x2; + final Handler mHandler; /** * Temporary for use in computing invalidate rectangles while @@ -15007,10 +15058,11 @@ public class View implements Drawable.Callback, Drawable.Callback2, KeyEvent.Cal * @param handler the events handler the view must use */ AttachInfo(IWindowSession session, IWindow window, - Handler handler, Callbacks effectPlayer) { + ViewRootImpl viewRootImpl, Handler handler, Callbacks effectPlayer) { mSession = session; mWindow = window; mWindowToken = window.asBinder(); + mViewRootImpl = viewRootImpl; mHandler = handler; mRootCallbacks = effectPlayer; } diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java index c1db5720e2f1..2a178451df9e 100644 --- a/core/java/android/view/ViewDebug.java +++ b/core/java/android/view/ViewDebug.java @@ -375,7 +375,7 @@ public class ViewDebug { } private static BufferedWriter sHierarchyTraces; - private static ViewRootImpl sHierarhcyRoot; + private static ViewRootImpl sHierarchyRoot; private static String sHierarchyTracePrefix; /** @@ -855,7 +855,7 @@ public class ViewDebug { return; } - if (sHierarhcyRoot != null) { + if (sHierarchyRoot != null) { throw new IllegalStateException("You must call stopHierarchyTracing() before running" + " a new trace!"); } @@ -874,7 +874,7 @@ public class ViewDebug { return; } - sHierarhcyRoot = (ViewRootImpl) view.getRootView().getParent(); + sHierarchyRoot = view.getViewRootImpl(); } /** @@ -896,7 +896,7 @@ public class ViewDebug { return; } - if (sHierarhcyRoot == null || sHierarchyTraces == null) { + if (sHierarchyRoot == null || sHierarchyTraces == null) { throw new IllegalStateException("You must call startHierarchyTracing() before" + " stopHierarchyTracing()!"); } @@ -921,7 +921,7 @@ public class ViewDebug { return; } - View view = sHierarhcyRoot.getView(); + View view = sHierarchyRoot.getView(); if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; dumpViewHierarchy(group, out, 0); @@ -932,7 +932,7 @@ public class ViewDebug { } } - sHierarhcyRoot = null; + sHierarchyRoot = null; } static void dispatchCommand(View view, String command, String parameters, diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index e6a833494284..2848e8802927 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -77,6 +77,7 @@ import java.util.HashSet; * @attr ref android.R.styleable#ViewGroup_animateLayoutChanges */ public abstract class ViewGroup extends View implements ViewParent, ViewManager { + private static final String TAG = "ViewGroup"; private static final boolean DBG = false; @@ -168,6 +169,11 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager */ protected int mGroupFlags; + /** + * NOTE: If you change the flags below make sure to reflect the changes + * the DisplayList class + */ + // When set, ViewGroup invalidates only the child's rectangle // Set by default static final int FLAG_CLIP_CHILDREN = 0x1; @@ -370,6 +376,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager @ViewDebug.ExportedProperty(category = "drawing") boolean mDrawLayers = true; + // Indicates how many of this container's child subtrees contain transient state + @ViewDebug.ExportedProperty(category = "layout") + private int mChildCountWithTransientState = 0; + public ViewGroup(Context context) { super(context); initViewGroup(); @@ -648,6 +658,38 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } /** + * Called when a child view has changed whether or not it is tracking transient state. + * + * @hide + */ + public void childHasTransientStateChanged(View child, boolean childHasTransientState) { + final boolean oldHasTransientState = hasTransientState(); + if (childHasTransientState) { + mChildCountWithTransientState++; + } else { + mChildCountWithTransientState--; + } + + final boolean newHasTransientState = hasTransientState(); + if (mParent != null && oldHasTransientState != newHasTransientState) { + try { + mParent.childHasTransientStateChanged(this, newHasTransientState); + } catch (AbstractMethodError e) { + Log.e(TAG, mParent.getClass().getSimpleName() + + " does not fully implement ViewParent", e); + } + } + } + + /** + * @hide + */ + @Override + public boolean hasTransientState() { + return mChildCountWithTransientState > 0 || super.hasTransientState(); + } + + /** * {@inheritDoc} */ @Override @@ -2627,6 +2669,15 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager return child.draw(canvas, this, drawingTime); } + @Override + public void requestLayout() { + if (mChildrenCount > 0 && getAccessibilityNodeProvider() != null) { + throw new IllegalStateException("Views with AccessibilityNodeProvider" + + " can't have children."); + } + super.requestLayout(); + } + /** * * @param enabled True if children should be drawn with layers, false otherwise. @@ -3094,6 +3145,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) { mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE; } + + if (child.hasTransientState()) { + childHasTransientStateChanged(child, true); + } } private void addInArray(View child, int index) { @@ -3290,6 +3345,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager view.dispatchDetachedFromWindow(); } + if (view.hasTransientState()) { + childHasTransientStateChanged(view, false); + } + onViewRemoved(view); needGlobalAttributesUpdate(false); @@ -3361,6 +3420,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager view.dispatchDetachedFromWindow(); } + if (view.hasTransientState()) { + childHasTransientStateChanged(view, false); + } + needGlobalAttributesUpdate(false); onViewRemoved(view); @@ -3426,6 +3489,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager view.dispatchDetachedFromWindow(); } + if (view.hasTransientState()) { + childHasTransientStateChanged(view, false); + } + onViewRemoved(view); view.mParent = null; @@ -3466,6 +3533,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager child.dispatchDetachedFromWindow(); } + if (child.hasTransientState()) { + childHasTransientStateChanged(child, false); + } + onViewRemoved(child); } @@ -3727,7 +3798,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager RectF boundingRect = attachInfo.mTmpTransformRect; boundingRect.set(dirty); m.mapRect(boundingRect); - dirty.set((int) boundingRect.left, (int) boundingRect.top, + dirty.set((int) (boundingRect.left - 0.5f), + (int) (boundingRect.top - 0.5f), (int) (boundingRect.right + 0.5f), (int) (boundingRect.bottom + 0.5f)); } @@ -4857,9 +4929,7 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager } @Override - protected void resetResolvedTextDirection() { - super.resetResolvedTextDirection(); - + public void onResetResolvedTextDirection() { // Take care of resetting the children resolution too final int count = getChildCount(); for (int i = 0; i < count; i++) { diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java index 873d4bbd63fd..8395f1ba4b8a 100644 --- a/core/java/android/view/ViewParent.java +++ b/core/java/android/view/ViewParent.java @@ -261,4 +261,14 @@ public interface ViewParent { * @return True if the event was sent. */ public boolean requestSendAccessibilityEvent(View child, AccessibilityEvent event); + + /** + * Called when a child view now has or no longer is tracking transient state. + * + * @param child Child view whose state has changed + * @param hasTransientState true if this child has transient state + * + * @hide + */ + public void childHasTransientStateChanged(View child, boolean hasTransientState); } diff --git a/core/java/android/view/ViewRootImpl.java b/core/java/android/view/ViewRootImpl.java index fbcb423dbfe7..28737fcaa091 100644 --- a/core/java/android/view/ViewRootImpl.java +++ b/core/java/android/view/ViewRootImpl.java @@ -61,6 +61,7 @@ import android.util.PoolableManager; import android.util.Pools; import android.util.Slog; import android.util.TypedValue; +import android.view.View.AttachInfo; import android.view.View.MeasureSpec; import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityInteractionClient; @@ -96,7 +97,7 @@ import java.util.List; * {@hide} */ @SuppressWarnings({"EmptyCatchBlock", "PointlessBooleanExpression"}) -public final class ViewRootImpl extends Handler implements ViewParent, +public final class ViewRootImpl implements ViewParent, View.AttachInfo.Callbacks, HardwareRenderer.HardwareDrawCallbacks { private static final String TAG = "ViewRootImpl"; private static final boolean DBG = false; @@ -212,6 +213,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, final Rect mVisRect; // used to retrieve visible rect of focused view. boolean mTraversalScheduled; + int mTraversalBarrier; long mLastTraversalFinishedTimeNanos; long mLastDrawFinishedTimeNanos; boolean mWillDrawSoon; @@ -379,7 +381,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, new AccessibilityInteractionConnectionManager(); mAccessibilityManager.addAccessibilityStateChangeListener( mAccessibilityInteractionConnectionManager); - mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this); + mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, mHandler, this); mViewConfiguration = ViewConfiguration.get(context); mDensity = context.getResources().getDisplayMetrics().densityDpi; mFallbackEventHandler = PolicyManager.makeNewFallbackEventHandler(context); @@ -838,22 +840,28 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; + mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); scheduleFrame(); } } public void unscheduleTraversals() { - mTraversalScheduled = false; + if (mTraversalScheduled) { + mTraversalScheduled = false; + mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); + } } void scheduleFrame() { if (!mFrameScheduled) { - mChoreographer.postDrawCallback(mFrameRunnable); mFrameScheduled = true; + mChoreographer.postDrawCallback(mFrameRunnable); } } void unscheduleFrame() { + unscheduleTraversals(); + if (mFrameScheduled) { mFrameScheduled = false; mChoreographer.removeDrawCallback(mFrameRunnable); @@ -868,6 +876,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (mTraversalScheduled) { mTraversalScheduled = false; + mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); doTraversal(); } } @@ -1929,7 +1938,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, sFirstDrawComplete = true; final int count = sFirstDrawHandlers.size(); for (int i = 0; i< count; i++) { - post(sFirstDrawHandlers.get(i)); + mHandler.post(sFirstDrawHandlers.get(i)); } } } @@ -2441,283 +2450,289 @@ public final class ViewRootImpl extends Handler implements ViewParent, } } - public final static int DIE = 1001; - public final static int RESIZED = 1002; - public final static int RESIZED_REPORT = 1003; - public final static int WINDOW_FOCUS_CHANGED = 1004; - public final static int DISPATCH_KEY = 1005; - public final static int DISPATCH_APP_VISIBILITY = 1008; - public final static int DISPATCH_GET_NEW_SURFACE = 1009; - public final static int IME_FINISHED_EVENT = 1010; - public final static int DISPATCH_KEY_FROM_IME = 1011; - public final static int FINISH_INPUT_CONNECTION = 1012; - public final static int CHECK_FOCUS = 1013; - public final static int CLOSE_SYSTEM_DIALOGS = 1014; - public final static int DISPATCH_DRAG_EVENT = 1015; - public final static int DISPATCH_DRAG_LOCATION_EVENT = 1016; - public final static int DISPATCH_SYSTEM_UI_VISIBILITY = 1017; - public final static int DISPATCH_GENERIC_MOTION = 1018; - public final static int UPDATE_CONFIGURATION = 1019; - public final static int DO_PERFORM_ACCESSIBILITY_ACTION = 1020; - public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 1021; - public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 1022; - public final static int DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 1023; - public final static int DO_PROCESS_INPUT_EVENTS = 1024; - - @Override - public String getMessageName(Message message) { - switch (message.what) { - case DIE: - return "DIE"; - case RESIZED: - return "RESIZED"; - case RESIZED_REPORT: - return "RESIZED_REPORT"; - case WINDOW_FOCUS_CHANGED: - return "WINDOW_FOCUS_CHANGED"; - case DISPATCH_KEY: - return "DISPATCH_KEY"; - case DISPATCH_APP_VISIBILITY: - return "DISPATCH_APP_VISIBILITY"; - case DISPATCH_GET_NEW_SURFACE: - return "DISPATCH_GET_NEW_SURFACE"; - case IME_FINISHED_EVENT: - return "IME_FINISHED_EVENT"; - case DISPATCH_KEY_FROM_IME: - return "DISPATCH_KEY_FROM_IME"; - case FINISH_INPUT_CONNECTION: - return "FINISH_INPUT_CONNECTION"; - case CHECK_FOCUS: - return "CHECK_FOCUS"; - case CLOSE_SYSTEM_DIALOGS: - return "CLOSE_SYSTEM_DIALOGS"; - case DISPATCH_DRAG_EVENT: - return "DISPATCH_DRAG_EVENT"; - case DISPATCH_DRAG_LOCATION_EVENT: - return "DISPATCH_DRAG_LOCATION_EVENT"; - case DISPATCH_SYSTEM_UI_VISIBILITY: - return "DISPATCH_SYSTEM_UI_VISIBILITY"; - case DISPATCH_GENERIC_MOTION: - return "DISPATCH_GENERIC_MOTION"; - case UPDATE_CONFIGURATION: - return "UPDATE_CONFIGURATION"; - case DO_PERFORM_ACCESSIBILITY_ACTION: - return "DO_PERFORM_ACCESSIBILITY_ACTION"; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: - return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: - return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: - return "DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT"; - case DO_PROCESS_INPUT_EVENTS: - return "DO_PROCESS_INPUT_EVENTS"; - } - return super.getMessageName(message); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case View.AttachInfo.INVALIDATE_MSG: - ((View) msg.obj).invalidate(); - break; - case View.AttachInfo.INVALIDATE_RECT_MSG: - final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; - info.target.invalidate(info.left, info.top, info.right, info.bottom); - info.release(); - break; - case IME_FINISHED_EVENT: - handleImeFinishedEvent(msg.arg1, msg.arg2 != 0); - break; - case DO_PROCESS_INPUT_EVENTS: - mProcessInputEventsScheduled = false; - doProcessInputEvents(); - break; - case DISPATCH_APP_VISIBILITY: - handleAppVisibility(msg.arg1 != 0); - break; - case DISPATCH_GET_NEW_SURFACE: - handleGetNewSurface(); - break; - case RESIZED: - ResizedInfo ri = (ResizedInfo)msg.obj; + private final static int MSG_INVALIDATE = 1; + private final static int MSG_INVALIDATE_RECT = 2; + private final static int MSG_DIE = 3; + private final static int MSG_RESIZED = 4; + private final static int MSG_RESIZED_REPORT = 5; + private final static int MSG_WINDOW_FOCUS_CHANGED = 6; + private final static int MSG_DISPATCH_KEY = 7; + private final static int MSG_DISPATCH_APP_VISIBILITY = 8; + private final static int MSG_DISPATCH_GET_NEW_SURFACE = 9; + private final static int MSG_IME_FINISHED_EVENT = 10; + private final static int MSG_DISPATCH_KEY_FROM_IME = 11; + private final static int MSG_FINISH_INPUT_CONNECTION = 12; + private final static int MSG_CHECK_FOCUS = 13; + private final static int MSG_CLOSE_SYSTEM_DIALOGS = 14; + private final static int MSG_DISPATCH_DRAG_EVENT = 15; + private final static int MSG_DISPATCH_DRAG_LOCATION_EVENT = 16; + private final static int MSG_DISPATCH_SYSTEM_UI_VISIBILITY = 17; + private final static int MSG_UPDATE_CONFIGURATION = 18; + private final static int MSG_PERFORM_ACCESSIBILITY_ACTION = 19; + private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID = 20; + private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID = 21; + private final static int MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT = 22; + private final static int MSG_PROCESS_INPUT_EVENTS = 23; + + final class ViewRootHandler extends Handler { + @Override + public String getMessageName(Message message) { + switch (message.what) { + case MSG_INVALIDATE: + return "MSG_INVALIDATE"; + case MSG_INVALIDATE_RECT: + return "MSG_INVALIDATE_RECT"; + case MSG_DIE: + return "MSG_DIE"; + case MSG_RESIZED: + return "MSG_RESIZED"; + case MSG_RESIZED_REPORT: + return "MSG_RESIZED_REPORT"; + case MSG_WINDOW_FOCUS_CHANGED: + return "MSG_WINDOW_FOCUS_CHANGED"; + case MSG_DISPATCH_KEY: + return "MSG_DISPATCH_KEY"; + case MSG_DISPATCH_APP_VISIBILITY: + return "MSG_DISPATCH_APP_VISIBILITY"; + case MSG_DISPATCH_GET_NEW_SURFACE: + return "MSG_DISPATCH_GET_NEW_SURFACE"; + case MSG_IME_FINISHED_EVENT: + return "MSG_IME_FINISHED_EVENT"; + case MSG_DISPATCH_KEY_FROM_IME: + return "MSG_DISPATCH_KEY_FROM_IME"; + case MSG_FINISH_INPUT_CONNECTION: + return "MSG_FINISH_INPUT_CONNECTION"; + case MSG_CHECK_FOCUS: + return "MSG_CHECK_FOCUS"; + case MSG_CLOSE_SYSTEM_DIALOGS: + return "MSG_CLOSE_SYSTEM_DIALOGS"; + case MSG_DISPATCH_DRAG_EVENT: + return "MSG_DISPATCH_DRAG_EVENT"; + case MSG_DISPATCH_DRAG_LOCATION_EVENT: + return "MSG_DISPATCH_DRAG_LOCATION_EVENT"; + case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: + return "MSG_DISPATCH_SYSTEM_UI_VISIBILITY"; + case MSG_UPDATE_CONFIGURATION: + return "MSG_UPDATE_CONFIGURATION"; + case MSG_PERFORM_ACCESSIBILITY_ACTION: + return "MSG_PERFORM_ACCESSIBILITY_ACTION"; + case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: + return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID"; + case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: + return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID"; + case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: + return "MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT"; + case MSG_PROCESS_INPUT_EVENTS: + return "MSG_PROCESS_INPUT_EVENTS"; + } + return super.getMessageName(message); + } - if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 - && mPendingContentInsets.equals(ri.coveredInsets) - && mPendingVisibleInsets.equals(ri.visibleInsets) - && ((ResizedInfo)msg.obj).newConfig == null) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MSG_INVALIDATE: + ((View) msg.obj).invalidate(); break; - } - // fall through... - case RESIZED_REPORT: - if (mAdded) { - Configuration config = ((ResizedInfo)msg.obj).newConfig; - if (config != null) { - updateConfiguration(config, false); - } - mWinFrame.left = 0; - mWinFrame.right = msg.arg1; - mWinFrame.top = 0; - mWinFrame.bottom = msg.arg2; - mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets); - mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets); - if (msg.what == RESIZED_REPORT) { - mReportNextDraw = true; + case MSG_INVALIDATE_RECT: + final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj; + info.target.invalidate(info.left, info.top, info.right, info.bottom); + info.release(); + break; + case MSG_IME_FINISHED_EVENT: + handleImeFinishedEvent(msg.arg1, msg.arg2 != 0); + break; + case MSG_PROCESS_INPUT_EVENTS: + mProcessInputEventsScheduled = false; + doProcessInputEvents(); + break; + case MSG_DISPATCH_APP_VISIBILITY: + handleAppVisibility(msg.arg1 != 0); + break; + case MSG_DISPATCH_GET_NEW_SURFACE: + handleGetNewSurface(); + break; + case MSG_RESIZED: + ResizedInfo ri = (ResizedInfo)msg.obj; + + if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2 + && mPendingContentInsets.equals(ri.coveredInsets) + && mPendingVisibleInsets.equals(ri.visibleInsets) + && ((ResizedInfo)msg.obj).newConfig == null) { + break; } + // fall through... + case MSG_RESIZED_REPORT: + if (mAdded) { + Configuration config = ((ResizedInfo)msg.obj).newConfig; + if (config != null) { + updateConfiguration(config, false); + } + mWinFrame.left = 0; + mWinFrame.right = msg.arg1; + mWinFrame.top = 0; + mWinFrame.bottom = msg.arg2; + mPendingContentInsets.set(((ResizedInfo)msg.obj).coveredInsets); + mPendingVisibleInsets.set(((ResizedInfo)msg.obj).visibleInsets); + if (msg.what == MSG_RESIZED_REPORT) { + mReportNextDraw = true; + } - if (mView != null) { - forceLayout(mView); + if (mView != null) { + forceLayout(mView); + } + requestLayout(); } - requestLayout(); - } - break; - case WINDOW_FOCUS_CHANGED: { - if (mAdded) { - boolean hasWindowFocus = msg.arg1 != 0; - mAttachInfo.mHasWindowFocus = hasWindowFocus; - - profileRendering(hasWindowFocus); + break; + case MSG_WINDOW_FOCUS_CHANGED: { + if (mAdded) { + boolean hasWindowFocus = msg.arg1 != 0; + mAttachInfo.mHasWindowFocus = hasWindowFocus; - if (hasWindowFocus) { - boolean inTouchMode = msg.arg2 != 0; - ensureTouchModeLocally(inTouchMode); + profileRendering(hasWindowFocus); - if (mAttachInfo.mHardwareRenderer != null && - mSurface != null && mSurface.isValid()) { - mFullRedrawNeeded = true; - try { - mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, - mHolder); - } catch (Surface.OutOfResourcesException e) { - Log.e(TAG, "OutOfResourcesException locking surface", e); + if (hasWindowFocus) { + boolean inTouchMode = msg.arg2 != 0; + ensureTouchModeLocally(inTouchMode); + + if (mAttachInfo.mHardwareRenderer != null && + mSurface != null && mSurface.isValid()) { + mFullRedrawNeeded = true; try { - if (!sWindowSession.outOfMemory(mWindow)) { - Slog.w(TAG, "No processes killed for memory; killing self"); - Process.killProcess(Process.myPid()); + mAttachInfo.mHardwareRenderer.initializeIfNeeded(mWidth, mHeight, + mHolder); + } catch (Surface.OutOfResourcesException e) { + Log.e(TAG, "OutOfResourcesException locking surface", e); + try { + if (!sWindowSession.outOfMemory(mWindow)) { + Slog.w(TAG, "No processes killed for memory; killing self"); + Process.killProcess(Process.myPid()); + } + } catch (RemoteException ex) { } - } catch (RemoteException ex) { + // Retry in a bit. + sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); + return; } - // Retry in a bit. - sendMessageDelayed(obtainMessage(msg.what, msg.arg1, msg.arg2), 500); - return; } } - } - mLastWasImTarget = WindowManager.LayoutParams - .mayUseInputMethod(mWindowAttributes.flags); + mLastWasImTarget = WindowManager.LayoutParams + .mayUseInputMethod(mWindowAttributes.flags); - InputMethodManager imm = InputMethodManager.peekInstance(); - if (mView != null) { - if (hasWindowFocus && imm != null && mLastWasImTarget) { - imm.startGettingWindowFocus(mView); + InputMethodManager imm = InputMethodManager.peekInstance(); + if (mView != null) { + if (hasWindowFocus && imm != null && mLastWasImTarget) { + imm.startGettingWindowFocus(mView); + } + mAttachInfo.mKeyDispatchState.reset(); + mView.dispatchWindowFocusChanged(hasWindowFocus); } - mAttachInfo.mKeyDispatchState.reset(); - mView.dispatchWindowFocusChanged(hasWindowFocus); - } - // Note: must be done after the focus change callbacks, - // so all of the view state is set up correctly. - if (hasWindowFocus) { - if (imm != null && mLastWasImTarget) { - imm.onWindowFocus(mView, mView.findFocus(), - mWindowAttributes.softInputMode, - !mHasHadWindowFocus, mWindowAttributes.flags); - } - // Clear the forward bit. We can just do this directly, since - // the window manager doesn't care about it. - mWindowAttributes.softInputMode &= - ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; - ((WindowManager.LayoutParams)mView.getLayoutParams()) - .softInputMode &= + // Note: must be done after the focus change callbacks, + // so all of the view state is set up correctly. + if (hasWindowFocus) { + if (imm != null && mLastWasImTarget) { + imm.onWindowFocus(mView, mView.findFocus(), + mWindowAttributes.softInputMode, + !mHasHadWindowFocus, mWindowAttributes.flags); + } + // Clear the forward bit. We can just do this directly, since + // the window manager doesn't care about it. + mWindowAttributes.softInputMode &= ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; - mHasHadWindowFocus = true; - } + ((WindowManager.LayoutParams)mView.getLayoutParams()) + .softInputMode &= + ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION; + mHasHadWindowFocus = true; + } - if (hasWindowFocus && mView != null) { - sendAccessibilityEvents(); + if (hasWindowFocus && mView != null && mAccessibilityManager.isEnabled()) { + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + } } + } break; + case MSG_DIE: + doDie(); + break; + case MSG_DISPATCH_KEY: { + KeyEvent event = (KeyEvent)msg.obj; + enqueueInputEvent(event, null, 0, true); + } break; + case MSG_DISPATCH_KEY_FROM_IME: { + if (LOCAL_LOGV) Log.v( + TAG, "Dispatching key " + + msg.obj + " from IME to " + mView); + KeyEvent event = (KeyEvent)msg.obj; + if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { + // The IME is trying to say this event is from the + // system! Bad bad bad! + //noinspection UnusedAssignment + event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); + } + enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true); + } break; + case MSG_FINISH_INPUT_CONNECTION: { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.reportFinishInputConnection((InputConnection)msg.obj); + } + } break; + case MSG_CHECK_FOCUS: { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.checkFocus(); + } + } break; + case MSG_CLOSE_SYSTEM_DIALOGS: { + if (mView != null) { + mView.onCloseSystemDialogs((String)msg.obj); + } + } break; + case MSG_DISPATCH_DRAG_EVENT: + case MSG_DISPATCH_DRAG_LOCATION_EVENT: { + DragEvent event = (DragEvent)msg.obj; + event.mLocalState = mLocalDragState; // only present when this app called startDrag() + handleDragEvent(event); + } break; + case MSG_DISPATCH_SYSTEM_UI_VISIBILITY: { + handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj); + } break; + case MSG_UPDATE_CONFIGURATION: { + Configuration config = (Configuration)msg.obj; + if (config.isOtherSeqNewer(mLastConfiguration)) { + config = mLastConfiguration; + } + updateConfiguration(config, false); + } break; + case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { + if (mView != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg); + } + } break; + case MSG_PERFORM_ACCESSIBILITY_ACTION: { + if (mView != null) { + getAccessibilityInteractionController() + .perfromAccessibilityActionUiThread(msg); + } + } break; + case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { + if (mView != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfoByViewIdUiThread(msg); + } + } break; + case MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: { + if (mView != null) { + getAccessibilityInteractionController() + .findAccessibilityNodeInfosByTextUiThread(msg); + } + } break; } - } break; - case DIE: - doDie(); - break; - case DISPATCH_KEY: { - KeyEvent event = (KeyEvent)msg.obj; - enqueueInputEvent(event, null, 0, true); - } break; - case DISPATCH_KEY_FROM_IME: { - if (LOCAL_LOGV) Log.v( - TAG, "Dispatching key " - + msg.obj + " from IME to " + mView); - KeyEvent event = (KeyEvent)msg.obj; - if ((event.getFlags()&KeyEvent.FLAG_FROM_SYSTEM) != 0) { - // The IME is trying to say this event is from the - // system! Bad bad bad! - //noinspection UnusedAssignment - event = KeyEvent.changeFlags(event, event.getFlags() & ~KeyEvent.FLAG_FROM_SYSTEM); - } - enqueueInputEvent(event, null, QueuedInputEvent.FLAG_DELIVER_POST_IME, true); - } break; - case FINISH_INPUT_CONNECTION: { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.reportFinishInputConnection((InputConnection)msg.obj); - } - } break; - case CHECK_FOCUS: { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.checkFocus(); - } - } break; - case CLOSE_SYSTEM_DIALOGS: { - if (mView != null) { - mView.onCloseSystemDialogs((String)msg.obj); - } - } break; - case DISPATCH_DRAG_EVENT: - case DISPATCH_DRAG_LOCATION_EVENT: { - DragEvent event = (DragEvent)msg.obj; - event.mLocalState = mLocalDragState; // only present when this app called startDrag() - handleDragEvent(event); - } break; - case DISPATCH_SYSTEM_UI_VISIBILITY: { - handleDispatchSystemUiVisibilityChanged((SystemUiVisibilityInfo)msg.obj); - } break; - case UPDATE_CONFIGURATION: { - Configuration config = (Configuration)msg.obj; - if (config.isOtherSeqNewer(mLastConfiguration)) { - config = mLastConfiguration; - } - updateConfiguration(config, false); - } break; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID: { - if (mView != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfoByAccessibilityIdUiThread(msg); - } - } break; - case DO_PERFORM_ACCESSIBILITY_ACTION: { - if (mView != null) { - getAccessibilityInteractionController() - .perfromAccessibilityActionUiThread(msg); - } - } break; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID: { - if (mView != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfoByViewIdUiThread(msg); - } - } break; - case DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT: { - if (mView != null) { - getAccessibilityInteractionController() - .findAccessibilityNodeInfosByTextUiThread(msg); - } - } break; } } + final ViewRootHandler mHandler = new ViewRootHandler(); /** * Something in the current window tells us we need to change the touch mode. For @@ -3684,7 +3699,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (immediate) { doDie(); } else { - sendEmptyMessage(DIE); + mHandler.sendEmptyMessage(MSG_DIE); } } @@ -3721,8 +3736,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, } public void requestUpdateConfiguration(Configuration config) { - Message msg = obtainMessage(UPDATE_CONFIGURATION, config); - sendMessage(msg); + Message msg = mHandler.obtainMessage(MSG_UPDATE_CONFIGURATION, config); + mHandler.sendMessage(msg); } private void destroyHardwareRenderer() { @@ -3734,10 +3749,15 @@ public final class ViewRootImpl extends Handler implements ViewParent, } void dispatchImeFinishedEvent(int seq, boolean handled) { - Message msg = obtainMessage(IME_FINISHED_EVENT); + Message msg = mHandler.obtainMessage(MSG_IME_FINISHED_EVENT); msg.arg1 = seq; msg.arg2 = handled ? 1 : 0; - sendMessage(msg); + mHandler.sendMessage(msg); + } + + public void dispatchFinishInputConnection(InputConnection connection) { + Message msg = mHandler.obtainMessage(MSG_FINISH_INPUT_CONNECTION, connection); + mHandler.sendMessage(msg); } public void dispatchResized(int w, int h, Rect coveredInsets, @@ -3746,7 +3766,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, + " h=" + h + " coveredInsets=" + coveredInsets.toShortString() + " visibleInsets=" + visibleInsets.toShortString() + " reportDraw=" + reportDraw); - Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED); + Message msg = mHandler.obtainMessage(reportDraw ? MSG_RESIZED_REPORT :MSG_RESIZED); if (mTranslator != null) { mTranslator.translateRectInScreenToAppWindow(coveredInsets); mTranslator.translateRectInScreenToAppWindow(visibleInsets); @@ -3760,7 +3780,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, ri.visibleInsets = new Rect(visibleInsets); ri.newConfig = newConfig; msg.obj = ri; - sendMessage(msg); + mHandler.sendMessage(msg); } /** @@ -3857,7 +3877,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, private void scheduleProcessInputEvents() { if (!mProcessInputEventsScheduled) { mProcessInputEventsScheduled = true; - sendEmptyMessage(DO_PROCESS_INPUT_EVENTS); + mHandler.sendEmptyMessage(MSG_PROCESS_INPUT_EVENTS); } } @@ -3874,7 +3894,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, // so we can clear the pending flag immediately. if (mProcessInputEventsScheduled) { mProcessInputEventsScheduled = false; - removeMessages(DO_PROCESS_INPUT_EVENTS); + mHandler.removeMessages(MSG_PROCESS_INPUT_EVENTS); } } @@ -3960,47 +3980,69 @@ public final class ViewRootImpl extends Handler implements ViewParent, } WindowInputEventReceiver mInputEventReceiver; + public void dispatchInvalidateDelayed(View view, long delayMilliseconds) { + Message msg = mHandler.obtainMessage(MSG_INVALIDATE, view); + mHandler.sendMessageDelayed(msg, delayMilliseconds); + } + + public void cancelInvalidate(View view) { + mHandler.removeMessages(MSG_INVALIDATE, view); + } + + public void dispatchInvalidateRectDelayed(AttachInfo.InvalidateInfo info, + long delayMilliseconds) { + final Message msg = mHandler.obtainMessage(MSG_INVALIDATE_RECT, info); + mHandler.sendMessageDelayed(msg, delayMilliseconds); + } + public void dispatchKey(KeyEvent event) { - Message msg = obtainMessage(DISPATCH_KEY, event); - sendMessage(msg); + Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY, event); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); + } + + public void dispatchKeyFromIme(KeyEvent event) { + Message msg = mHandler.obtainMessage(MSG_DISPATCH_KEY_FROM_IME, event); + msg.setAsynchronous(true); + mHandler.sendMessage(msg); } public void dispatchAppVisibility(boolean visible) { - Message msg = obtainMessage(DISPATCH_APP_VISIBILITY); + Message msg = mHandler.obtainMessage(MSG_DISPATCH_APP_VISIBILITY); msg.arg1 = visible ? 1 : 0; - sendMessage(msg); + mHandler.sendMessage(msg); } public void dispatchGetNewSurface() { - Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE); - sendMessage(msg); + Message msg = mHandler.obtainMessage(MSG_DISPATCH_GET_NEW_SURFACE); + mHandler.sendMessage(msg); } public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) { Message msg = Message.obtain(); - msg.what = WINDOW_FOCUS_CHANGED; + msg.what = MSG_WINDOW_FOCUS_CHANGED; msg.arg1 = hasFocus ? 1 : 0; msg.arg2 = inTouchMode ? 1 : 0; - sendMessage(msg); + mHandler.sendMessage(msg); } public void dispatchCloseSystemDialogs(String reason) { Message msg = Message.obtain(); - msg.what = CLOSE_SYSTEM_DIALOGS; + msg.what = MSG_CLOSE_SYSTEM_DIALOGS; msg.obj = reason; - sendMessage(msg); + mHandler.sendMessage(msg); } public void dispatchDragEvent(DragEvent event) { final int what; if (event.getAction() == DragEvent.ACTION_DRAG_LOCATION) { - what = DISPATCH_DRAG_LOCATION_EVENT; - removeMessages(what); + what = MSG_DISPATCH_DRAG_LOCATION_EVENT; + mHandler.removeMessages(what); } else { - what = DISPATCH_DRAG_EVENT; + what = MSG_DISPATCH_DRAG_EVENT; } - Message msg = obtainMessage(what, event); - sendMessage(msg); + Message msg = mHandler.obtainMessage(what, event); + mHandler.sendMessage(msg); } public void dispatchSystemUiVisibilityChanged(int seq, int globalVisibility, @@ -4010,21 +4052,13 @@ public final class ViewRootImpl extends Handler implements ViewParent, args.globalVisibility = globalVisibility; args.localValue = localValue; args.localChanges = localChanges; - sendMessage(obtainMessage(DISPATCH_SYSTEM_UI_VISIBILITY, args)); + mHandler.sendMessage(mHandler.obtainMessage(MSG_DISPATCH_SYSTEM_UI_VISIBILITY, args)); } - /** - * The window is getting focus so if there is anything focused/selected - * send an {@link AccessibilityEvent} to announce that. - */ - private void sendAccessibilityEvents() { - if (!mAccessibilityManager.isEnabled()) { - return; - } - mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); - View focusedView = mView.findFocus(); - if (focusedView != null && focusedView != mView) { - focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + public void dispatchCheckFocus() { + if (!mHandler.hasMessages(MSG_CHECK_FOCUS)) { + // This will result in a call to checkFocus() below. + mHandler.sendEmptyMessage(MSG_CHECK_FOCUS); } } @@ -4041,7 +4075,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, } if (!mSendWindowContentChangedAccessibilityEvent.mIsPending) { mSendWindowContentChangedAccessibilityEvent.mIsPending = true; - postDelayed(mSendWindowContentChangedAccessibilityEvent, + mHandler.postDelayed(mSendWindowContentChangedAccessibilityEvent, ViewConfiguration.getSendRecurringAccessibilityEventsInterval()); } } @@ -4052,7 +4086,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, */ private void removeSendWindowContentChangedCallback() { if (mSendWindowContentChangedAccessibilityEvent != null) { - removeCallbacks(mSendWindowContentChangedAccessibilityEvent); + mHandler.removeCallbacks(mSendWindowContentChangedAccessibilityEvent); } } @@ -4095,6 +4129,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, return scrollToRectOrFocus(rectangle, immediate); } + public void childHasTransientStateChanged(View child, boolean hasTransientState) { + // Do nothing. + } + class TakenSurfaceHolder extends BaseSurfaceHolder { @Override public boolean onAllowLockCanvas() { @@ -4512,6 +4550,9 @@ public final class ViewRootImpl extends Handler implements ViewParent, } /** + * The run queue is used to enqueue pending work from Views when no Handler is + * attached. The work is executed during the next call to performTraversals on + * the thread. * @hide */ static final class RunQueue { @@ -4590,24 +4631,35 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void onAccessibilityStateChanged(boolean enabled) { if (enabled) { ensureConnection(); + if (mAttachInfo != null && mAttachInfo.mHasWindowFocus) { + mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + View focusedView = mView.findFocus(); + if (focusedView != null && focusedView != mView) { + focusedView.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); + } + } } else { ensureNoConnection(); } } public void ensureConnection() { - final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; - if (!registered) { - mAttachInfo.mAccessibilityWindowId = - mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, - new AccessibilityInteractionConnection(ViewRootImpl.this)); + if (mAttachInfo != null) { + final boolean registered = + mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED; + if (!registered) { + mAttachInfo.mAccessibilityWindowId = + mAccessibilityManager.addAccessibilityInteractionConnection(mWindow, + new AccessibilityInteractionConnection(ViewRootImpl.this)); + } } } public void ensureNoConnection() { - final boolean registered = mAttachInfo.mAccessibilityWindowId != View.NO_ID; + final boolean registered = + mAttachInfo.mAccessibilityWindowId != AccessibilityNodeInfo.UNDEFINED; if (registered) { - mAttachInfo.mAccessibilityWindowId = View.NO_ID; + mAttachInfo.mAccessibilityWindowId = AccessibilityNodeInfo.UNDEFINED; mAccessibilityManager.removeAccessibilityInteractionConnection(mWindow); } } @@ -4770,8 +4822,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, long accessibilityNodeId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid) { - Message message = Message.obtain(); - message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; + Message message = mHandler.obtainMessage(); + message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_ACCESSIBILITY_ID; message.arg1 = interrogatingPid; SomeArgs args = mPool.acquire(); args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); @@ -4785,11 +4837,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, // client can handle the message to generate the result. if (interrogatingPid == Process.myPid() && interrogatingTid == Looper.getMainLooper().getThread().getId()) { - message.setTarget(ViewRootImpl.this); AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { - sendMessage(message); + mHandler.sendMessage(message); } } @@ -4805,14 +4856,21 @@ public final class ViewRootImpl extends Handler implements ViewParent, List<AccessibilityNodeInfo> infos = mTempAccessibilityNodeInfoList; infos.clear(); try { - View target = findViewByAccessibilityId(accessibilityViewId); - if (target != null && target.getVisibility() == View.VISIBLE) { - AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); - if (provider != null) { - infos.add(provider.createAccessibilityNodeInfo(virtualDescendantId)); - } else if (virtualDescendantId == View.NO_ID) { - getAccessibilityPrefetchStrategy().prefetchAccessibilityNodeInfos( - interrogatingPid, target, infos); + if (accessibilityViewId == AccessibilityNodeInfo.UNDEFINED) { + View target = ViewRootImpl.this.mView; + if (target != null && target.getVisibility() == View.VISIBLE) { + infos.add(target.createAccessibilityNodeInfo()); + } + } else { + View target = findViewByAccessibilityId(accessibilityViewId); + if (target != null && target.getVisibility() == View.VISIBLE) { + AccessibilityNodeProvider provider = target.getAccessibilityNodeProvider(); + if (provider != null) { + infos.add(provider.createAccessibilityNodeInfo(virtualDescendantId)); + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) { + getAccessibilityPrefetchStrategy().prefetchAccessibilityNodeInfos( + interrogatingPid, target, infos); + } } } } finally { @@ -4828,8 +4886,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void findAccessibilityNodeInfoByViewIdClientThread(long accessibilityNodeId, int viewId, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid) { - Message message = Message.obtain(); - message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; + Message message = mHandler.obtainMessage(); + message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_VIEW_ID; message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); SomeArgs args = mPool.acquire(); args.argi1 = viewId; @@ -4842,11 +4900,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, // client can handle the message to generate the result. if (interrogatingPid == Process.myPid() && interrogatingTid == Looper.getMainLooper().getThread().getId()) { - message.setTarget(ViewRootImpl.this); AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { - sendMessage(message); + mHandler.sendMessage(message); } } @@ -4861,7 +4918,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, AccessibilityNodeInfo info = null; try { View root = null; - if (accessibilityViewId != View.NO_ID) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { root = findViewByAccessibilityId(accessibilityViewId); } else { root = ViewRootImpl.this.mView; @@ -4885,8 +4942,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, String text, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interrogatingPid, long interrogatingTid) { - Message message = Message.obtain(); - message.what = DO_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT; + Message message = mHandler.obtainMessage(); + message.what = MSG_FIND_ACCESSIBLITY_NODE_INFO_BY_TEXT; SomeArgs args = mPool.acquire(); args.arg1 = text; args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); @@ -4900,11 +4957,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, // client can handle the message to generate the result. if (interrogatingPid == Process.myPid() && interrogatingTid == Looper.getMainLooper().getThread().getId()) { - message.setTarget(ViewRootImpl.this); AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { - sendMessage(message); + mHandler.sendMessage(message); } } @@ -4920,7 +4976,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, List<AccessibilityNodeInfo> infos = null; try { View target; - if (accessibilityViewId != View.NO_ID) { + if (accessibilityViewId != AccessibilityNodeInfo.UNDEFINED) { target = findViewByAccessibilityId(accessibilityViewId); } else { target = ViewRootImpl.this.mView; @@ -4930,7 +4986,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (provider != null) { infos = provider.findAccessibilityNodeInfosByText(text, virtualDescendantId); - } else if (virtualDescendantId == View.NO_ID) { + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) { ArrayList<View> foundViews = mAttachInfo.mFocusablesTempList; foundViews.clear(); target.findViewsWithText(foundViews, text, View.FIND_VIEWS_WITH_TEXT @@ -4971,8 +5027,8 @@ public final class ViewRootImpl extends Handler implements ViewParent, public void performAccessibilityActionClientThread(long accessibilityNodeId, int action, int interactionId, IAccessibilityInteractionConnectionCallback callback, int interogatingPid, long interrogatingTid) { - Message message = Message.obtain(); - message.what = DO_PERFORM_ACCESSIBILITY_ACTION; + Message message = mHandler.obtainMessage(); + message.what = MSG_PERFORM_ACCESSIBILITY_ACTION; message.arg1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId); message.arg2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId); SomeArgs args = mPool.acquire(); @@ -4986,11 +5042,10 @@ public final class ViewRootImpl extends Handler implements ViewParent, // client can handle the message to generate the result. if (interogatingPid == Process.myPid() && interrogatingTid == Looper.getMainLooper().getThread().getId()) { - message.setTarget(ViewRootImpl.this); AccessibilityInteractionClient.getInstanceForThread( interrogatingTid).setSameThreadMessage(message); } else { - sendMessage(message); + mHandler.sendMessage(message); } } @@ -5011,7 +5066,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, if (provider != null) { succeeded = provider.performAccessibilityAction(action, virtualDescendantId); - } else if (virtualDescendantId == View.NO_ID) { + } else if (virtualDescendantId == AccessibilityNodeInfo.UNDEFINED) { switch (action) { case AccessibilityNodeInfo.ACTION_FOCUS: { if (!target.hasFocus()) { @@ -5119,7 +5174,7 @@ public final class ViewRootImpl extends Handler implements ViewParent, private void addAndCacheNotCachedNodeInfo(long interrogatingPid, View view, List<AccessibilityNodeInfo> outInfos) { final long accessibilityNodeId = AccessibilityNodeInfo.makeNodeId( - view.getAccessibilityViewId(), View.NO_ID); + view.getAccessibilityViewId(), AccessibilityNodeInfo.UNDEFINED); AccessibilityNodeInfoCache cache = getCacheForInterrogatingPid(interrogatingPid); if (!cache.containsKey(accessibilityNodeId)) { // Account for the ids of the fetched infos. The infos will be diff --git a/core/java/android/view/accessibility/AccessibilityInteractionClient.java b/core/java/android/view/accessibility/AccessibilityInteractionClient.java index 072fdd86ed08..105c010d4be9 100644 --- a/core/java/android/view/accessibility/AccessibilityInteractionClient.java +++ b/core/java/android/view/accessibility/AccessibilityInteractionClient.java @@ -153,10 +153,12 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. - * @param accessibilityNodeId A unique node accessibility id - * (accessibility view and virtual descendant id). + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} + * to start from the root. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. */ public AccessibilityNodeInfo findAccessibilityNodeInfoByAccessibilityId(int connectionId, @@ -164,7 +166,8 @@ public final class AccessibilityInteractionClient try { IAccessibilityServiceConnection connection = getConnection(connectionId); if (connection != null) { - AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get(accessibilityNodeId); + AccessibilityNodeInfo cachedInfo = sAccessibilityNodeInfoCache.get( + accessibilityNodeId); if (cachedInfo != null) { return cachedInfo; } @@ -176,7 +179,7 @@ public final class AccessibilityInteractionClient if (windowScale > 0) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); if (infos != null && !infos.isEmpty()) { return infos.get(0); } @@ -202,10 +205,11 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. - * @param accessibilityNodeId A unique view id from where to start the search. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} * to start from the root. * @param viewId The id of the view. * @return An {@link AccessibilityNodeInfo} if found, null otherwise. @@ -224,7 +228,7 @@ public final class AccessibilityInteractionClient if (windowScale > 0) { AccessibilityNodeInfo info = getFindAccessibilityNodeInfoResultAndClear( interactionId); - finalizeAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); return info; } } else { @@ -249,10 +253,12 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. - * @param accessibilityNodeId A unique view id from where to start the search. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ROOT_NODE_ID} + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} + * to start from the root. * @param text The searched text. * @return A list of found {@link AccessibilityNodeInfo}s. */ @@ -269,7 +275,7 @@ public final class AccessibilityInteractionClient if (windowScale > 0) { List<AccessibilityNodeInfo> infos = getFindAccessibilityNodeInfosResultAndClear( interactionId); - finalizeAccessibilityNodeInfos(infos, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfos(infos, connectionId, windowScale); return infos; } } else { @@ -291,9 +297,12 @@ public final class AccessibilityInteractionClient * * @param connectionId The id of a connection for interacting with the system. * @param accessibilityWindowId A unique window id. Use - * {@link com.android.server.accessibility.AccessibilityManagerService#ACTIVE_WINDOW_ID} + * {@link android.view.accessibility.AccessibilityNodeInfo#ACTIVE_WINDOW_ID} * to query the currently active window. - * @param accessibilityNodeId A unique node id (accessibility and virtual descendant id). + * @param accessibilityNodeId A unique view id or virtual descendant id from + * where to start the search. Use + * {@link android.view.accessibility.AccessibilityNodeInfo#ROOT_NODE_ID} + * to start from the root. * @param action The action to perform. * @return Whether the action was performed. */ @@ -323,16 +332,10 @@ public final class AccessibilityInteractionClient } public void clearCache() { - if (DEBUG) { - Log.w(LOG_TAG, "clearCache()"); - } sAccessibilityNodeInfoCache.clear(); } public void removeCachedNode(long accessibilityNodeId) { - if (DEBUG) { - Log.w(LOG_TAG, "removeCachedNode(" + accessibilityNodeId +")"); - } sAccessibilityNodeInfoCache.remove(accessibilityNodeId); } @@ -364,9 +367,6 @@ public final class AccessibilityInteractionClient if (interactionId > mInteractionId) { mFindAccessibilityNodeInfoResult = info; mInteractionId = interactionId; - if (info != null) { - sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info); - } } mInstanceLock.notifyAll(); } @@ -404,11 +404,6 @@ public final class AccessibilityInteractionClient mFindAccessibilityNodeInfosResult = infos; } mInteractionId = interactionId; - final int infoCount = infos.size(); - for (int i = 0; i < infoCount; i ++) { - AccessibilityNodeInfo info = infos.get(i); - sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info); - } } mInstanceLock.notifyAll(); } @@ -513,12 +508,13 @@ public final class AccessibilityInteractionClient * @param connectionId The id of the connection to the system. * @param windowScale The source window compatibility scale. */ - private void finalizeAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, + private void finalizeAndCacheAccessibilityNodeInfo(AccessibilityNodeInfo info, int connectionId, float windowScale) { if (info != null) { applyCompatibilityScaleIfNeeded(info, windowScale); info.setConnectionId(connectionId); info.setSealed(true); + sAccessibilityNodeInfoCache.put(info.getSourceNodeId(), info); } } @@ -529,13 +525,13 @@ public final class AccessibilityInteractionClient * @param connectionId The id of the connection to the system. * @param windowScale The source window compatibility scale. */ - private void finalizeAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, + private void finalizeAndCacheAccessibilityNodeInfos(List<AccessibilityNodeInfo> infos, int connectionId, float windowScale) { if (infos != null) { final int infosCount = infos.size(); for (int i = 0; i < infosCount; i++) { AccessibilityNodeInfo info = infos.get(i); - finalizeAccessibilityNodeInfo(info, connectionId, windowScale); + finalizeAndCacheAccessibilityNodeInfo(info, connectionId, windowScale); } } } diff --git a/core/java/android/view/accessibility/AccessibilityNodeInfo.java b/core/java/android/view/accessibility/AccessibilityNodeInfo.java index d7d67928e158..84ad268f59e1 100644 --- a/core/java/android/view/accessibility/AccessibilityNodeInfo.java +++ b/core/java/android/view/accessibility/AccessibilityNodeInfo.java @@ -52,7 +52,14 @@ public class AccessibilityNodeInfo implements Parcelable { private static final boolean DEBUG = false; - private static final int UNDEFINED = -1; + /** @hide */ + public static final int UNDEFINED = -1; + + /** @hide */ + public static final long ROOT_NODE_ID = makeNodeId(UNDEFINED, UNDEFINED); + + /** @hide */ + public static final int ACTIVE_WINDOW_ID = UNDEFINED; // Actions. @@ -162,8 +169,8 @@ public class AccessibilityNodeInfo implements Parcelable { // Data. private int mWindowId = UNDEFINED; - private long mSourceNodeId = makeNodeId(UNDEFINED, UNDEFINED); - private long mParentNodeId = makeNodeId(UNDEFINED, UNDEFINED); + private long mSourceNodeId = ROOT_NODE_ID; + private long mParentNodeId = ROOT_NODE_ID; private int mBooleanProperties; private final Rect mBoundsInParent = new Rect(); @@ -1160,8 +1167,8 @@ public class AccessibilityNodeInfo implements Parcelable { */ private void clear() { mSealed = false; - mSourceNodeId = makeNodeId(UNDEFINED, UNDEFINED); - mParentNodeId = makeNodeId(UNDEFINED, UNDEFINED); + mSourceNodeId = ROOT_NODE_ID; + mParentNodeId = ROOT_NODE_ID; mWindowId = UNDEFINED; mConnectionId = UNDEFINED; mChildIds.clear(); diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java index bd02d62b64f2..89aba3ca1e83 100644 --- a/core/java/android/view/inputmethod/BaseInputConnection.java +++ b/core/java/android/view/inputmethod/BaseInputConnection.java @@ -19,7 +19,6 @@ package android.view.inputmethod; import android.content.Context; import android.content.res.TypedArray; import android.os.Bundle; -import android.os.Handler; import android.os.SystemClock; import android.text.Editable; import android.text.NoCopySpan; @@ -497,15 +496,14 @@ public class BaseInputConnection implements InputConnection { */ public boolean sendKeyEvent(KeyEvent event) { synchronized (mIMM.mH) { - Handler h = mTargetView != null ? mTargetView.getHandler() : null; - if (h == null) { + ViewRootImpl viewRootImpl = mTargetView != null ? mTargetView.getViewRootImpl() : null; + if (viewRootImpl == null) { if (mIMM.mServedView != null) { - h = mIMM.mServedView.getHandler(); + viewRootImpl = mIMM.mServedView.getViewRootImpl(); } } - if (h != null) { - h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, - event)); + if (viewRootImpl != null) { + viewRootImpl.dispatchKeyFromIme(event); } } return false; diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java index 7171b58482d4..c51d2440f893 100644 --- a/core/java/android/view/inputmethod/InputMethodManager.java +++ b/core/java/android/view/inputmethod/InputMethodManager.java @@ -336,6 +336,7 @@ public final class InputMethodManager { } case MSG_UNBIND: { final int sequence = msg.arg1; + boolean startInput = false; synchronized (mH) { if (mBindSequence == sequence) { if (false) { @@ -355,7 +356,12 @@ public final class InputMethodManager { if (mServedView != null && mServedView.isFocused()) { mServedConnecting = true; } + if (mActive) { + startInput = true; + } } + } + if (startInput) { startInputInner(); } return; @@ -669,11 +675,10 @@ public final class InputMethodManager { // longer the input target, so it can reset its state. Schedule // this call on its window's Handler so it will be on the correct // thread and outside of our lock. - Handler vh = mServedView.getHandler(); - if (vh != null) { + ViewRootImpl viewRootImpl = mServedView.getViewRootImpl(); + if (viewRootImpl != null) { // This will result in a call to reportFinishInputConnection() below. - vh.sendMessage(vh.obtainMessage(ViewRootImpl.FINISH_INPUT_CONNECTION, - mServedInputConnection)); + viewRootImpl.dispatchFinishInputConnection(mServedInputConnection); } } } @@ -1124,31 +1129,36 @@ public final class InputMethodManager { } static void scheduleCheckFocusLocked(View view) { - Handler vh = view.getHandler(); - if (vh != null && !vh.hasMessages(ViewRootImpl.CHECK_FOCUS)) { - // This will result in a call to checkFocus() below. - vh.sendMessage(vh.obtainMessage(ViewRootImpl.CHECK_FOCUS)); + ViewRootImpl viewRootImpl = view.getViewRootImpl(); + if (viewRootImpl != null) { + viewRootImpl.dispatchCheckFocus(); } } - + /** * @hide */ public void checkFocus() { + if (checkFocusNoStartInput()) { + startInputInner(); + } + } + + private boolean checkFocusNoStartInput() { // This is called a lot, so short-circuit before locking. if (mServedView == mNextServedView && !mNextServedNeedsStart) { - return; + return false; } InputConnection ic = null; synchronized (mH) { if (mServedView == mNextServedView && !mNextServedNeedsStart) { - return; + return false; } if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView + " next=" + mNextServedView + " restart=" + mNextServedNeedsStart); - + mNextServedNeedsStart = false; if (mNextServedView == null) { finishInputLocked(); @@ -1156,22 +1166,22 @@ public final class InputMethodManager { // but no longer do. We should make sure the input method is // no longer shown, since it serves no purpose. closeCurrentInput(); - return; + return false; } - + ic = mServedInputConnection; - + mServedView = mNextServedView; mCurrentTextBoxAttribute = null; mCompletions = null; mServedConnecting = true; } - + if (ic != null) { ic.finishComposingText(); } - - startInputInner(); + + return true; } void closeCurrentInput() { @@ -1200,7 +1210,7 @@ public final class InputMethodManager { focusInLocked(focusedView != null ? focusedView : rootView); } - checkFocus(); + boolean startInput = checkFocusNoStartInput(); synchronized (mH) { try { @@ -1212,6 +1222,10 @@ public final class InputMethodManager { } catch (RemoteException e) { } } + + if (startInput) { + startInputInner(); + } } /** @hide */ diff --git a/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl b/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl new file mode 100644 index 000000000000..d0b6ba6f3ad7 --- /dev/null +++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2012 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.view.textservice; + +parcelable SentenceSuggestionsInfo; diff --git a/core/java/android/view/textservice/SentenceSuggestionsInfo.java b/core/java/android/view/textservice/SentenceSuggestionsInfo.java new file mode 100644 index 000000000000..8d7c6cf841f9 --- /dev/null +++ b/core/java/android/view/textservice/SentenceSuggestionsInfo.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2012 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.view.textservice; + +import android.os.Parcel; +import android.os.Parcelable; + +import java.util.Arrays; + +/** + * @hide + * This class contains a metadata of sentence level suggestions from the text service + */ +public final class SentenceSuggestionsInfo implements Parcelable { + + private final SuggestionsInfo[] mSuggestionsInfos; + private final int[] mOffsets; + private final int[] mLengths; + + /** + * Constructor. + * @param suggestionsInfos from the text service + * @param offsets the array of offsets of suggestions + * @param lengths the array of lengths of suggestions + */ + public SentenceSuggestionsInfo( + SuggestionsInfo[] suggestionsInfos, int[] offsets, int[] lengths) { + if (suggestionsInfos == null || offsets == null || lengths == null) { + throw new NullPointerException(); + } + if (suggestionsInfos.length != offsets.length || offsets.length != lengths.length) { + throw new IllegalArgumentException(); + } + final int infoSize = suggestionsInfos.length; + mSuggestionsInfos = Arrays.copyOf(suggestionsInfos, infoSize); + mOffsets = Arrays.copyOf(offsets, infoSize); + mLengths = Arrays.copyOf(lengths, infoSize); + } + + public SentenceSuggestionsInfo(Parcel source) { + final int infoSize = source.readInt(); + mSuggestionsInfos = new SuggestionsInfo[infoSize]; + source.readTypedArray(mSuggestionsInfos, SuggestionsInfo.CREATOR); + mOffsets = new int[mSuggestionsInfos.length]; + source.readIntArray(mOffsets); + mLengths = new int[mSuggestionsInfos.length]; + source.readIntArray(mLengths); + } + + /** + * Used to package this object into a {@link Parcel}. + * + * @param dest The {@link Parcel} to be written. + * @param flags The flags used for parceling. + */ + @Override + public void writeToParcel(Parcel dest, int flags) { + final int infoSize = mSuggestionsInfos.length; + dest.writeInt(infoSize); + dest.writeTypedArray(mSuggestionsInfos, 0); + dest.writeIntArray(mOffsets); + dest.writeIntArray(mLengths); + } + + @Override + public int describeContents() { + return 0; + } + + /** + * @hide + */ + public SuggestionsInfo getSuggestionsInfoAt(int i) { + if (i >= 0 && i < mSuggestionsInfos.length) { + return mSuggestionsInfos[i]; + } + return null; + } + + /** + * @hide + */ + public int getOffsetAt(int i) { + if (i >= 0 && i < mOffsets.length) { + return mOffsets[i]; + } + return -1; + } + + /** + * @hide + */ + public int getLengthAt(int i) { + if (i >= 0 && i < mLengths.length) { + return mLengths[i]; + } + return -1; + } + + /** + * Used to make this class parcelable. + */ + public static final Parcelable.Creator<SentenceSuggestionsInfo> CREATOR + = new Parcelable.Creator<SentenceSuggestionsInfo>() { + @Override + public SentenceSuggestionsInfo createFromParcel(Parcel source) { + return new SentenceSuggestionsInfo(source); + } + + @Override + public SentenceSuggestionsInfo[] newArray(int size) { + return new SentenceSuggestionsInfo[size]; + } + }; +} diff --git a/core/java/android/view/textservice/SpellCheckerSession.java b/core/java/android/view/textservice/SpellCheckerSession.java index f6418ce6f1ce..3491a5377080 100644 --- a/core/java/android/view/textservice/SpellCheckerSession.java +++ b/core/java/android/view/textservice/SpellCheckerSession.java @@ -115,7 +115,7 @@ public class SpellCheckerSession { handleOnGetSuggestionsMultiple((SuggestionsInfo[]) msg.obj); break; case MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE: - handleOnGetSuggestionsMultipleForSentence((SuggestionsInfo[]) msg.obj); + handleOnGetSentenceSuggestionsMultiple((SentenceSuggestionsInfo[]) msg.obj); break; } } @@ -180,8 +180,8 @@ public class SpellCheckerSession { /** * @hide */ - public void getSuggestionsForSentence(TextInfo textInfo, int suggestionsLimit) { - mSpellCheckerSessionListenerImpl.getSuggestionsMultipleForSentence( + public void getSentenceSuggestions(TextInfo textInfo, int suggestionsLimit) { + mSpellCheckerSessionListenerImpl.getSentenceSuggestionsMultiple( new TextInfo[] {textInfo}, suggestionsLimit); } @@ -214,8 +214,8 @@ public class SpellCheckerSession { mSpellCheckerSessionListener.onGetSuggestions(suggestionInfos); } - private void handleOnGetSuggestionsMultipleForSentence(SuggestionsInfo[] suggestionInfos) { - mSpellCheckerSessionListener.onGetSuggestionsForSentence(suggestionInfos); + private void handleOnGetSentenceSuggestionsMultiple(SentenceSuggestionsInfo[] suggestionInfos) { + mSpellCheckerSessionListener.onGetSentenceSuggestions(suggestionInfos); } private static class SpellCheckerSessionListenerImpl extends ISpellCheckerSessionListener.Stub { @@ -285,7 +285,7 @@ public class SpellCheckerSession { throw new IllegalArgumentException(); } try { - session.onGetSuggestionsMultipleForSentence( + session.onGetSentenceSuggestionsMultiple( scp.mTextInfos, scp.mSuggestionsLimit); } catch (RemoteException e) { Log.e(TAG, "Failed to get suggestions " + e); @@ -366,9 +366,9 @@ public class SpellCheckerSession { suggestionsLimit, sequentialWords)); } - public void getSuggestionsMultipleForSentence(TextInfo[] textInfos, int suggestionsLimit) { + public void getSentenceSuggestionsMultiple(TextInfo[] textInfos, int suggestionsLimit) { if (DBG) { - Log.w(TAG, "getSuggestionsMultipleForSentence"); + Log.w(TAG, "getSentenceSuggestionsMultiple"); } processOrEnqueueTask( new SpellCheckerParams(TASK_GET_SUGGESTIONS_MULTIPLE_FOR_SENTENCE, @@ -399,8 +399,8 @@ public class SpellCheckerSession { while (!mPendingTasks.isEmpty()) { final SpellCheckerParams tmp = mPendingTasks.poll(); if (tmp.mWhat == TASK_CLOSE) { - // Only one close task should be processed, while we need to remove all - // close tasks from the queue + // Only one close task should be processed, while we need to remove + // all close tasks from the queue closeTask = tmp; } } @@ -426,7 +426,7 @@ public class SpellCheckerSession { } @Override - public void onGetSuggestionsForSentence(SuggestionsInfo[] results) { + public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) { mHandler.sendMessage( Message.obtain(mHandler, MSG_ON_GET_SUGGESTION_MULTIPLE_FOR_SENTENCE, results)); } @@ -444,7 +444,7 @@ public class SpellCheckerSession { /** * @hide */ - public void onGetSuggestionsForSentence(SuggestionsInfo[] results); + public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results); } private static class InternalListener extends ITextServicesSessionListener.Stub { diff --git a/core/java/android/view/textservice/SuggestionsInfo.java b/core/java/android/view/textservice/SuggestionsInfo.java index 9b99770246d7..78bc1a9678f1 100644 --- a/core/java/android/view/textservice/SuggestionsInfo.java +++ b/core/java/android/view/textservice/SuggestionsInfo.java @@ -21,14 +21,11 @@ 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 @@ -50,8 +47,6 @@ 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; @@ -74,46 +69,12 @@ 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; @@ -126,10 +87,6 @@ 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); } /** @@ -145,8 +102,6 @@ public final class SuggestionsInfo implements Parcelable { dest.writeInt(mCookie); dest.writeInt(mSequence); dest.writeInt(mSuggestionsAvailable ? 1 : 0); - dest.writeIntArray(mStartPosArray); - dest.writeIntArray(mLengthArray); } /** @@ -227,24 +182,4 @@ 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/webkit/BrowserFrame.java b/core/java/android/webkit/BrowserFrame.java index 6fddb1ab90b7..8ccc59c78a9c 100644 --- a/core/java/android/webkit/BrowserFrame.java +++ b/core/java/android/webkit/BrowserFrame.java @@ -471,18 +471,6 @@ class BrowserFrame extends Handler { } /** - * We have received an SSL certificate for the main top-level page. - * Used by the Android HTTP stack only. - */ - void certificate(SslCertificate certificate) { - if (mIsMainFrame) { - // we want to make this call even if the certificate is null - // (ie, the site is not secure) - mCallbackProxy.onReceivedCertificate(certificate); - } - } - - /** * Destroy all native components of the BrowserFrame. */ public void destroy() { @@ -515,10 +503,6 @@ class BrowserFrame extends Handler { } } } - if (!JniUtil.useChromiumHttpStack()) { - WebViewWorker.getHandler().sendEmptyMessage( - WebViewWorker.MSG_TRIM_CACHE); - } break; } @@ -767,10 +751,9 @@ class BrowserFrame extends Handler { } else if (mSettings.getAllowContentAccess() && url.startsWith(ANDROID_CONTENT)) { try { - // Strip off mimetype, for compatibility with ContentLoader.java - // If we don't do this, we can fail to load Gmail attachments, - // because the URL being loaded doesn't exactly match the URL we - // have permission to read. + // Strip off MIME type. If we don't do this, we can fail to + // load Gmail attachments, because the URL being loaded doesn't + // exactly match the URL we have permission to read. int mimeIndex = url.lastIndexOf('?'); if (mimeIndex != -1) { url = url.substring(0, mimeIndex); @@ -787,101 +770,11 @@ class BrowserFrame extends Handler { } /** - * Start loading a resource. - * @param loaderHandle The native ResourceLoader that is the target of the - * data. - * @param url The url to load. - * @param method The http method. - * @param headers The http headers. - * @param postData If the method is "POST" postData is sent as the request - * body. Is null when empty. - * @param postDataIdentifier If the post data contained form this is the form identifier, otherwise it is 0. - * @param cacheMode The cache mode to use when loading this resource. See WebSettings.setCacheMode - * @param mainResource True if the this resource is the main request, not a supporting resource - * @param userGesture - * @param synchronous True if the load is synchronous. - * @return A newly created LoadListener object. - */ - private LoadListener startLoadingResource(int loaderHandle, - String url, - String method, - HashMap headers, - byte[] postData, - long postDataIdentifier, - int cacheMode, - boolean mainResource, - boolean userGesture, - boolean synchronous, - String username, - String password) { - if (mSettings.getCacheMode() != WebSettings.LOAD_DEFAULT) { - cacheMode = mSettings.getCacheMode(); - } - - if (method.equals("POST")) { - // Don't use the cache on POSTs when issuing a normal POST - // request. - if (cacheMode == WebSettings.LOAD_NORMAL) { - cacheMode = WebSettings.LOAD_NO_CACHE; - } - String[] ret = getUsernamePassword(); - if (ret != null) { - String domUsername = ret[0]; - String domPassword = ret[1]; - maybeSavePassword(postData, domUsername, domPassword); - } - } - - // is this resource the main-frame top-level page? - boolean isMainFramePage = mIsMainFrame; - - if (DebugFlags.BROWSER_FRAME) { - Log.v(LOGTAG, "startLoadingResource: url=" + url + ", method=" - + method + ", postData=" + postData + ", isMainFramePage=" - + isMainFramePage + ", mainResource=" + mainResource - + ", userGesture=" + userGesture); - } - - // Create a LoadListener - LoadListener loadListener = LoadListener.getLoadListener(mContext, - this, url, loaderHandle, synchronous, isMainFramePage, - mainResource, userGesture, postDataIdentifier, username, password); - - if (LoadListener.getNativeLoaderCount() > MAX_OUTSTANDING_REQUESTS) { - // send an error message, so that loadListener can be deleted - // after this is returned. This is important as LoadListener's - // nativeError will remove the request from its DocLoader's request - // list. But the set up is not done until this method is returned. - loadListener.error( - android.net.http.EventHandler.ERROR, mContext.getString( - com.android.internal.R.string.httpErrorTooManyRequests)); - return loadListener; - } - - // Note that we are intentionally skipping - // inputStreamForAndroidResource. This is so that FrameLoader will use - // the various StreamLoader classes to handle assets. - FrameLoader loader = new FrameLoader(loadListener, mSettings, method, - mCallbackProxy.shouldInterceptRequest(url)); - loader.setHeaders(headers); - loader.setPostData(postData); - // Set the load mode to the mode used for the current page. - // If WebKit wants validation, go to network directly. - loader.setCacheMode(headers.containsKey("If-Modified-Since") - || headers.containsKey("If-None-Match") ? - WebSettings.LOAD_NO_CACHE : cacheMode); - loader.executeLoad(); - // Set referrer to current URL? - return !synchronous ? loadListener : null; - } - - /** * If this looks like a POST request (form submission) containing a username * and password, give the user the option of saving them. Will either do * nothing, or block until the UI interaction is complete. * - * Called by startLoadingResource when using the Apache HTTP stack. - * Called directly by WebKit when using the Chrome HTTP stack. + * Called directly by WebKit. * * @param postData The data about to be sent as the body of a POST request. * @param username The username entered by the user (sniffed from the DOM). @@ -1351,15 +1244,6 @@ class BrowserFrame extends Handler { private native void nativeAddJavascriptInterface(int nativeFramePointer, Object obj, String interfaceName); - /** - * Enable or disable the native cache. - */ - /* FIXME: The native cache is always on for now until we have a better - * solution for our 2 caches. */ - private native void setCacheDisabled(boolean disabled); - - public native boolean cacheDisabled(); - public native void clearCache(); /** diff --git a/core/java/android/webkit/CacheLoader.java b/core/java/android/webkit/CacheLoader.java deleted file mode 100644 index 072104562f16..000000000000 --- a/core/java/android/webkit/CacheLoader.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.webkit; - -import android.net.http.Headers; -import android.text.TextUtils; -import android.webkit.JniUtil; - -/** - * This class is a concrete implementation of StreamLoader that uses a - * CacheResult as the source for the stream. The CacheResult stored mimetype - * and encoding is added to the HTTP response headers. - */ -class CacheLoader extends StreamLoader { - - CacheManager.CacheResult mCacheResult; // Content source - - /** - * Constructs a CacheLoader for use when loading content from the cache. - * - * @param loadListener LoadListener to pass the content to - * @param result CacheResult used as the source for the content. - */ - CacheLoader(LoadListener loadListener, CacheManager.CacheResult result) { - super(loadListener); - - assert !JniUtil.useChromiumHttpStack(); - - mCacheResult = result; - } - - @Override - protected boolean setupStreamAndSendStatus() { - mDataStream = mCacheResult.inStream; - mContentLength = mCacheResult.contentLength; - mLoadListener.status(1, 1, mCacheResult.httpStatusCode, "OK"); - return true; - } - - @Override - protected void buildHeaders(Headers headers) { - StringBuilder sb = new StringBuilder(mCacheResult.mimeType); - if (!TextUtils.isEmpty(mCacheResult.encoding)) { - sb.append(';'); - sb.append(mCacheResult.encoding); - } - headers.setContentType(sb.toString()); - - if (!TextUtils.isEmpty(mCacheResult.location)) { - headers.setLocation(mCacheResult.location); - } - - if (!TextUtils.isEmpty(mCacheResult.expiresString)) { - headers.setExpires(mCacheResult.expiresString); - } - - if (!TextUtils.isEmpty(mCacheResult.contentdisposition)) { - headers.setContentDisposition(mCacheResult.contentdisposition); - } - - if (!TextUtils.isEmpty(mCacheResult.crossDomain)) { - headers.setXPermittedCrossDomainPolicies(mCacheResult.crossDomain); - } - } -} diff --git a/core/java/android/webkit/CacheManager.java b/core/java/android/webkit/CacheManager.java index e21a02e8d8b7..6a85e0026331 100644 --- a/core/java/android/webkit/CacheManager.java +++ b/core/java/android/webkit/CacheManager.java @@ -37,18 +37,18 @@ import com.android.org.bouncycastle.crypto.Digest; import com.android.org.bouncycastle.crypto.digests.SHA1Digest; /** - * The class CacheManager provides the persistent cache of content that is - * received over the network. The component handles parsing of HTTP headers and - * utilizes the relevant cache headers to determine if the content should be - * stored and if so, how long it is valid for. Network requests are provided to - * this component and if they can not be resolved by the cache, the HTTP headers - * are attached, as appropriate, to the request for revalidation of content. The - * class also manages the cache size. - * - * CacheManager may only be used if your activity contains a WebView. - * + * Manages the HTTP cache used by an application's {@link WebView} instances. * @deprecated Access to the HTTP cache will be removed in a future release. */ +// The class CacheManager provides the persistent cache of content that is +// received over the network. The component handles parsing of HTTP headers and +// utilizes the relevant cache headers to determine if the content should be +// stored and if so, how long it is valid for. Network requests are provided to +// this component and if they can not be resolved by the cache, the HTTP headers +// are attached, as appropriate, to the request for revalidation of content. The +// class also manages the cache size. +// +// CacheManager may only be used if your activity contains a WebView. @Deprecated public final class CacheManager { @@ -57,40 +57,12 @@ public final class CacheManager { static final String HEADER_KEY_IFMODIFIEDSINCE = "if-modified-since"; static final String HEADER_KEY_IFNONEMATCH = "if-none-match"; - private static final String NO_STORE = "no-store"; - private static final String NO_CACHE = "no-cache"; - private static final String MAX_AGE = "max-age"; - private static final String MANIFEST_MIME = "text/cache-manifest"; - - private static long CACHE_THRESHOLD = 6 * 1024 * 1024; - private static long CACHE_TRIM_AMOUNT = 2 * 1024 * 1024; - - // Limit the maximum cache file size to half of the normal capacity - static long CACHE_MAX_SIZE = (CACHE_THRESHOLD - CACHE_TRIM_AMOUNT) / 2; - - private static boolean mDisabled; - - // Reference count the enable/disable transaction - private static int mRefCount; - - // trimCacheIfNeeded() is called when a page is fully loaded. But JavaScript - // can load the content, e.g. in a slideshow, continuously, so we need to - // trim the cache on a timer base too. endCacheTransaction() is called on a - // timer base. We share the same timer with less frequent update. - private static int mTrimCacheCount = 0; - private static final int TRIM_CACHE_INTERVAL = 5; - - private static WebViewDatabase mDataBase; private static File mBaseDir; - // Flag to clear the cache when the CacheManager is initialized - private static boolean mClearCacheOnInit = false; - /** - * This class represents a resource retrieved from the HTTP cache. - * Instances of this class can be obtained by invoking the - * CacheManager.getCacheFile() method. - * + * Represents a resource stored in the HTTP cache. Instances of this class + * can be obtained by calling + * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>))}. * @deprecated Access to the HTTP cache will be removed in a future release. */ @Deprecated @@ -114,64 +86,136 @@ public final class CacheManager { OutputStream outStream; File outFile; + /** + * Gets the status code of this cache entry. + * @return The status code of this cache entry + */ public int getHttpStatusCode() { return httpStatusCode; } + /** + * Gets the content length of this cache entry. + * @return The content length of this cache entry + */ public long getContentLength() { return contentLength; } + /** + * Gets the path of the file used to store the content of this cache + * entry, relative to the base directory of the cache. See + * {@link CacheManager#getCacheFileBaseDir CacheManager.getCacheFileBaseDir()}. + * @return The path of the file used to store this cache entry + */ public String getLocalPath() { return localPath; } + /** + * Gets the expiry date of this cache entry, expressed in milliseconds + * since midnight, January 1, 1970 UTC. + * @return The expiry date of this cache entry + */ public long getExpires() { return expires; } + /** + * Gets the expiry date of this cache entry, expressed as a string. + * @return The expiry date of this cache entry + * + */ public String getExpiresString() { return expiresString; } + /** + * Gets the date at which this cache entry was last modified, expressed + * as a string. + * @return The date at which this cache entry was last modified + */ public String getLastModified() { return lastModified; } + /** + * Gets the entity tag of this cache entry. + * @return The entity tag of this cache entry + */ public String getETag() { return etag; } + /** + * Gets the MIME type of this cache entry. + * @return The MIME type of this cache entry + */ public String getMimeType() { return mimeType; } + /** + * Gets the value of the HTTP 'Location' header with which this cache + * entry was received. + * @return The HTTP 'Location' header for this cache entry + */ public String getLocation() { return location; } + /** + * Gets the encoding of this cache entry. + * @return The encoding of this cache entry + */ public String getEncoding() { return encoding; } + /** + * Gets the value of the HTTP 'Content-Disposition' header with which + * this cache entry was received. + * @return The HTTP 'Content-Disposition' header for this cache entry + * + */ public String getContentDisposition() { return contentdisposition; } - // For out-of-package access to the underlying streams. + /** + * Gets the input stream to the content of this cache entry, to allow + * content to be read. See + * {@link CacheManager#getCacheFile CacheManager.getCacheFile(String, Map<String, String>)}. + * @return An input stream to the content of this cache entry + */ public InputStream getInputStream() { return inStream; } + /** + * Gets an output stream to the content of this cache entry, to allow + * content to be written. See + * {@link CacheManager#saveCacheFile CacheManager.saveCacheFile(String, CacheResult)}. + * @return An output stream to the content of this cache entry + */ + // Note that this is always null for objects returned by getCacheFile()! public OutputStream getOutputStream() { return outStream; } - // These fields can be set manually. + + /** + * Sets an input stream to the content of this cache entry. + * @param stream An input stream to the content of this cache entry + */ public void setInputStream(InputStream stream) { this.inStream = stream; } + /** + * Sets the encoding of this cache entry. + * @param encoding The encoding of this cache entry + */ public void setEncoding(String encoding) { this.encoding = encoding; } @@ -185,70 +229,25 @@ public final class CacheManager { } /** - * Initialize the CacheManager. - * - * Note that this is called automatically when a {@link android.webkit.WebView} is created. - * - * @param context The application context. + * Initializes the HTTP cache. This method must be called before any + * CacheManager methods are used. Note that this is called automatically + * when a {@link WebView} is created. + * @param context The application context */ static void init(Context context) { - if (JniUtil.useChromiumHttpStack()) { - // This isn't actually where the real cache lives, but where we put files for the - // purpose of getCacheFile(). - mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging"); - if (!mBaseDir.exists()) { - mBaseDir.mkdirs(); - } - return; - } - - mDataBase = WebViewDatabase.getInstance(context.getApplicationContext()); - mBaseDir = new File(context.getCacheDir(), "webviewCache"); - if (createCacheDirectory() && mClearCacheOnInit) { - removeAllCacheFiles(); - mClearCacheOnInit = false; - } - } - - /** - * Create the cache directory if it does not already exist. - * - * @return true if the cache directory didn't exist and was created. - */ - static private boolean createCacheDirectory() { - assert !JniUtil.useChromiumHttpStack(); - + // This isn't actually where the real cache lives, but where we put files for the + // purpose of getCacheFile(). + mBaseDir = new File(context.getCacheDir(), "webviewCacheChromiumStaging"); if (!mBaseDir.exists()) { - if(!mBaseDir.mkdirs()) { - Log.w(LOGTAG, "Unable to create webviewCache directory"); - return false; - } - FileUtils.setPermissions( - mBaseDir.toString(), - FileUtils.S_IRWXU | FileUtils.S_IRWXG, - -1, -1); - // If we did create the directory, we need to flush - // the cache database. The directory could be recreated - // because the system flushed all the data/cache directories - // to free up disk space. - // delete rows in the cache database - WebViewWorker.getHandler().sendEmptyMessage( - WebViewWorker.MSG_CLEAR_CACHE); - return true; + mBaseDir.mkdirs(); } - return false; } /** - * Get the base directory of the cache. Together with the local path of the CacheResult, - * obtained from {@link android.webkit.CacheManager.CacheResult#getLocalPath}, this - * identifies the cache file. - * - * Cache files are not guaranteed to be in this directory before - * CacheManager#getCacheFile(String, Map<String, String>) is called. - * - * @return File The base directory of the cache. - * + * Gets the base directory in which the files used to store the contents of + * cache entries are placed. See + * {@link CacheManager.CacheResult#getLocalPath CacheManager.CacheResult.getLocalPath()}. + * @return The base directory of the cache * @deprecated Access to the HTTP cache will be removed in a future release. */ @Deprecated @@ -257,93 +256,32 @@ public final class CacheManager { } /** - * Sets whether the cache is disabled. - * - * @param disabled Whether the cache should be disabled - */ - static void setCacheDisabled(boolean disabled) { - assert !JniUtil.useChromiumHttpStack(); - - if (disabled == mDisabled) { - return; - } - mDisabled = disabled; - if (mDisabled) { - removeAllCacheFiles(); - } - } - - /** - * Whether the cache is disabled. - * - * @return return Whether the cache is disabled - * + * Gets whether the HTTP cache is disabled. + * @return True if the HTTP cache is disabled * @deprecated Access to the HTTP cache will be removed in a future release. */ @Deprecated public static boolean cacheDisabled() { - return mDisabled; - } - - // only called from WebViewWorkerThread - // make sure to call enableTransaction/disableTransaction in pair - static boolean enableTransaction() { - assert !JniUtil.useChromiumHttpStack(); - - if (++mRefCount == 1) { - mDataBase.startCacheTransaction(); - return true; - } return false; } - // only called from WebViewWorkerThread - // make sure to call enableTransaction/disableTransaction in pair - static boolean disableTransaction() { - assert !JniUtil.useChromiumHttpStack(); - - if (--mRefCount == 0) { - mDataBase.endCacheTransaction(); - return true; - } - return false; - } - - // only called from WebViewWorkerThread - // make sure to call startTransaction/endTransaction in pair - static boolean startTransaction() { - assert !JniUtil.useChromiumHttpStack(); - - return mDataBase.startCacheTransaction(); - } - - // only called from WebViewWorkerThread - // make sure to call startTransaction/endTransaction in pair - static boolean endTransaction() { - assert !JniUtil.useChromiumHttpStack(); - - boolean ret = mDataBase.endCacheTransaction(); - if (++mTrimCacheCount >= TRIM_CACHE_INTERVAL) { - mTrimCacheCount = 0; - trimCacheIfNeeded(); - } - return ret; - } - - // only called from WebCore Thread - // make sure to call startCacheTransaction/endCacheTransaction in pair /** - * @deprecated Always returns false. + * Starts a cache transaction. Returns true if this is the only running + * transaction. Otherwise, this transaction is nested inside currently + * running transactions and false is returned. + * @return True if this is the only running transaction + * @deprecated This method no longer has any effect and always returns false */ @Deprecated public static boolean startCacheTransaction() { return false; } - // only called from WebCore Thread - // make sure to call startCacheTransaction/endCacheTransaction in pair /** - * @deprecated Always returns false. + * Ends the innermost cache transaction and returns whether this was the + * only running transaction. + * @return True if this was the only running transaction + * @deprecated This method no longer has any effect and always returns false */ @Deprecated public static boolean endCacheTransaction() { @@ -351,15 +289,15 @@ public final class CacheManager { } /** - * Given a URL, returns the corresponding CacheResult if it exists, or null otherwise. - * - * The input stream of the CacheEntry object is initialized and opened and should be closed by - * the caller when access to the underlying file is no longer required. - * If a non-zero value is provided for the headers map, and the cache entry needs validation, - * HEADER_KEY_IFNONEMATCH or HEADER_KEY_IFMODIFIEDSINCE will be set in headers. - * - * @return The CacheResult for the given URL - * + * Gets the cache entry for the specified URL, or null if none is found. + * If a non-null value is provided for the HTTP headers map, and the cache + * entry needs validation, appropriate headers will be added to the map. + * The input stream of the CacheEntry object should be closed by the caller + * when access to the underlying file is no longer required. + * @param url The URL for which a cache entry is requested + * @param headers A map from HTTP header name to value, to be populated + * for the returned cache entry + * @return The cache entry for the specified URL * @deprecated Access to the HTTP cache will be removed in a future release. */ @Deprecated @@ -370,54 +308,22 @@ public final class CacheManager { static CacheResult getCacheFile(String url, long postIdentifier, Map<String, String> headers) { - if (mDisabled) { - return null; - } - - if (JniUtil.useChromiumHttpStack()) { - CacheResult result = nativeGetCacheResult(url); - if (result == null) { - return null; - } - // A temporary local file will have been created native side and localPath set - // appropriately. - File src = new File(mBaseDir, result.localPath); - try { - // Open the file here so that even if it is deleted, the content - // is still readable by the caller until close() is called. - result.inStream = new FileInputStream(src); - } catch (FileNotFoundException e) { - Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e); - // TODO: The files in the cache directory can be removed by the - // system. If it is gone, what should we do? - return null; - } - return result; - } - - String databaseKey = getDatabaseKey(url, postIdentifier); - CacheResult result = mDataBase.getCache(databaseKey); + CacheResult result = nativeGetCacheResult(url); if (result == null) { return null; } - if (result.contentLength == 0) { - if (!isCachableRedirect(result.httpStatusCode)) { - // This should not happen. If it does, remove it. - mDataBase.removeCache(databaseKey); - return null; - } - } else { - File src = new File(mBaseDir, result.localPath); - try { - // Open the file here so that even if it is deleted, the content - // is still readable by the caller until close() is called. - result.inStream = new FileInputStream(src); - } catch (FileNotFoundException e) { - // The files in the cache directory can be removed by the - // system. If it is gone, clean up the database. - mDataBase.removeCache(databaseKey); - return null; - } + // A temporary local file will have been created native side and localPath set + // appropriately. + File src = new File(mBaseDir, result.localPath); + try { + // Open the file here so that even if it is deleted, the content + // is still readable by the caller until close() is called. + result.inStream = new FileInputStream(src); + } catch (FileNotFoundException e) { + Log.v(LOGTAG, "getCacheFile(): Failed to open file: " + e); + // TODO: The files in the cache directory can be removed by the + // system. If it is gone, what should we do? + return null; } // A null value for headers is used by CACHE_MODE_CACHE_ONLY to imply @@ -454,89 +360,23 @@ public final class CacheManager { * CacheResult, and is used to supply surrogate responses for URL * interception. * @return CacheResult for a given url - * @hide - hide createCacheFile since it has a parameter of type headers, which is - * in a hidden package. - * - * @deprecated Access to the HTTP cache will be removed in a future release. */ - @Deprecated - public static CacheResult createCacheFile(String url, int statusCode, - Headers headers, String mimeType, boolean forceCache) { - if (JniUtil.useChromiumHttpStack()) { - // This method is public but hidden. We break functionality. - return null; - } - - return createCacheFile(url, statusCode, headers, mimeType, 0, - forceCache); - } - static CacheResult createCacheFile(String url, int statusCode, - Headers headers, String mimeType, long postIdentifier, - boolean forceCache) { - assert !JniUtil.useChromiumHttpStack(); - - if (!forceCache && mDisabled) { - return null; - } - - String databaseKey = getDatabaseKey(url, postIdentifier); - - // according to the rfc 2616, the 303 response MUST NOT be cached. - if (statusCode == 303) { - // remove the saved cache if there is any - mDataBase.removeCache(databaseKey); - return null; - } - - // like the other browsers, do not cache redirects containing a cookie - // header. - if (isCachableRedirect(statusCode) && !headers.getSetCookie().isEmpty()) { - // remove the saved cache if there is any - mDataBase.removeCache(databaseKey); - return null; - } - - CacheResult ret = parseHeaders(statusCode, headers, mimeType); - if (ret == null) { - // this should only happen if the headers has "no-store" in the - // cache-control. remove the saved cache if there is any - mDataBase.removeCache(databaseKey); - } else { - setupFiles(databaseKey, ret); - try { - ret.outStream = new FileOutputStream(ret.outFile); - } catch (FileNotFoundException e) { - // This can happen with the system did a purge and our - // subdirectory has gone, so lets try to create it again - if (createCacheDirectory()) { - try { - ret.outStream = new FileOutputStream(ret.outFile); - } catch (FileNotFoundException e2) { - // We failed to create the file again, so there - // is something else wrong. Return null. - return null; - } - } else { - // Failed to create cache directory - return null; - } - } - ret.mimeType = mimeType; - } - - return ret; + Headers headers, String mimeType, boolean forceCache) { + // This method is public but hidden. We break functionality. + return null; } /** - * Save the info of a cache file for a given url to the CacheMap so that it - * can be reused later - * + * Adds a cache entry to the HTTP cache for the specicifed URL. Also closes + * the cache entry's output stream. + * @param url The URL for which the cache entry should be added + * @param cacheResult The cache entry to add * @deprecated Access to the HTTP cache will be removed in a future release. */ @Deprecated - public static void saveCacheFile(String url, CacheResult cacheRet) { - saveCacheFile(url, 0, cacheRet); + public static void saveCacheFile(String url, CacheResult cacheResult) { + saveCacheFile(url, 0, cacheResult); } static void saveCacheFile(String url, long postIdentifier, @@ -547,54 +387,24 @@ public final class CacheManager { return; } - if (JniUtil.useChromiumHttpStack()) { - // This method is exposed in the public API but the API provides no way to obtain a - // new CacheResult object with a non-null output stream ... - // - CacheResult objects returned by getCacheFile() have a null output stream. - // - new CacheResult objects have a null output stream and no setter is provided. - // Since for the Android HTTP stack this method throws a null pointer exception in this - // case, this method is effectively useless from the point of view of the public API. - - // We should already have thrown an exception above, to maintain 'backward - // compatibility' with the Android HTTP stack. - assert false; - } - - if (!cacheRet.outFile.exists()) { - // the file in the cache directory can be removed by the system - return; - } - - boolean redirect = isCachableRedirect(cacheRet.httpStatusCode); - if (redirect) { - // location is in database, no need to keep the file - cacheRet.contentLength = 0; - cacheRet.localPath = ""; - } - if ((redirect || cacheRet.contentLength == 0) - && !cacheRet.outFile.delete()) { - Log.e(LOGTAG, cacheRet.outFile.getPath() + " delete failed."); - } - if (cacheRet.contentLength == 0) { - return; - } - - mDataBase.addCache(getDatabaseKey(url, postIdentifier), cacheRet); - - if (DebugFlags.CACHE_MANAGER) { - Log.v(LOGTAG, "saveCacheFile for url " + url); - } - } - - static boolean cleanupCacheFile(CacheResult cacheRet) { - assert !JniUtil.useChromiumHttpStack(); - - try { - cacheRet.outStream.close(); - } catch (IOException e) { - return false; - } - return cacheRet.outFile.delete(); + // This method is exposed in the public API but the API provides no + // way to obtain a new CacheResult object with a non-null output + // stream ... + // - CacheResult objects returned by getCacheFile() have a null + // output stream. + // - new CacheResult objects have a null output stream and no + // setter is provided. + // Since this method throws a null pointer exception in this case, + // it is effectively useless from the point of view of the public + // API. + // + // With the Chromium HTTP stack we continue to throw the same + // exception for 'backwards compatibility' with the Android HTTP + // stack. + // + // This method is not used from within this package, and for public API + // use, we should already have thrown an exception above. + assert false; } /** @@ -603,21 +413,6 @@ public final class CacheManager { * @return Whether the removal succeeded. */ static boolean removeAllCacheFiles() { - // Note, this is called before init() when the database is - // created or upgraded. - if (mBaseDir == null) { - // This method should not be called before init() when using the - // chrome http stack - assert !JniUtil.useChromiumHttpStack(); - // Init() has not been called yet, so just flag that - // we need to clear the cache when init() is called. - mClearCacheOnInit = true; - return true; - } - // delete rows in the cache database - if (!JniUtil.useChromiumHttpStack()) - WebViewWorker.getHandler().sendEmptyMessage(WebViewWorker.MSG_CLEAR_CACHE); - // delete cache files in a separate thread to not block UI. final Runnable clearCache = new Runnable() { public void run() { @@ -642,323 +437,5 @@ public final class CacheManager { return true; } - static void trimCacheIfNeeded() { - assert !JniUtil.useChromiumHttpStack(); - - if (mDataBase.getCacheTotalSize() > CACHE_THRESHOLD) { - List<String> pathList = mDataBase.trimCache(CACHE_TRIM_AMOUNT); - int size = pathList.size(); - for (int i = 0; i < size; i++) { - File f = new File(mBaseDir, pathList.get(i)); - if (!f.delete()) { - Log.e(LOGTAG, f.getPath() + " delete failed."); - } - } - // remove the unreferenced files in the cache directory - final List<String> fileList = mDataBase.getAllCacheFileNames(); - if (fileList == null) return; - String[] toDelete = mBaseDir.list(new FilenameFilter() { - public boolean accept(File dir, String filename) { - if (fileList.contains(filename)) { - return false; - } else { - return true; - } - } - }); - if (toDelete == null) return; - size = toDelete.length; - for (int i = 0; i < size; i++) { - File f = new File(mBaseDir, toDelete[i]); - if (!f.delete()) { - Log.e(LOGTAG, f.getPath() + " delete failed."); - } - } - } - } - - static void clearCache() { - assert !JniUtil.useChromiumHttpStack(); - - // delete database - mDataBase.clearCache(); - } - - private static boolean isCachableRedirect(int statusCode) { - if (statusCode == 301 || statusCode == 302 || statusCode == 307) { - // as 303 can't be cached, we do not return true - return true; - } else { - return false; - } - } - - private static String getDatabaseKey(String url, long postIdentifier) { - assert !JniUtil.useChromiumHttpStack(); - - if (postIdentifier == 0) return url; - return postIdentifier + url; - } - - @SuppressWarnings("deprecation") - private static void setupFiles(String url, CacheResult cacheRet) { - assert !JniUtil.useChromiumHttpStack(); - - if (true) { - // Note: SHA1 is much stronger hash. But the cost of setupFiles() is - // 3.2% cpu time for a fresh load of nytimes.com. While a simple - // String.hashCode() is only 0.6%. If adding the collision resolving - // to String.hashCode(), it makes the cpu time to be 1.6% for a - // fresh load, but 5.3% for the worst case where all the files - // already exist in the file system, but database is gone. So it - // needs to resolve collision for every file at least once. - int hashCode = url.hashCode(); - StringBuffer ret = new StringBuffer(8); - appendAsHex(hashCode, ret); - String path = ret.toString(); - File file = new File(mBaseDir, path); - if (true) { - boolean checkOldPath = true; - // Check hash collision. If the hash file doesn't exist, just - // continue. There is a chance that the old cache file is not - // same as the hash file. As mDataBase.getCache() is more - // expansive than "leak" a file until clear cache, don't bother. - // If the hash file exists, make sure that it is same as the - // cache file. If it is not, resolve the collision. - while (file.exists()) { - if (checkOldPath) { - CacheResult oldResult = mDataBase.getCache(url); - if (oldResult != null && oldResult.contentLength > 0) { - if (path.equals(oldResult.localPath)) { - path = oldResult.localPath; - } else { - path = oldResult.localPath; - file = new File(mBaseDir, path); - } - break; - } - checkOldPath = false; - } - ret = new StringBuffer(8); - appendAsHex(++hashCode, ret); - path = ret.toString(); - file = new File(mBaseDir, path); - } - } - cacheRet.localPath = path; - cacheRet.outFile = file; - } else { - // get hash in byte[] - Digest digest = new SHA1Digest(); - int digestLen = digest.getDigestSize(); - byte[] hash = new byte[digestLen]; - int urlLen = url.length(); - byte[] data = new byte[urlLen]; - url.getBytes(0, urlLen, data, 0); - digest.update(data, 0, urlLen); - digest.doFinal(hash, 0); - // convert byte[] to hex String - StringBuffer result = new StringBuffer(2 * digestLen); - for (int i = 0; i < digestLen; i = i + 4) { - int h = (0x00ff & hash[i]) << 24 | (0x00ff & hash[i + 1]) << 16 - | (0x00ff & hash[i + 2]) << 8 | (0x00ff & hash[i + 3]); - appendAsHex(h, result); - } - cacheRet.localPath = result.toString(); - cacheRet.outFile = new File(mBaseDir, cacheRet.localPath); - } - } - - private static void appendAsHex(int i, StringBuffer ret) { - assert !JniUtil.useChromiumHttpStack(); - - String hex = Integer.toHexString(i); - switch (hex.length()) { - case 1: - ret.append("0000000"); - break; - case 2: - ret.append("000000"); - break; - case 3: - ret.append("00000"); - break; - case 4: - ret.append("0000"); - break; - case 5: - ret.append("000"); - break; - case 6: - ret.append("00"); - break; - case 7: - ret.append("0"); - break; - } - ret.append(hex); - } - - private static CacheResult parseHeaders(int statusCode, Headers headers, - String mimeType) { - assert !JniUtil.useChromiumHttpStack(); - - // if the contentLength is already larger than CACHE_MAX_SIZE, skip it - if (headers.getContentLength() > CACHE_MAX_SIZE) return null; - - // The HTML 5 spec, section 6.9.4, step 7.3 of the application cache - // process states that HTTP caching rules are ignored for the - // purposes of the application cache download process. - // At this point we can't tell that if a file is part of this process, - // except for the manifest, which has its own mimeType. - // TODO: work out a way to distinguish all responses that are part of - // the application download process and skip them. - if (MANIFEST_MIME.equals(mimeType)) return null; - - // TODO: if authenticated or secure, return null - CacheResult ret = new CacheResult(); - ret.httpStatusCode = statusCode; - - ret.location = headers.getLocation(); - - ret.expires = -1; - ret.expiresString = headers.getExpires(); - if (ret.expiresString != null) { - try { - ret.expires = AndroidHttpClient.parseDate(ret.expiresString); - } catch (IllegalArgumentException ex) { - // Take care of the special "-1" and "0" cases - if ("-1".equals(ret.expiresString) - || "0".equals(ret.expiresString)) { - // make it expired, but can be used for history navigation - ret.expires = 0; - } else { - Log.e(LOGTAG, "illegal expires: " + ret.expiresString); - } - } - } - - ret.contentdisposition = headers.getContentDisposition(); - - ret.crossDomain = headers.getXPermittedCrossDomainPolicies(); - - // lastModified and etag may be set back to http header. So they can't - // be empty string. - String lastModified = headers.getLastModified(); - if (lastModified != null && lastModified.length() > 0) { - ret.lastModified = lastModified; - } - - String etag = headers.getEtag(); - if (etag != null && etag.length() > 0) { - ret.etag = etag; - } - - String cacheControl = headers.getCacheControl(); - if (cacheControl != null) { - String[] controls = cacheControl.toLowerCase().split("[ ,;]"); - boolean noCache = false; - for (int i = 0; i < controls.length; i++) { - if (NO_STORE.equals(controls[i])) { - return null; - } - // According to the spec, 'no-cache' means that the content - // must be re-validated on every load. It does not mean that - // the content can not be cached. set to expire 0 means it - // can only be used in CACHE_MODE_CACHE_ONLY case - if (NO_CACHE.equals(controls[i])) { - ret.expires = 0; - noCache = true; - // if cache control = no-cache has been received, ignore max-age - // header, according to http spec: - // If a request includes the no-cache directive, it SHOULD NOT - // include min-fresh, max-stale, or max-age. - } else if (controls[i].startsWith(MAX_AGE) && !noCache) { - int separator = controls[i].indexOf('='); - if (separator < 0) { - separator = controls[i].indexOf(':'); - } - if (separator > 0) { - String s = controls[i].substring(separator + 1); - try { - long sec = Long.parseLong(s); - if (sec >= 0) { - ret.expires = System.currentTimeMillis() + 1000 - * sec; - } - } catch (NumberFormatException ex) { - if ("1d".equals(s)) { - // Take care of the special "1d" case - ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000 - } else { - Log.e(LOGTAG, "exception in parseHeaders for " - + "max-age:" - + controls[i].substring(separator + 1)); - ret.expires = 0; - } - } - } - } - } - } - - // According to RFC 2616 section 14.32: - // HTTP/1.1 caches SHOULD treat "Pragma: no-cache" as if the - // client had sent "Cache-Control: no-cache" - if (NO_CACHE.equals(headers.getPragma())) { - ret.expires = 0; - } - - // According to RFC 2616 section 13.2.4, if an expiration has not been - // explicitly defined a heuristic to set an expiration may be used. - if (ret.expires == -1) { - if (ret.httpStatusCode == 301) { - // If it is a permanent redirect, and it did not have an - // explicit cache directive, then it never expires - ret.expires = Long.MAX_VALUE; - } else if (ret.httpStatusCode == 302 || ret.httpStatusCode == 307) { - // If it is temporary redirect, expires - ret.expires = 0; - } else if (ret.lastModified == null) { - // When we have no last-modified, then expire the content with - // in 24hrs as, according to the RFC, longer time requires a - // warning 113 to be added to the response. - - // Only add the default expiration for non-html markup. Some - // sites like news.google.com have no cache directives. - if (!mimeType.startsWith("text/html")) { - ret.expires = System.currentTimeMillis() + 86400000; // 24*60*60*1000 - } else { - // Setting a expires as zero will cache the result for - // forward/back nav. - ret.expires = 0; - } - } else { - // If we have a last-modified value, we could use it to set the - // expiration. Suggestion from RFC is 10% of time since - // last-modified. As we are on mobile, loads are expensive, - // increasing this to 20%. - - // 24 * 60 * 60 * 1000 - long lastmod = System.currentTimeMillis() + 86400000; - try { - lastmod = AndroidHttpClient.parseDate(ret.lastModified); - } catch (IllegalArgumentException ex) { - Log.e(LOGTAG, "illegal lastModified: " + ret.lastModified); - } - long difference = System.currentTimeMillis() - lastmod; - if (difference > 0) { - ret.expires = System.currentTimeMillis() + difference / 5; - } else { - // last modified is in the future, expire the content - // on the last modified - ret.expires = lastmod; - } - } - } - - return ret; - } - private static native CacheResult nativeGetCacheResult(String url); } diff --git a/core/java/android/webkit/CallbackProxy.java b/core/java/android/webkit/CallbackProxy.java index 75ee33841203..3a05bcab12e8 100644 --- a/core/java/android/webkit/CallbackProxy.java +++ b/core/java/android/webkit/CallbackProxy.java @@ -920,7 +920,6 @@ class CallbackProxy extends Handler { if (PERF_PROBE) { mWebCoreThreadTime = SystemClock.currentThreadTimeMillis(); mWebCoreIdleTime = 0; - Network.getInstance(mContext).startTiming(); // un-comment this if PERF_PROBE is true // Looper.myQueue().setWaitCallback(mIdleCallback); } @@ -938,7 +937,6 @@ class CallbackProxy extends Handler { Log.d("WebCore", "WebCore thread used " + (SystemClock.currentThreadTimeMillis() - mWebCoreThreadTime) + " ms and idled " + mWebCoreIdleTime + " ms"); - Network.getInstance(mContext).stopTiming(); } Message msg = obtainMessage(PAGE_FINISHED, url); sendMessage(msg); diff --git a/core/java/android/webkit/ContentLoader.java b/core/java/android/webkit/ContentLoader.java deleted file mode 100644 index d13210aa2a0b..000000000000 --- a/core/java/android/webkit/ContentLoader.java +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright (C) 2008 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.webkit; - -import android.net.http.EventHandler; -import android.net.http.Headers; -import android.net.Uri; - -/** - * This class is a concrete implementation of StreamLoader that loads - * "content:" URIs - */ -class ContentLoader extends StreamLoader { - - private String mUrl; - private String mContentType; - - /** - * Construct a ContentLoader with the specified content URI - * - * @param rawUrl "content:" url pointing to content to be loaded. This url - * is the same url passed in to the WebView. - * @param loadListener LoadListener to pass the content to - */ - ContentLoader(String rawUrl, LoadListener loadListener) { - super(loadListener); - - /* strip off mimetype */ - int mimeIndex = rawUrl.lastIndexOf('?'); - if (mimeIndex != -1) { - mUrl = rawUrl.substring(0, mimeIndex); - mContentType = rawUrl.substring(mimeIndex + 1); - } else { - mUrl = rawUrl; - } - - } - - private String errString(Exception ex) { - String exMessage = ex.getMessage(); - String errString = mContext.getString( - com.android.internal.R.string.httpErrorFileNotFound); - if (exMessage != null) { - errString += " " + exMessage; - } - return errString; - } - - @Override - protected boolean setupStreamAndSendStatus() { - Uri uri = Uri.parse(mUrl); - if (uri == null) { - mLoadListener.error( - EventHandler.FILE_NOT_FOUND_ERROR, - mContext.getString( - com.android.internal.R.string.httpErrorBadUrl) + - " " + mUrl); - return false; - } - - try { - mDataStream = mContext.getContentResolver().openInputStream(uri); - mLoadListener.status(1, 1, 200, "OK"); - } catch (java.io.FileNotFoundException ex) { - mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex)); - return false; - } catch (RuntimeException ex) { - // readExceptionWithFileNotFoundExceptionFromParcel in DatabaseUtils - // can throw a serial of RuntimeException. Catch them all here. - mLoadListener.error(EventHandler.FILE_ERROR, errString(ex)); - return false; - } - return true; - } - - @Override - protected void buildHeaders(Headers headers) { - if (mContentType != null) { - headers.setContentType("text/html"); - } - // content can change, we don't want WebKit to cache it - headers.setCacheControl("no-store, no-cache"); - } -} diff --git a/core/java/android/webkit/CookieManager.java b/core/java/android/webkit/CookieManager.java index 9fa5593e7ad9..497cab7d2392 100644 --- a/core/java/android/webkit/CookieManager.java +++ b/core/java/android/webkit/CookieManager.java @@ -34,7 +34,8 @@ import java.util.SortedSet; import java.util.TreeSet; /** - * CookieManager manages cookies according to RFC2109 spec. + * Manages the cookies used by an application's {@link WebView} instances. + * Cookies are manipulated according to RFC2109. */ public final class CookieManager { @@ -94,12 +95,7 @@ public final class CookieManager { // max domain count to limit RAM cookie takes less than 100k, private static final int MAX_RAM_DOMAIN_COUNT = 15; - private Map<String, ArrayList<Cookie>> mCookieMap = new LinkedHashMap - <String, ArrayList<Cookie>>(MAX_DOMAIN_COUNT, 0.75f, true); - - private boolean mAcceptCookie = true; - - private int pendingCookieOperations = 0; + private int mPendingCookieOperations = 0; /** * This contains a list of 2nd-level domains that aren't allowed to have @@ -246,12 +242,12 @@ public final class CookieManager { } /** - * Get a singleton CookieManager. If this is called before any - * {@link WebView} is created or outside of {@link WebView} context, the - * caller needs to call {@link CookieSyncManager#createInstance(Context)} + * Gets the singleton CookieManager instance. If this method is used + * before the application instantiates a {@link WebView} instance, + * {@link CookieSyncManager#createInstance(Context)} must be called * first. * - * @return CookieManager + * @return The singleton CookieManager instance */ public static synchronized CookieManager getInstance() { if (sRef == null) { @@ -261,69 +257,45 @@ public final class CookieManager { } /** - * Control whether cookie is enabled or disabled - * @param accept TRUE if accept cookie + * Sets whether the application's {@link WebView} instances should send and + * accept cookies. + * @param accept Whether {@link WebView} instances should send and accept + * cookies */ public synchronized void setAcceptCookie(boolean accept) { - if (JniUtil.useChromiumHttpStack()) { - nativeSetAcceptCookie(accept); - return; - } - - mAcceptCookie = accept; + nativeSetAcceptCookie(accept); } /** - * Return whether cookie is enabled - * @return TRUE if accept cookie + * Gets whether the application's {@link WebView} instances send and accept + * cookies. + * @return True if {@link WebView} instances send and accept cookies */ public synchronized boolean acceptCookie() { - if (JniUtil.useChromiumHttpStack()) { - return nativeAcceptCookie(); - } - - return mAcceptCookie; + return nativeAcceptCookie(); } /** - * Set cookie for a given url. The old cookie with same host/path/name will - * be removed. The new cookie will be added if it is not expired or it does - * not have expiration which implies it is session cookie. - * @param url The url which cookie is set for - * @param value The value for set-cookie: in http response header + * Sets a cookie for the given URL. Any existing cookie with the same host, + * path and name will be replaced with the new cookie. The cookie being set + * must not have expired and must not be a session cookie, otherwise it + * will be ignored. + * @param url The URL for which the cookie is set + * @param value The cookie as a string, using the format of the + * 'Set-Cookie' HTTP response header */ public void setCookie(String url, String value) { - if (JniUtil.useChromiumHttpStack()) { - setCookie(url, value, false); - return; - } - - WebAddress uri; - try { - uri = new WebAddress(url); - } catch (ParseException ex) { - Log.e(LOGTAG, "Bad address: " + url); - return; - } - - setCookie(uri, value); + setCookie(url, value, false); } /** - * Set cookie for a given url. The old cookie with same host/path/name will - * be removed. The new cookie will be added if it is not expired or it does - * not have expiration which implies it is session cookie. - * @param url The url which cookie is set for - * @param value The value for set-cookie: in http response header - * @param privateBrowsing cookie jar to use - * @hide hiding private browsing + * See {@link setCookie(String, String)} + * @param url The URL for which the cookie is set + * @param value The value of the cookie, as a string, using the format of + * the 'Set-Cookie' HTTP response header + * @param privateBrowsing Whether to use the private browsing cookie jar */ - public void setCookie(String url, String value, boolean privateBrowsing) { - if (!JniUtil.useChromiumHttpStack()) { - setCookie(url, value); - return; - } - + void setCookie(String url, String value, boolean privateBrowsing) { WebAddress uri; try { uri = new WebAddress(url); @@ -336,155 +308,24 @@ public final class CookieManager { } /** - * Set cookie for a given uri. The old cookie with same host/path/name will - * be removed. The new cookie will be added if it is not expired or it does - * not have expiration which implies it is session cookie. - * @param uri The uri which cookie is set for - * @param value The value for set-cookie: in http response header - * @hide - hide this because it takes in a parameter of type WebAddress, - * a system private class. - */ - public synchronized void setCookie(WebAddress uri, String value) { - if (JniUtil.useChromiumHttpStack()) { - nativeSetCookie(uri.toString(), value, false); - return; - } - - if (value != null && value.length() > MAX_COOKIE_LENGTH) { - return; - } - if (!mAcceptCookie || uri == null) { - return; - } - if (DebugFlags.COOKIE_MANAGER) { - Log.v(LOGTAG, "setCookie: uri: " + uri + " value: " + value); - } - - String[] hostAndPath = getHostAndPath(uri); - if (hostAndPath == null) { - return; - } - - // For default path, when setting a cookie, the spec says: - //Path: Defaults to the path of the request URL that generated the - // Set-Cookie response, up to, but not including, the - // right-most /. - if (hostAndPath[1].length() > 1) { - int index = hostAndPath[1].lastIndexOf(PATH_DELIM); - hostAndPath[1] = hostAndPath[1].substring(0, - index > 0 ? index : index + 1); - } - - ArrayList<Cookie> cookies = null; - try { - cookies = parseCookie(hostAndPath[0], hostAndPath[1], value); - } catch (RuntimeException ex) { - Log.e(LOGTAG, "parse cookie failed for: " + value); - } - - if (cookies == null || cookies.size() == 0) { - return; - } - - String baseDomain = getBaseDomain(hostAndPath[0]); - ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain); - if (cookieList == null) { - cookieList = CookieSyncManager.getInstance() - .getCookiesForDomain(baseDomain); - mCookieMap.put(baseDomain, cookieList); - } - - long now = System.currentTimeMillis(); - int size = cookies.size(); - for (int i = 0; i < size; i++) { - Cookie cookie = cookies.get(i); - - boolean done = false; - Iterator<Cookie> iter = cookieList.iterator(); - while (iter.hasNext()) { - Cookie cookieEntry = iter.next(); - if (cookie.exactMatch(cookieEntry)) { - // expires == -1 means no expires defined. Otherwise - // negative means far future - if (cookie.expires < 0 || cookie.expires > now) { - // secure cookies can't be overwritten by non-HTTPS url - if (!cookieEntry.secure || HTTPS.equals(uri.getScheme())) { - cookieEntry.value = cookie.value; - cookieEntry.expires = cookie.expires; - cookieEntry.secure = cookie.secure; - cookieEntry.lastAcessTime = now; - cookieEntry.lastUpdateTime = now; - cookieEntry.mode = Cookie.MODE_REPLACED; - } - } else { - cookieEntry.lastUpdateTime = now; - cookieEntry.mode = Cookie.MODE_DELETED; - } - done = true; - break; - } - } - - // expires == -1 means no expires defined. Otherwise negative means - // far future - if (!done && (cookie.expires < 0 || cookie.expires > now)) { - cookie.lastAcessTime = now; - cookie.lastUpdateTime = now; - cookie.mode = Cookie.MODE_NEW; - if (cookieList.size() > MAX_COOKIE_COUNT_PER_BASE_DOMAIN) { - Cookie toDelete = new Cookie(); - toDelete.lastAcessTime = now; - Iterator<Cookie> iter2 = cookieList.iterator(); - while (iter2.hasNext()) { - Cookie cookieEntry2 = iter2.next(); - if ((cookieEntry2.lastAcessTime < toDelete.lastAcessTime) - && cookieEntry2.mode != Cookie.MODE_DELETED) { - toDelete = cookieEntry2; - } - } - toDelete.mode = Cookie.MODE_DELETED; - } - cookieList.add(cookie); - } - } - } - - /** - * Get cookie(s) for a given url so that it can be set to "cookie:" in http - * request header. - * @param url The url needs cookie - * @return The cookies in the format of NAME=VALUE [; NAME=VALUE] + * Gets the cookies for the given URL. + * @param url The URL for which the cookies are requested + * @return value The cookies as a string, using the format of the 'Cookie' + * HTTP request header */ public String getCookie(String url) { - if (JniUtil.useChromiumHttpStack()) { - return getCookie(url, false); - } - - WebAddress uri; - try { - uri = new WebAddress(url); - } catch (ParseException ex) { - Log.e(LOGTAG, "Bad address: " + url); - return null; - } - - return getCookie(uri); + return getCookie(url, false); } /** - * Get cookie(s) for a given url so that it can be set to "cookie:" in http - * request header. - * @param url The url needs cookie - * @param privateBrowsing cookie jar to use - * @return The cookies in the format of NAME=VALUE [; NAME=VALUE] - * @hide Private mode is not very well exposed for now + * See {@link getCookie(String)} + * @param url The URL for which the cookies are requested + * @param privateBrowsing Whether to use the private browsing cookie jar + * @return value The cookies as a string, using the format of the 'Cookie' + * HTTP request header + * @hide Used by Browser, no intention to publish. */ public String getCookie(String url, boolean privateBrowsing) { - if (!JniUtil.useChromiumHttpStack()) { - // Just redirect to regular get cookie for android stack - return getCookie(url); - } - WebAddress uri; try { uri = new WebAddress(url); @@ -499,93 +340,23 @@ public final class CookieManager { /** * Get cookie(s) for a given uri so that it can be set to "cookie:" in http * request header. - * @param uri The uri needs cookie - * @return The cookies in the format of NAME=VALUE [; NAME=VALUE] - * @hide - hide this because it has a parameter of type WebAddress, which - * is a system private class. + * @param uri The WebAddress for which the cookies are requested + * @return value The cookies as a string, using the format of the 'Cookie' + * HTTP request header + * @hide Used by RequestHandle, no intention to publish. */ public synchronized String getCookie(WebAddress uri) { - if (JniUtil.useChromiumHttpStack()) { - return nativeGetCookie(uri.toString(), false); - } - - if (!mAcceptCookie || uri == null) { - return null; - } - - String[] hostAndPath = getHostAndPath(uri); - if (hostAndPath == null) { - return null; - } - - String baseDomain = getBaseDomain(hostAndPath[0]); - ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain); - if (cookieList == null) { - cookieList = CookieSyncManager.getInstance() - .getCookiesForDomain(baseDomain); - mCookieMap.put(baseDomain, cookieList); - } - - long now = System.currentTimeMillis(); - boolean secure = HTTPS.equals(uri.getScheme()); - Iterator<Cookie> iter = cookieList.iterator(); - - SortedSet<Cookie> cookieSet = new TreeSet<Cookie>(COMPARATOR); - while (iter.hasNext()) { - Cookie cookie = iter.next(); - if (cookie.domainMatch(hostAndPath[0]) && - cookie.pathMatch(hostAndPath[1]) - // expires == -1 means no expires defined. Otherwise - // negative means far future - && (cookie.expires < 0 || cookie.expires > now) - && (!cookie.secure || secure) - && cookie.mode != Cookie.MODE_DELETED) { - cookie.lastAcessTime = now; - cookieSet.add(cookie); - } - } - - StringBuilder ret = new StringBuilder(256); - Iterator<Cookie> setIter = cookieSet.iterator(); - while (setIter.hasNext()) { - Cookie cookie = setIter.next(); - if (ret.length() > 0) { - ret.append(SEMICOLON); - // according to RC2109, SEMICOLON is official separator, - // but when log in yahoo.com, it needs WHITE_SPACE too. - ret.append(WHITE_SPACE); - } - - ret.append(cookie.name); - if (cookie.value != null) { - ret.append(EQUAL); - ret.append(cookie.value); - } - } - - if (ret.length() > 0) { - if (DebugFlags.COOKIE_MANAGER) { - Log.v(LOGTAG, "getCookie: uri: " + uri + " value: " + ret); - } - return ret.toString(); - } else { - if (DebugFlags.COOKIE_MANAGER) { - Log.v(LOGTAG, "getCookie: uri: " + uri - + " But can't find cookie."); - } - return null; - } + return nativeGetCookie(uri.toString(), false); } /** * Waits for pending operations to completed. - * {@hide} Too late to release publically. */ - public void waitForCookieOperationsToComplete() { + void waitForCookieOperationsToComplete() { // Note that this function is applicable for both the java // and native http stacks, and works correctly with either. synchronized (this) { - while (pendingCookieOperations > 0) { + while (mPendingCookieOperations > 0) { try { wait(); } catch (InterruptedException e) { } @@ -594,131 +365,59 @@ public final class CookieManager { } private synchronized void signalCookieOperationsComplete() { - pendingCookieOperations--; - assert pendingCookieOperations > -1; + mPendingCookieOperations--; + assert mPendingCookieOperations > -1; notify(); } private synchronized void signalCookieOperationsStart() { - pendingCookieOperations++; + mPendingCookieOperations++; } /** - * Remove all session cookies, which are cookies without expiration date + * Removes all session cookies, which are cookies without an expiration + * date. */ public void removeSessionCookie() { signalCookieOperationsStart(); - if (JniUtil.useChromiumHttpStack()) { - new AsyncTask<Void, Void, Void>() { - protected Void doInBackground(Void... none) { - nativeRemoveSessionCookie(); - signalCookieOperationsComplete(); - return null; - } - }.execute(); - return; - } - - final Runnable clearCache = new Runnable() { - public void run() { - synchronized(CookieManager.this) { - Collection<ArrayList<Cookie>> cookieList = mCookieMap.values(); - Iterator<ArrayList<Cookie>> listIter = cookieList.iterator(); - while (listIter.hasNext()) { - ArrayList<Cookie> list = listIter.next(); - Iterator<Cookie> iter = list.iterator(); - while (iter.hasNext()) { - Cookie cookie = iter.next(); - if (cookie.expires == -1) { - iter.remove(); - } - } - } - CookieSyncManager.getInstance().clearSessionCookies(); - signalCookieOperationsComplete(); - } + new AsyncTask<Void, Void, Void>() { + protected Void doInBackground(Void... none) { + nativeRemoveSessionCookie(); + signalCookieOperationsComplete(); + return null; } - }; - new Thread(clearCache).start(); + }.execute(); } /** - * Remove all cookies + * Removes all cookies. */ public void removeAllCookie() { - if (JniUtil.useChromiumHttpStack()) { - nativeRemoveAllCookie(); - return; - } - - final Runnable clearCache = new Runnable() { - public void run() { - synchronized(CookieManager.this) { - mCookieMap = new LinkedHashMap<String, ArrayList<Cookie>>( - MAX_DOMAIN_COUNT, 0.75f, true); - CookieSyncManager.getInstance().clearAllCookies(); - } - } - }; - new Thread(clearCache).start(); + nativeRemoveAllCookie(); } /** - * Return true if there are stored cookies. + * Gets whether there are stored cookies. + * @return True if there are stored cookies. */ public synchronized boolean hasCookies() { - if (JniUtil.useChromiumHttpStack()) { - return hasCookies(false); - } - - return CookieSyncManager.getInstance().hasCookies(); + return hasCookies(false); } /** - * Return true if there are stored cookies. - * @param privateBrowsing cookie jar to use - * @hide Hiding private mode + * See {@link hasCookies()}. + * @param privateBrowsing Whether to use the private browsing cookie jar + * @hide Used by Browser, no intention to publish. */ public synchronized boolean hasCookies(boolean privateBrowsing) { - if (!JniUtil.useChromiumHttpStack()) { - return hasCookies(); - } - return nativeHasCookies(privateBrowsing); } /** - * Remove all expired cookies + * Removes all expired cookies. */ public void removeExpiredCookie() { - if (JniUtil.useChromiumHttpStack()) { - nativeRemoveExpiredCookie(); - return; - } - - final Runnable clearCache = new Runnable() { - public void run() { - synchronized(CookieManager.this) { - long now = System.currentTimeMillis(); - Collection<ArrayList<Cookie>> cookieList = mCookieMap.values(); - Iterator<ArrayList<Cookie>> listIter = cookieList.iterator(); - while (listIter.hasNext()) { - ArrayList<Cookie> list = listIter.next(); - Iterator<Cookie> iter = list.iterator(); - while (iter.hasNext()) { - Cookie cookie = iter.next(); - // expires == -1 means no expires defined. Otherwise - // negative means far future - if (cookie.expires > 0 && cookie.expires < now) { - iter.remove(); - } - } - } - CookieSyncManager.getInstance().clearExpiredCookies(now); - } - } - }; - new Thread(clearCache).start(); + nativeRemoveExpiredCookie(); } /** @@ -727,483 +426,31 @@ public final class CookieManager { * Flush all cookies managed by the Chrome HTTP stack to flash. */ void flushCookieStore() { - if (JniUtil.useChromiumHttpStack()) { - nativeFlushCookieStore(); - } + nativeFlushCookieStore(); } /** - * Whether cookies are accepted for file scheme URLs. + * Gets whether the application's {@link WebView} instances send and accept + * cookies for file scheme URLs. + * @return True if {@link WebView} instances send and accept cookies for + * file scheme URLs */ public static boolean allowFileSchemeCookies() { - if (JniUtil.useChromiumHttpStack()) { - return nativeAcceptFileSchemeCookies(); - } else { - return true; - } + return nativeAcceptFileSchemeCookies(); } /** - * Sets whether cookies are accepted for file scheme URLs. - * - * Use of cookies with file scheme URLs is potentially insecure. Do not use this feature unless - * you can be sure that no unintentional sharing of cookie data can take place. + * Sets whether the application's {@link WebView} instances should send and + * accept cookies for file scheme URLs. + * Use of cookies with file scheme URLs is potentially insecure. Do not use + * this feature unless you can be sure that no unintentional sharing of + * cookie data can take place. * <p> - * Note that calls to this method will have no effect if made after a WebView or CookieManager - * instance has been created. + * Note that calls to this method will have no effect if made after a + * {@link WebView} or CookieManager instance has been created. */ public static void setAcceptFileSchemeCookies(boolean accept) { - if (JniUtil.useChromiumHttpStack()) { - nativeSetAcceptFileSchemeCookies(accept); - } - } - - /** - * Package level api, called from CookieSyncManager - * - * Get a list of cookies which are updated since a given time. - * @param last The given time in millisec - * @return A list of cookies - */ - synchronized ArrayList<Cookie> getUpdatedCookiesSince(long last) { - ArrayList<Cookie> cookies = new ArrayList<Cookie>(); - Collection<ArrayList<Cookie>> cookieList = mCookieMap.values(); - Iterator<ArrayList<Cookie>> listIter = cookieList.iterator(); - while (listIter.hasNext()) { - ArrayList<Cookie> list = listIter.next(); - Iterator<Cookie> iter = list.iterator(); - while (iter.hasNext()) { - Cookie cookie = iter.next(); - if (cookie.lastUpdateTime > last) { - cookies.add(cookie); - } - } - } - return cookies; - } - - /** - * Package level api, called from CookieSyncManager - * - * Delete a Cookie in the RAM - * @param cookie Cookie to be deleted - */ - synchronized void deleteACookie(Cookie cookie) { - if (cookie.mode == Cookie.MODE_DELETED) { - String baseDomain = getBaseDomain(cookie.domain); - ArrayList<Cookie> cookieList = mCookieMap.get(baseDomain); - if (cookieList != null) { - cookieList.remove(cookie); - if (cookieList.isEmpty()) { - mCookieMap.remove(baseDomain); - } - } - } - } - - /** - * Package level api, called from CookieSyncManager - * - * Called after a cookie is synced to FLASH - * @param cookie Cookie to be synced - */ - synchronized void syncedACookie(Cookie cookie) { - cookie.mode = Cookie.MODE_NORMAL; - } - - /** - * Package level api, called from CookieSyncManager - * - * Delete the least recent used domains if the total cookie count in RAM - * exceeds the limit - * @return A list of cookies which are removed from RAM - */ - synchronized ArrayList<Cookie> deleteLRUDomain() { - int count = 0; - int byteCount = 0; - int mapSize = mCookieMap.size(); - - if (mapSize < MAX_RAM_DOMAIN_COUNT) { - Collection<ArrayList<Cookie>> cookieLists = mCookieMap.values(); - Iterator<ArrayList<Cookie>> listIter = cookieLists.iterator(); - while (listIter.hasNext() && count < MAX_RAM_COOKIES_COUNT) { - ArrayList<Cookie> list = listIter.next(); - if (DebugFlags.COOKIE_MANAGER) { - Iterator<Cookie> iter = list.iterator(); - while (iter.hasNext() && count < MAX_RAM_COOKIES_COUNT) { - Cookie cookie = iter.next(); - // 14 is 3 * sizeof(long) + sizeof(boolean) - // + sizeof(byte) - byteCount += cookie.domain.length() - + cookie.path.length() - + cookie.name.length() - + (cookie.value != null - ? cookie.value.length() - : 0) - + 14; - count++; - } - } else { - count += list.size(); - } - } - } - - ArrayList<Cookie> retlist = new ArrayList<Cookie>(); - if (mapSize >= MAX_RAM_DOMAIN_COUNT || count >= MAX_RAM_COOKIES_COUNT) { - if (DebugFlags.COOKIE_MANAGER) { - Log.v(LOGTAG, count + " cookies used " + byteCount - + " bytes with " + mapSize + " domains"); - } - Object[] domains = mCookieMap.keySet().toArray(); - int toGo = mapSize / 10 + 1; - while (toGo-- > 0){ - String domain = domains[toGo].toString(); - if (DebugFlags.COOKIE_MANAGER) { - Log.v(LOGTAG, "delete domain: " + domain - + " from RAM cache"); - } - retlist.addAll(mCookieMap.get(domain)); - mCookieMap.remove(domain); - } - } - return retlist; - } - - /** - * Extract the host and path out of a uri - * @param uri The given WebAddress - * @return The host and path in the format of String[], String[0] is host - * which has at least two periods, String[1] is path which always - * ended with "/" - */ - private String[] getHostAndPath(WebAddress uri) { - if (uri.getHost() != null && uri.getPath() != null) { - - /* - * The domain (i.e. host) portion of the cookie is supposed to be - * case-insensitive. We will consistently return the domain in lower - * case, which allows us to do the more efficient equals comparison - * instead of equalIgnoreCase. - * - * See: http://www.ieft.org/rfc/rfc2965.txt (Section 3.3.3) - */ - String[] ret = new String[2]; - ret[0] = uri.getHost().toLowerCase(); - ret[1] = uri.getPath(); - - int index = ret[0].indexOf(PERIOD); - if (index == -1) { - if (uri.getScheme().equalsIgnoreCase("file")) { - // There is a potential bug where a local file path matches - // another file in the local web server directory. Still - // "localhost" is the best pseudo domain name. - ret[0] = "localhost"; - } - } else if (index == ret[0].lastIndexOf(PERIOD)) { - // cookie host must have at least two periods - ret[0] = PERIOD + ret[0]; - } - - if (ret[1].charAt(0) != PATH_DELIM) { - return null; - } - - /* - * find cookie path, e.g. for http://www.google.com, the path is "/" - * for http://www.google.com/lab/, the path is "/lab" - * for http://www.google.com/lab/foo, the path is "/lab/foo" - * for http://www.google.com/lab?hl=en, the path is "/lab" - * for http://www.google.com/lab.asp?hl=en, the path is "/lab.asp" - * Note: the path from URI has at least one "/" - * See: - * http://www.unix.com.ua/rfc/rfc2109.html - */ - index = ret[1].indexOf(QUESTION_MARK); - if (index != -1) { - ret[1] = ret[1].substring(0, index); - } - - return ret; - } else - return null; - } - - /** - * Get the base domain for a give host. E.g. mail.google.com will return - * google.com - * @param host The give host - * @return the base domain - */ - private String getBaseDomain(String host) { - int startIndex = 0; - int nextIndex = host.indexOf(PERIOD); - int lastIndex = host.lastIndexOf(PERIOD); - while (nextIndex < lastIndex) { - startIndex = nextIndex + 1; - nextIndex = host.indexOf(PERIOD, startIndex); - } - if (startIndex > 0) { - return host.substring(startIndex); - } else { - return host; - } - } - - /** - * parseCookie() parses the cookieString which is a comma-separated list of - * one or more cookies in the format of "NAME=VALUE; expires=DATE; - * path=PATH; domain=DOMAIN_NAME; secure httponly" to a list of Cookies. - * Here is a sample: IGDND=1, IGPC=ET=UB8TSNwtDmQ:AF=0; expires=Sun, - * 17-Jan-2038 19:14:07 GMT; path=/ig; domain=.google.com, =, - * PREF=ID=408909b1b304593d:TM=1156459854:LM=1156459854:S=V-vCAU6Sh-gobCfO; - * expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com which - * contains 3 cookies IGDND, IGPC, PREF and an empty cookie - * @param host The default host - * @param path The default path - * @param cookieString The string coming from "Set-Cookie:" - * @return A list of Cookies - */ - private ArrayList<Cookie> parseCookie(String host, String path, - String cookieString) { - ArrayList<Cookie> ret = new ArrayList<Cookie>(); - - int index = 0; - int length = cookieString.length(); - while (true) { - Cookie cookie = null; - - // done - if (index < 0 || index >= length) { - break; - } - - // skip white space - if (cookieString.charAt(index) == WHITE_SPACE) { - index++; - continue; - } - - /* - * get NAME=VALUE; pair. detecting the end of a pair is tricky, it - * can be the end of a string, like "foo=bluh", it can be semicolon - * like "foo=bluh;path=/"; or it can be enclosed by \", like - * "foo=\"bluh bluh\";path=/" - * - * Note: in the case of "foo=bluh, bar=bluh;path=/", we interpret - * it as one cookie instead of two cookies. - */ - int semicolonIndex = cookieString.indexOf(SEMICOLON, index); - int equalIndex = cookieString.indexOf(EQUAL, index); - cookie = new Cookie(host, path); - - // Cookies like "testcookie; path=/;" are valid and used - // (lovefilm.se). - // Look for 2 cases: - // 1. "foo" or "foo;" where equalIndex is -1 - // 2. "foo; path=..." where the first semicolon is before an equal - // and a semicolon exists. - if ((semicolonIndex != -1 && (semicolonIndex < equalIndex)) || - equalIndex == -1) { - // Fix up the index in case we have a string like "testcookie" - if (semicolonIndex == -1) { - semicolonIndex = length; - } - cookie.name = cookieString.substring(index, semicolonIndex); - cookie.value = null; - } else { - cookie.name = cookieString.substring(index, equalIndex); - // Make sure we do not throw an exception if the cookie is like - // "foo=" - if ((equalIndex < length - 1) && - (cookieString.charAt(equalIndex + 1) == QUOTATION)) { - index = cookieString.indexOf(QUOTATION, equalIndex + 2); - if (index == -1) { - // bad format, force return - break; - } - } - // Get the semicolon index again in case it was contained within - // the quotations. - semicolonIndex = cookieString.indexOf(SEMICOLON, index); - if (semicolonIndex == -1) { - semicolonIndex = length; - } - if (semicolonIndex - equalIndex > MAX_COOKIE_LENGTH) { - // cookie is too big, trim it - cookie.value = cookieString.substring(equalIndex + 1, - equalIndex + 1 + MAX_COOKIE_LENGTH); - } else if (equalIndex + 1 == semicolonIndex - || semicolonIndex < equalIndex) { - // this is an unusual case like "foo=;" or "foo=" - cookie.value = ""; - } else { - cookie.value = cookieString.substring(equalIndex + 1, - semicolonIndex); - } - } - // get attributes - index = semicolonIndex; - while (true) { - // done - if (index < 0 || index >= length) { - break; - } - - // skip white space and semicolon - if (cookieString.charAt(index) == WHITE_SPACE - || cookieString.charAt(index) == SEMICOLON) { - index++; - continue; - } - - // comma means next cookie - if (cookieString.charAt(index) == COMMA) { - index++; - break; - } - - // "secure" is a known attribute doesn't use "="; - // while sites like live.com uses "secure=" - if (length - index >= SECURE_LENGTH - && cookieString.substring(index, index + SECURE_LENGTH). - equalsIgnoreCase(SECURE)) { - index += SECURE_LENGTH; - cookie.secure = true; - if (index == length) break; - if (cookieString.charAt(index) == EQUAL) index++; - continue; - } - - // "httponly" is a known attribute doesn't use "="; - // while sites like live.com uses "httponly=" - if (length - index >= HTTP_ONLY_LENGTH - && cookieString.substring(index, - index + HTTP_ONLY_LENGTH). - equalsIgnoreCase(HTTP_ONLY)) { - index += HTTP_ONLY_LENGTH; - if (index == length) break; - if (cookieString.charAt(index) == EQUAL) index++; - // FIXME: currently only parse the attribute - continue; - } - equalIndex = cookieString.indexOf(EQUAL, index); - if (equalIndex > 0) { - String name = cookieString.substring(index, equalIndex).toLowerCase(); - int valueIndex = equalIndex + 1; - while (valueIndex < length && cookieString.charAt(valueIndex) == WHITE_SPACE) { - valueIndex++; - } - - if (name.equals(EXPIRES)) { - int comaIndex = cookieString.indexOf(COMMA, equalIndex); - - // skip ',' in (Wdy, DD-Mon-YYYY HH:MM:SS GMT) or - // (Weekday, DD-Mon-YY HH:MM:SS GMT) if it applies. - // "Wednesday" is the longest Weekday which has length 9 - if ((comaIndex != -1) && - (comaIndex - valueIndex <= 10)) { - index = comaIndex + 1; - } - } - semicolonIndex = cookieString.indexOf(SEMICOLON, index); - int commaIndex = cookieString.indexOf(COMMA, index); - if (semicolonIndex == -1 && commaIndex == -1) { - index = length; - } else if (semicolonIndex == -1) { - index = commaIndex; - } else if (commaIndex == -1) { - index = semicolonIndex; - } else { - index = Math.min(semicolonIndex, commaIndex); - } - String value = cookieString.substring(valueIndex, index); - - // Strip quotes if they exist - if (value.length() > 2 && value.charAt(0) == QUOTATION) { - int endQuote = value.indexOf(QUOTATION, 1); - if (endQuote > 0) { - value = value.substring(1, endQuote); - } - } - if (name.equals(EXPIRES)) { - try { - cookie.expires = AndroidHttpClient.parseDate(value); - } catch (IllegalArgumentException ex) { - Log.e(LOGTAG, - "illegal format for expires: " + value); - } - } else if (name.equals(MAX_AGE)) { - try { - cookie.expires = System.currentTimeMillis() + 1000 - * Long.parseLong(value); - } catch (NumberFormatException ex) { - Log.e(LOGTAG, - "illegal format for max-age: " + value); - } - } else if (name.equals(PATH)) { - // only allow non-empty path value - if (value.length() > 0) { - cookie.path = value; - } - } else if (name.equals(DOMAIN)) { - int lastPeriod = value.lastIndexOf(PERIOD); - if (lastPeriod == 0) { - // disallow cookies set for TLDs like [.com] - cookie.domain = null; - continue; - } - try { - Integer.parseInt(value.substring(lastPeriod + 1)); - // no wildcard for ip address match - if (!value.equals(host)) { - // no cross-site cookie - cookie.domain = null; - } - continue; - } catch (NumberFormatException ex) { - // ignore the exception, value is a host name - } - value = value.toLowerCase(); - if (value.charAt(0) != PERIOD) { - // pre-pended dot to make it as a domain cookie - value = PERIOD + value; - lastPeriod++; - } - if (host.endsWith(value.substring(1))) { - int len = value.length(); - int hostLen = host.length(); - if (hostLen > (len - 1) - && host.charAt(hostLen - len) != PERIOD) { - // make sure the bar.com doesn't match .ar.com - cookie.domain = null; - continue; - } - // disallow cookies set on ccTLDs like [.co.uk] - if ((len == lastPeriod + 3) - && (len >= 6 && len <= 8)) { - String s = value.substring(1, lastPeriod); - if (Arrays.binarySearch(BAD_COUNTRY_2LDS, s) >= 0) { - cookie.domain = null; - continue; - } - } - cookie.domain = value; - } else { - // no cross-site or more specific sub-domain cookie - cookie.domain = null; - } - } - } else { - // bad format, force return - index = length; - } - } - if (cookie != null && cookie.domain != null) { - ret.add(cookie); - } - } - return ret; + nativeSetAcceptFileSchemeCookies(accept); } // Native functions diff --git a/core/java/android/webkit/CookieSyncManager.java b/core/java/android/webkit/CookieSyncManager.java index a6998006e895..19fa09672c42 100644 --- a/core/java/android/webkit/CookieSyncManager.java +++ b/core/java/android/webkit/CookieSyncManager.java @@ -100,77 +100,6 @@ public final class CookieSyncManager extends WebSyncManager { return sRef; } - /** - * Package level api, called from CookieManager. Get all the cookies which - * matches a given base domain. - * @param domain - * @return A list of Cookie - */ - ArrayList<Cookie> getCookiesForDomain(String domain) { - // null mDataBase implies that the host application doesn't support - // persistent cookie. No sync needed. - if (mDataBase == null) { - return new ArrayList<Cookie>(); - } - - return mDataBase.getCookiesForDomain(domain); - } - - /** - * Package level api, called from CookieManager Clear all cookies in the - * database - */ - void clearAllCookies() { - // null mDataBase implies that the host application doesn't support - // persistent cookie. - if (mDataBase == null) { - return; - } - - mDataBase.clearCookies(); - } - - /** - * Returns true if there are any saved cookies. - */ - boolean hasCookies() { - // null mDataBase implies that the host application doesn't support - // persistent cookie. - if (mDataBase == null) { - return false; - } - - return mDataBase.hasCookies(); - } - - /** - * Package level api, called from CookieManager Clear all session cookies in - * the database - */ - void clearSessionCookies() { - // null mDataBase implies that the host application doesn't support - // persistent cookie. - if (mDataBase == null) { - return; - } - - mDataBase.clearSessionCookies(); - } - - /** - * Package level api, called from CookieManager Clear all expired cookies in - * the database - */ - void clearExpiredCookies(long now) { - // null mDataBase implies that the host application doesn't support - // persistent cookie. - if (mDataBase == null) { - return; - } - - mDataBase.clearExpiredCookies(now); - } - protected void syncFromRamToFlash() { if (DebugFlags.COOKIE_SYNC_MANAGER) { Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash STARTS"); @@ -182,41 +111,13 @@ public final class CookieSyncManager extends WebSyncManager { return; } - if (JniUtil.useChromiumHttpStack()) { - manager.flushCookieStore(); - } else { - ArrayList<Cookie> cookieList = manager.getUpdatedCookiesSince(mLastUpdate); - mLastUpdate = System.currentTimeMillis(); - syncFromRamToFlash(cookieList); - - ArrayList<Cookie> lruList = manager.deleteLRUDomain(); - syncFromRamToFlash(lruList); - } + manager.flushCookieStore(); if (DebugFlags.COOKIE_SYNC_MANAGER) { Log.v(LOGTAG, "CookieSyncManager::syncFromRamToFlash DONE"); } } - private void syncFromRamToFlash(ArrayList<Cookie> list) { - Iterator<Cookie> iter = list.iterator(); - while (iter.hasNext()) { - Cookie cookie = iter.next(); - if (cookie.mode != Cookie.MODE_NORMAL) { - if (cookie.mode != Cookie.MODE_NEW) { - mDataBase.deleteCookies(cookie.domain, cookie.path, - cookie.name); - } - if (cookie.mode != Cookie.MODE_DELETED) { - mDataBase.addCookie(cookie); - CookieManager.getInstance().syncedACookie(cookie); - } else { - CookieManager.getInstance().deleteACookie(cookie); - } - } - } - } - private static void checkInstanceIsCreated() { if (sRef == null) { throw new IllegalStateException( diff --git a/core/java/android/webkit/DataLoader.java b/core/java/android/webkit/DataLoader.java deleted file mode 100644 index e8d906934954..000000000000 --- a/core/java/android/webkit/DataLoader.java +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.webkit; - -import android.net.http.EventHandler; - -import com.android.internal.R; - -import java.io.ByteArrayInputStream; - -import libcore.io.Base64; - -/** - * This class is a concrete implementation of StreamLoader that uses the - * content supplied as a URL as the source for the stream. The mimetype - * optionally provided in the URL is extracted and inserted into the HTTP - * response headers. - */ -class DataLoader extends StreamLoader { - - /** - * Constructor uses the dataURL as the source for an InputStream - * @param dataUrl data: URL string optionally containing a mimetype - * @param loadListener LoadListener to pass the content to - */ - DataLoader(String dataUrl, LoadListener loadListener) { - super(loadListener); - - String url = dataUrl.substring("data:".length()); - byte[] data = null; - int commaIndex = url.indexOf(','); - if (commaIndex != -1) { - String contentType = url.substring(0, commaIndex); - data = url.substring(commaIndex + 1).getBytes(); - loadListener.parseContentTypeHeader(contentType); - if ("base64".equals(loadListener.transferEncoding())) { - data = Base64.decode(data); - } - } else { - data = url.getBytes(); - } - if (data != null) { - mDataStream = new ByteArrayInputStream(data); - mContentLength = data.length; - } - } - - @Override - protected boolean setupStreamAndSendStatus() { - if (mDataStream != null) { - mLoadListener.status(1, 1, 200, "OK"); - return true; - } else { - mLoadListener.error(EventHandler.ERROR, - mContext.getString(R.string.httpError)); - return false; - } - } - - @Override - protected void buildHeaders(android.net.http.Headers h) { - } -} diff --git a/core/java/android/webkit/FileLoader.java b/core/java/android/webkit/FileLoader.java deleted file mode 100644 index e21e9ef8e9f1..000000000000 --- a/core/java/android/webkit/FileLoader.java +++ /dev/null @@ -1,189 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.webkit; - -import com.android.internal.R; - -import android.content.res.AssetManager; -import android.net.http.EventHandler; -import android.net.http.Headers; -import android.util.Log; -import android.util.TypedValue; - -import java.io.File; -import java.io.FileInputStream; -import java.lang.reflect.Field; - -/** - * This class is a concrete implementation of StreamLoader that uses a - * file or asset as the source for the stream. - * - */ -class FileLoader extends StreamLoader { - - private String mPath; // Full path to the file to load - private int mType; // Indicates the type of the load - private boolean mAllowFileAccess; // Allow/block file system access - - // used for files under asset directory - static final int TYPE_ASSET = 1; - // used for files under res directory - static final int TYPE_RES = 2; - // generic file - static final int TYPE_FILE = 3; - - private static final String LOGTAG = "webkit"; - - /** - * Construct a FileLoader with the file URL specified as the content - * source. - * - * @param url Full file url pointing to content to be loaded - * @param loadListener LoadListener to pass the content to - * @param asset true if url points to an asset. - * @param allowFileAccess true if this WebView is allowed to access files - * on the file system. - */ - FileLoader(String url, LoadListener loadListener, int type, - boolean allowFileAccess) { - super(loadListener); - mType = type; - mAllowFileAccess = allowFileAccess; - - // clean the Url - int index = url.indexOf('?'); - if (mType == TYPE_ASSET) { - mPath = index > 0 ? URLUtil.stripAnchor( - url.substring(URLUtil.ASSET_BASE.length(), index)) : - URLUtil.stripAnchor(url.substring( - URLUtil.ASSET_BASE.length())); - } else if (mType == TYPE_RES) { - mPath = index > 0 ? URLUtil.stripAnchor( - url.substring(URLUtil.RESOURCE_BASE.length(), index)) : - URLUtil.stripAnchor(url.substring( - URLUtil.RESOURCE_BASE.length())); - } else { - mPath = index > 0 ? URLUtil.stripAnchor( - url.substring(URLUtil.FILE_BASE.length(), index)) : - URLUtil.stripAnchor(url.substring( - URLUtil.FILE_BASE.length())); - } - } - - private String errString(Exception ex) { - String exMessage = ex.getMessage(); - String errString = mContext.getString(R.string.httpErrorFileNotFound); - if (exMessage != null) { - errString += " " + exMessage; - } - return errString; - } - - @Override - protected boolean setupStreamAndSendStatus() { - try { - if (mType == TYPE_ASSET) { - try { - mDataStream = mContext.getAssets().open(mPath); - } catch (java.io.FileNotFoundException ex) { - // try the rest files included in the package - mDataStream = mContext.getAssets().openNonAsset(mPath); - } - } else if (mType == TYPE_RES) { - // get the resource id from the path. e.g. for the path like - // drawable/foo.png, the id is located at field "foo" of class - // "<package>.R$drawable" - if (mPath == null || mPath.length() == 0) { - Log.e(LOGTAG, "Need a path to resolve the res file"); - mLoadListener.error(EventHandler.FILE_ERROR, mContext - .getString(R.string.httpErrorFileNotFound)); - return false; - - } - int slash = mPath.indexOf('/'); - int dot = mPath.indexOf('.', slash); - if (slash == -1 || dot == -1) { - Log.e(LOGTAG, "Incorrect res path: " + mPath); - mLoadListener.error(EventHandler.FILE_ERROR, mContext - .getString(R.string.httpErrorFileNotFound)); - return false; - } - String subClassName = mPath.substring(0, slash); - String fieldName = mPath.substring(slash + 1, dot); - String errorMsg = null; - try { - final Class<?> d = mContext.getApplicationContext() - .getClassLoader().loadClass( - mContext.getPackageName() + ".R$" - + subClassName); - final Field field = d.getField(fieldName); - final int id = field.getInt(null); - TypedValue value = new TypedValue(); - mContext.getResources().getValue(id, value, true); - if (value.type == TypedValue.TYPE_STRING) { - mDataStream = mContext.getAssets().openNonAsset( - value.assetCookie, value.string.toString(), - AssetManager.ACCESS_STREAMING); - } else { - errorMsg = "Only support TYPE_STRING for the res files"; - } - } catch (ClassNotFoundException e) { - errorMsg = "Can't find class: " - + mContext.getPackageName() + ".R$" + subClassName; - } catch (SecurityException e) { - errorMsg = "Caught SecurityException: " + e; - } catch (NoSuchFieldException e) { - errorMsg = "Can't find field: " + fieldName + " in " - + mContext.getPackageName() + ".R$" + subClassName; - } catch (IllegalArgumentException e) { - errorMsg = "Caught IllegalArgumentException: " + e; - } catch (IllegalAccessException e) { - errorMsg = "Caught IllegalAccessException: " + e; - } - if (errorMsg != null) { - mLoadListener.error(EventHandler.FILE_ERROR, mContext - .getString(R.string.httpErrorFileNotFound)); - return false; - } - } else { - if (!mAllowFileAccess) { - mLoadListener.error(EventHandler.FILE_ERROR, - mContext.getString(R.string.httpErrorFileNotFound)); - return false; - } - - mDataStream = new FileInputStream(mPath); - mContentLength = (new File(mPath)).length(); - } - mLoadListener.status(1, 1, 200, "OK"); - - } catch (java.io.FileNotFoundException ex) { - mLoadListener.error(EventHandler.FILE_NOT_FOUND_ERROR, errString(ex)); - return false; - - } catch (java.io.IOException ex) { - mLoadListener.error(EventHandler.FILE_ERROR, errString(ex)); - return false; - } - return true; - } - - @Override - protected void buildHeaders(Headers headers) { - // do nothing. - } -} diff --git a/core/java/android/webkit/FindActionModeCallback.java b/core/java/android/webkit/FindActionModeCallback.java index fffa90b5518a..10b0885359db 100644 --- a/core/java/android/webkit/FindActionModeCallback.java +++ b/core/java/android/webkit/FindActionModeCallback.java @@ -43,7 +43,9 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, private Resources mResources; private boolean mMatchesFound; private int mNumberOfMatches; + private int mActiveMatchIndex; private ActionMode mActionMode; + private String mLastFind; FindActionModeCallback(Context context) { mCustomView = LayoutInflater.from(context).inflate( @@ -132,16 +134,13 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, mWebView.clearMatches(); mMatches.setVisibility(View.GONE); mMatchesFound = false; + mLastFind = null; } else { mMatchesFound = true; - mMatches.setVisibility(View.VISIBLE); - mNumberOfMatches = mWebView.findAll(find.toString()); - if (0 == mNumberOfMatches) { - mMatches.setText(mResources.getString( - com.android.internal.R.string.no_matches)); - } else { - updateMatchesString(); - } + mMatches.setVisibility(View.INVISIBLE); + mNumberOfMatches = 0; + mLastFind = find.toString(); + mWebView.findAllAsync(mLastFind); } } @@ -151,17 +150,31 @@ class FindActionModeCallback implements ActionMode.Callback, TextWatcher, mInput.showSoftInput(mEditText, 0); } + public void updateMatchCount(int matchIndex, int matchCount, + String findText) { + if (mLastFind != null && mLastFind.equals(findText)) { + mNumberOfMatches = matchCount; + mActiveMatchIndex = matchIndex; + updateMatchesString(); + } else { + mMatches.setVisibility(View.INVISIBLE); + mNumberOfMatches = 0; + } + } + /* * Update the string which tells the user how many matches were found, and * which match is currently highlighted. - * Not to be called when mNumberOfMatches is 0. */ private void updateMatchesString() { - String template = mResources.getQuantityString( + if (mNumberOfMatches == 0) { + mMatches.setText(com.android.internal.R.string.no_matches); + } else { + mMatches.setText(mResources.getQuantityString( com.android.internal.R.plurals.matches_found, mNumberOfMatches, - mWebView.findIndex() + 1, mNumberOfMatches); - - mMatches.setText(template); + mActiveMatchIndex + 1, mNumberOfMatches)); + } + mMatches.setVisibility(View.VISIBLE); } // OnLongClickListener implementation diff --git a/core/java/android/webkit/FrameLoader.java b/core/java/android/webkit/FrameLoader.java deleted file mode 100644 index 0d8030285244..000000000000 --- a/core/java/android/webkit/FrameLoader.java +++ /dev/null @@ -1,421 +0,0 @@ -/* - * Copyright (C) 2006 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.webkit; - -import android.net.http.ErrorStrings; -import android.net.http.EventHandler; -import android.net.http.RequestHandle; -import android.os.Build; -import android.util.Log; -import android.webkit.CacheManager.CacheResult; -import android.webkit.JniUtil; - -import java.util.HashMap; -import java.util.Map; - -class FrameLoader { - - private final LoadListener mListener; - private final String mMethod; - private final WebSettings mSettings; - private Map<String, String> mHeaders; - private byte[] mPostData; - private Network mNetwork; - private int mCacheMode; - private String mReferrer; - private String mContentType; - private final String mUaprofHeader; - private final WebResourceResponse mInterceptResponse; - - private static final int URI_PROTOCOL = 0x100; - - private static final String CONTENT_TYPE = "content-type"; - - // Contents of an about:blank page - private static final String mAboutBlank = - "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EB\">" + - "<html><head><title>about:blank</title></head><body></body></html>"; - - static final String HEADER_STR = "text/xml, text/html, " + - "application/xhtml+xml, image/png, text/plain, */*;q=0.8"; - - private static final String LOGTAG = "webkit"; - - FrameLoader(LoadListener listener, WebSettings settings, - String method, WebResourceResponse interceptResponse) { - assert !JniUtil.useChromiumHttpStack(); - - mListener = listener; - mHeaders = null; - mMethod = method; - mCacheMode = WebSettings.LOAD_NORMAL; - mSettings = settings; - mInterceptResponse = interceptResponse; - mUaprofHeader = mListener.getContext().getResources().getString( - com.android.internal.R.string.config_useragentprofile_url, Build.MODEL); - } - - public void setReferrer(String ref) { - // only set referrer for http or https - if (URLUtil.isNetworkUrl(ref)) mReferrer = ref; - } - - public void setPostData(byte[] postData) { - mPostData = postData; - } - - public void setContentTypeForPost(String postContentType) { - mContentType = postContentType; - } - - public void setCacheMode(int cacheMode) { - mCacheMode = cacheMode; - } - - public void setHeaders(HashMap headers) { - mHeaders = headers; - } - - public LoadListener getLoadListener() { - return mListener; - } - - /** - * Issues the load request. - * - * Return value does not indicate if the load was successful or not. It - * simply indicates that the load request is reasonable. - * - * @return true if the load is reasonable. - */ - public boolean executeLoad() { - String url = mListener.url(); - - // Process intercepted requests first as they could be any url. - if (mInterceptResponse != null) { - if (mListener.isSynchronous()) { - mInterceptResponse.loader(mListener).load(); - } else { - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, - mInterceptResponse.loader(mListener)).sendToTarget(); - } - return true; - } else if (URLUtil.isNetworkUrl(url)){ - if (mSettings.getBlockNetworkLoads()) { - mListener.error(EventHandler.ERROR_BAD_URL, - mListener.getContext().getString( - com.android.internal.R.string.httpErrorBadUrl)); - return false; - } - // Make sure the host part of the url is correctly - // encoded before sending the request - if (!URLUtil.verifyURLEncoding(mListener.host())) { - mListener.error(EventHandler.ERROR_BAD_URL, - mListener.getContext().getString( - com.android.internal.R.string.httpErrorBadUrl)); - return false; - } - mNetwork = Network.getInstance(mListener.getContext()); - if (mListener.isSynchronous()) { - return handleHTTPLoad(); - } - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_HTTPLOADER, this).sendToTarget(); - return true; - } else if (handleLocalFile(url, mListener, mSettings)) { - return true; - } - if (DebugFlags.FRAME_LOADER) { - Log.v(LOGTAG, "FrameLoader.executeLoad: url protocol not supported:" - + mListener.url()); - } - mListener.error(EventHandler.ERROR_UNSUPPORTED_SCHEME, - mListener.getContext().getText( - com.android.internal.R.string.httpErrorUnsupportedScheme).toString()); - return false; - - } - - private static boolean handleLocalFile(String url, LoadListener loadListener, - WebSettings settings) { - assert !JniUtil.useChromiumHttpStack(); - - // Attempt to decode the percent-encoded url before passing to the - // local loaders. - try { - url = new String(URLUtil.decode(url.getBytes())); - } catch (IllegalArgumentException e) { - loadListener.error(EventHandler.ERROR_BAD_URL, - loadListener.getContext().getString( - com.android.internal.R.string.httpErrorBadUrl)); - // Return true here so we do not trigger an unsupported scheme - // error. - return true; - } - if (URLUtil.isAssetUrl(url)) { - if (loadListener.isSynchronous()) { - new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, - true).load(); - } else { - // load asset in a separate thread as it involves IO - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, - new FileLoader(url, loadListener, FileLoader.TYPE_ASSET, - true)).sendToTarget(); - } - return true; - } else if (URLUtil.isResourceUrl(url)) { - if (loadListener.isSynchronous()) { - new FileLoader(url, loadListener, FileLoader.TYPE_RES, - true).load(); - } else { - // load resource in a separate thread as it involves IO - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, - new FileLoader(url, loadListener, FileLoader.TYPE_RES, - true)).sendToTarget(); - } - return true; - } else if (URLUtil.isFileUrl(url)) { - if (loadListener.isSynchronous()) { - new FileLoader(url, loadListener, FileLoader.TYPE_FILE, - settings.getAllowFileAccess()).load(); - } else { - // load file in a separate thread as it involves IO - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, - new FileLoader(url, loadListener, FileLoader.TYPE_FILE, - settings.getAllowFileAccess())).sendToTarget(); - } - return true; - } else if (settings.getAllowContentAccess() && - URLUtil.isContentUrl(url)) { - // Send the raw url to the ContentLoader because it will do a - // permission check and the url has to match. - if (loadListener.isSynchronous()) { - new ContentLoader(loadListener.url(), loadListener).load(); - } else { - // load content in a separate thread as it involves IO - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, - new ContentLoader(loadListener.url(), loadListener)) - .sendToTarget(); - } - return true; - } else if (URLUtil.isDataUrl(url)) { - // load data in the current thread to reduce the latency - new DataLoader(url, loadListener).load(); - return true; - } else if (URLUtil.isAboutUrl(url)) { - loadListener.data(mAboutBlank.getBytes(), mAboutBlank.length()); - loadListener.endData(); - return true; - } - return false; - } - - boolean handleHTTPLoad() { - if (mHeaders == null) { - mHeaders = new HashMap<String, String>(); - } - populateStaticHeaders(); - populateHeaders(); - - // response was handled by Cache, don't issue HTTP request - if (handleCache()) { - // push the request data down to the LoadListener - // as response from the cache could be a redirect - // and we may need to initiate a network request if the cache - // can't satisfy redirect URL - mListener.setRequestData(mMethod, mHeaders, mPostData); - return true; - } - - if (DebugFlags.FRAME_LOADER) { - Log.v(LOGTAG, "FrameLoader: http " + mMethod + " load for: " - + mListener.url()); - } - - boolean ret = false; - int error = EventHandler.ERROR_UNSUPPORTED_SCHEME; - - try { - ret = mNetwork.requestURL(mMethod, mHeaders, - mPostData, mListener); - } catch (android.net.ParseException ex) { - error = EventHandler.ERROR_BAD_URL; - } catch (java.lang.RuntimeException ex) { - /* probably an empty header set by javascript. We want - the same result as bad URL */ - error = EventHandler.ERROR_BAD_URL; - } - if (!ret) { - mListener.error(error, ErrorStrings.getString(error, mListener.getContext())); - return false; - } - return true; - } - - /* - * This function is used by handleCache to - * setup a load from the byte stream in a CacheResult. - */ - private void startCacheLoad(CacheResult result) { - if (DebugFlags.FRAME_LOADER) { - Log.v(LOGTAG, "FrameLoader: loading from cache: " - + mListener.url()); - } - // Tell the Listener respond with the cache file - CacheLoader cacheLoader = - new CacheLoader(mListener, result); - mListener.setCacheLoader(cacheLoader); - if (mListener.isSynchronous()) { - cacheLoader.load(); - } else { - // Load the cached file in a separate thread - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, cacheLoader).sendToTarget(); - } - } - - /* - * This function is used by the handleHTTPLoad to setup the cache headers - * correctly. - * Returns true if the response was handled from the cache - */ - private boolean handleCache() { - switch (mCacheMode) { - // This mode is normally used for a reload, it instructs the http - // loader to not use the cached content. - case WebSettings.LOAD_NO_CACHE: - break; - - - // This mode is used when the content should only be loaded from - // the cache. If it is not there, then fail the load. This is used - // to load POST content in a history navigation. - case WebSettings.LOAD_CACHE_ONLY: { - CacheResult result = CacheManager.getCacheFile(mListener.url(), - mListener.postIdentifier(), null); - if (result != null) { - startCacheLoad(result); - } else { - // This happens if WebCore was first told that the POST - // response was in the cache, then when we try to use it - // it has gone. - // Generate a file not found error - int err = EventHandler.FILE_NOT_FOUND_ERROR; - mListener.error(err, - ErrorStrings.getString(err, mListener.getContext())); - } - return true; - } - - // This mode is for when the user is doing a history navigation - // in the browser and should returned cached content regardless - // of it's state. If it is not in the cache, then go to the - // network. - case WebSettings.LOAD_CACHE_ELSE_NETWORK: { - if (DebugFlags.FRAME_LOADER) { - Log.v(LOGTAG, "FrameLoader: checking cache: " - + mListener.url()); - } - // Get the cache file name for the current URL, passing null for - // the validation headers causes no validation to occur - CacheResult result = CacheManager.getCacheFile(mListener.url(), - mListener.postIdentifier(), null); - if (result != null) { - startCacheLoad(result); - return true; - } - break; - } - - // This is the default case, which is to check to see if the - // content in the cache can be used. If it can be used, then - // use it. If it needs revalidation then the relevant headers - // are added to the request. - default: - case WebSettings.LOAD_NORMAL: - return mListener.checkCache(mHeaders); - }// end of switch - - return false; - } - - /** - * Add the static headers that don't change with each request. - */ - private void populateStaticHeaders() { - // Accept header should already be there as they are built by WebCore, - // but in the case they are missing, add some. - String accept = mHeaders.get("Accept"); - if (accept == null || accept.length() == 0) { - mHeaders.put("Accept", HEADER_STR); - } - mHeaders.put("Accept-Charset", "utf-8, iso-8859-1, utf-16, *;q=0.7"); - - String acceptLanguage = mSettings.getAcceptLanguage(); - if (acceptLanguage.length() > 0) { - mHeaders.put("Accept-Language", acceptLanguage); - } - - mHeaders.put("User-Agent", mSettings.getUserAgentString()); - - // Set the x-wap-profile header - if (mUaprofHeader != null && mUaprofHeader.length() > 0) { - mHeaders.put("x-wap-profile", mUaprofHeader); - } - } - - /** - * Add the content related headers. These headers contain user private data - * and is not used when we are proxying an untrusted request. - */ - private void populateHeaders() { - - if (mReferrer != null) mHeaders.put("Referer", mReferrer); - if (mContentType != null) mHeaders.put(CONTENT_TYPE, mContentType); - - // if we have an active proxy and have proxy credentials, do pre-emptive - // authentication to avoid an extra round-trip: - if (mNetwork.isValidProxySet()) { - String username; - String password; - /* The proxy credentials can be set in the Network thread */ - synchronized (mNetwork) { - username = mNetwork.getProxyUsername(); - password = mNetwork.getProxyPassword(); - } - if (username != null && password != null) { - // we collect credentials ONLY if the proxy scheme is BASIC!!! - String proxyHeader = RequestHandle.authorizationHeader(true); - mHeaders.put(proxyHeader, - "Basic " + RequestHandle.computeBasicAuthResponse( - username, password)); - } - } - - // Set cookie header - String cookie = CookieManager.getInstance().getCookie( - mListener.getWebAddress()); - if (cookie != null && cookie.length() > 0) { - mHeaders.put("Cookie", cookie); - } - } -} diff --git a/core/java/android/webkit/HttpAuthHandlerImpl.java b/core/java/android/webkit/HttpAuthHandlerImpl.java deleted file mode 100644 index 01e8eb8a644f..000000000000 --- a/core/java/android/webkit/HttpAuthHandlerImpl.java +++ /dev/null @@ -1,279 +0,0 @@ -/* - * 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.webkit; - -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -import java.util.ListIterator; -import java.util.LinkedList; - -/** - * HttpAuthHandler implementation is used only by the Android Java HTTP stack. - * <p> - * This class is not needed when we're using the Chromium HTTP stack. - */ -class HttpAuthHandlerImpl extends HttpAuthHandler { - /* - * It is important that the handler is in Network, because we want to share - * it accross multiple loaders and windows (like our subwindow and the main - * window). - */ - - private static final String LOGTAG = "network"; - - /** - * Network. - */ - private Network mNetwork; - - /** - * Loader queue. - */ - private LinkedList<LoadListener> mLoaderQueue; - - - // Message id for handling the user response - private static final int AUTH_PROCEED = 100; - private static final int AUTH_CANCEL = 200; - - // Use to synchronize when making synchronous calls to - // onReceivedHttpAuthRequest(). We can't use a single Boolean object for - // both the lock and the state, because Boolean is immutable. - Object mRequestInFlightLock = new Object(); - boolean mRequestInFlight; - String mUsername; - String mPassword; - - /** - * Creates a new HTTP authentication handler with an empty - * loader queue - * - * @param network The parent network object - */ - /* package */ HttpAuthHandlerImpl(Network network) { - mNetwork = network; - mLoaderQueue = new LinkedList<LoadListener>(); - } - - - @Override - public void handleMessage(Message msg) { - LoadListener loader = null; - synchronized (mLoaderQueue) { - loader = mLoaderQueue.poll(); - } - assert(loader.isSynchronous() == false); - - switch (msg.what) { - case AUTH_PROCEED: - String username = msg.getData().getString("username"); - String password = msg.getData().getString("password"); - - loader.handleAuthResponse(username, password); - break; - - case AUTH_CANCEL: - loader.handleAuthResponse(null, null); - break; - } - - processNextLoader(); - } - - /** - * Helper method used to unblock handleAuthRequest(), which in the case of a - * synchronous request will wait for proxy.onReceivedHttpAuthRequest() to - * call back to either proceed() or cancel(). - * - * @param username The username to use for authentication - * @param password The password to use for authentication - * @return True if the request is synchronous and handleAuthRequest() has - * been unblocked - */ - private boolean handleResponseForSynchronousRequest(String username, String password) { - LoadListener loader = null; - synchronized (mLoaderQueue) { - loader = mLoaderQueue.peek(); - } - if (loader.isSynchronous()) { - mUsername = username; - mPassword = password; - return true; - } - return false; - } - - private void signalRequestComplete() { - synchronized (mRequestInFlightLock) { - assert(mRequestInFlight); - mRequestInFlight = false; - mRequestInFlightLock.notify(); - } - } - - /** - * Proceed with the authorization with the given credentials - * - * May be called on the UI thread, rather than the WebCore thread. - * - * @param username The username to use for authentication - * @param password The password to use for authentication - */ - public void proceed(String username, String password) { - if (handleResponseForSynchronousRequest(username, password)) { - signalRequestComplete(); - return; - } - Message msg = obtainMessage(AUTH_PROCEED); - msg.getData().putString("username", username); - msg.getData().putString("password", password); - sendMessage(msg); - signalRequestComplete(); - } - - /** - * Cancel the authorization request - * - * May be called on the UI thread, rather than the WebCore thread. - * - */ - public void cancel() { - if (handleResponseForSynchronousRequest(null, null)) { - signalRequestComplete(); - return; - } - sendMessage(obtainMessage(AUTH_CANCEL)); - signalRequestComplete(); - } - - /** - * @return True if we can use user credentials on record - * (ie, if we did not fail trying to use them last time) - */ - public boolean useHttpAuthUsernamePassword() { - LoadListener loader = null; - synchronized (mLoaderQueue) { - loader = mLoaderQueue.peek(); - } - if (loader != null) { - return !loader.authCredentialsInvalid(); - } - - return false; - } - - /** - * Enqueues the loader, if the loader is the only element - * in the queue, starts processing the loader - * - * @param loader The loader that resulted in this http - * authentication request - */ - /* package */ void handleAuthRequest(LoadListener loader) { - // The call to proxy.onReceivedHttpAuthRequest() may be asynchronous. If - // the request is synchronous, we must block here until we have a - // response. - if (loader.isSynchronous()) { - // If there's a request in flight, wait for it to complete. The - // response will queue a message on this thread. - waitForRequestToComplete(); - // Make a request to the proxy for this request, jumping the queue. - // We use the queue so that the loader is present in - // useHttpAuthUsernamePassword(). - synchronized (mLoaderQueue) { - mLoaderQueue.addFirst(loader); - } - processNextLoader(); - // Wait for this request to complete. - waitForRequestToComplete(); - // Pop the loader from the queue. - synchronized (mLoaderQueue) { - assert(mLoaderQueue.peek() == loader); - mLoaderQueue.poll(); - } - // Call back. - loader.handleAuthResponse(mUsername, mPassword); - // The message queued by the response from the last asynchronous - // request, if present, will start the next request. - return; - } - - boolean processNext = false; - - synchronized (mLoaderQueue) { - mLoaderQueue.offer(loader); - processNext = - (mLoaderQueue.size() == 1); - } - - if (processNext) { - processNextLoader(); - } - } - - /** - * Wait for the request in flight, if any, to complete - */ - private void waitForRequestToComplete() { - synchronized (mRequestInFlightLock) { - while (mRequestInFlight) { - try { - mRequestInFlightLock.wait(); - } catch(InterruptedException e) { - Log.e(LOGTAG, "Interrupted while waiting for request to complete"); - } - } - } - } - - /** - * Process the next loader in the queue (helper method) - */ - private void processNextLoader() { - LoadListener loader = null; - synchronized (mLoaderQueue) { - loader = mLoaderQueue.peek(); - } - if (loader != null) { - synchronized (mRequestInFlightLock) { - assert(mRequestInFlight == false); - mRequestInFlight = true; - } - - CallbackProxy proxy = loader.getFrame().getCallbackProxy(); - - String hostname = loader.proxyAuthenticate() ? - mNetwork.getProxyHostname() : loader.host(); - - String realm = loader.realm(); - - proxy.onReceivedHttpAuthRequest(this, hostname, realm); - } - } - - /** - * Informs the WebView of a new set of credentials. - */ - public static void onReceivedCredentials(LoadListener loader, - String host, String realm, String username, String password) { - CallbackProxy proxy = loader.getFrame().getCallbackProxy(); - proxy.onReceivedHttpAuthCredentials(host, realm, username, password); - } -} diff --git a/core/java/android/webkit/JniUtil.java b/core/java/android/webkit/JniUtil.java index 7b449385ebfa..343d34a56643 100644 --- a/core/java/android/webkit/JniUtil.java +++ b/core/java/android/webkit/JniUtil.java @@ -37,7 +37,6 @@ class JniUtil { // Used by the Chromium HTTP stack. private static String sDatabaseDirectory; private static String sCacheDirectory; - private static Boolean sUseChromiumHttpStack; private static Context sContext; private static void checkInitialized() { @@ -111,10 +110,9 @@ class JniUtil { // content:// if (url.startsWith(ANDROID_CONTENT)) { try { - // Strip off mimetype, for compatibility with ContentLoader.java - // If we don't do this, we can fail to load Gmail attachments, - // because the URL being loaded doesn't exactly match the URL we - // have permission to read. + // Strip off MIME type. If we don't do this, we can fail to + // load Gmail attachments, because the URL being loaded doesn't + // exactly match the URL we have permission to read. int mimeIndex = url.lastIndexOf('?'); if (mimeIndex != -1) { url = url.substring(0, mimeIndex); @@ -152,6 +150,7 @@ class JniUtil { if (url.startsWith(ANDROID_CONTENT)) { try { // Strip off mimetype, for compatibility with ContentLoader.java + // (used with Android HTTP stack, now removed). // If we don't do this, we can fail to load Gmail attachments, // because the URL being loaded doesn't exactly match the URL we // have permission to read. @@ -170,19 +169,6 @@ class JniUtil { } } - /** - * Returns true if we're using the Chromium HTTP stack. - * - * TODO: Remove this if/when we permanently switch to the Chromium HTTP stack - * http:/b/3118772 - */ - static boolean useChromiumHttpStack() { - if (sUseChromiumHttpStack == null) { - sUseChromiumHttpStack = nativeUseChromiumHttpStack(); - } - return sUseChromiumHttpStack; - } - private static synchronized String getAutofillQueryUrl() { checkInitialized(); // If the device has not checked in it won't have pulled down the system setting for the @@ -200,7 +186,4 @@ class JniUtil { long leftToAllocate = memInfo.availMem - memInfo.threshold; return !memInfo.lowMemory && bytesRequested < leftToAllocate; } - - - private static native boolean nativeUseChromiumHttpStack(); } diff --git a/core/java/android/webkit/LoadListener.java b/core/java/android/webkit/LoadListener.java deleted file mode 100644 index 37e8bc090837..000000000000 --- a/core/java/android/webkit/LoadListener.java +++ /dev/null @@ -1,1685 +0,0 @@ -/* - * Copyright (C) 2006 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.webkit; - -import android.content.ActivityNotFoundException; -import android.content.Context; -import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.ParseException; -import android.net.Uri; -import android.net.WebAddress; -import android.net.http.EventHandler; -import android.net.http.Headers; -import android.net.http.HttpAuthHeader; -import android.net.http.RequestHandle; -import android.net.http.SslCertificate; -import android.net.http.SslError; - -import android.os.Handler; -import android.os.Message; -import android.util.Log; -import android.webkit.CacheManager.CacheResult; -import android.webkit.JniUtil; - -import com.android.internal.R; - -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Vector; -import java.util.regex.Pattern; -import java.util.regex.Matcher; - -class LoadListener extends Handler implements EventHandler { - - private static final String LOGTAG = "webkit"; - - // Messages used internally to communicate state between the - // Network thread and the WebCore thread. - private static final int MSG_CONTENT_HEADERS = 100; - private static final int MSG_CONTENT_DATA = 110; - private static final int MSG_CONTENT_FINISHED = 120; - private static final int MSG_CONTENT_ERROR = 130; - private static final int MSG_LOCATION_CHANGED = 140; - private static final int MSG_LOCATION_CHANGED_REQUEST = 150; - private static final int MSG_STATUS = 160; - private static final int MSG_SSL_CERTIFICATE = 170; - private static final int MSG_SSL_ERROR = 180; - - // Standard HTTP status codes in a more representative format - private static final int HTTP_OK = 200; - private static final int HTTP_PARTIAL_CONTENT = 206; - private static final int HTTP_MOVED_PERMANENTLY = 301; - private static final int HTTP_FOUND = 302; - private static final int HTTP_SEE_OTHER = 303; - private static final int HTTP_NOT_MODIFIED = 304; - private static final int HTTP_TEMPORARY_REDIRECT = 307; - private static final int HTTP_AUTH = 401; - private static final int HTTP_NOT_FOUND = 404; - private static final int HTTP_PROXY_AUTH = 407; - - private static int sNativeLoaderCount; - - private final ByteArrayBuilder mDataBuilder = new ByteArrayBuilder(); - - private String mUrl; - private WebAddress mUri; - private boolean mPermanent; - private String mOriginalUrl; - private Context mContext; - private BrowserFrame mBrowserFrame; - private int mNativeLoader; - private String mMimeType; - private String mEncoding; - private String mTransferEncoding; - private int mStatusCode; - private String mStatusText; - public long mContentLength; // Content length of the incoming data - private boolean mCancelled; // The request has been cancelled. - private boolean mAuthFailed; // indicates that the prev. auth failed - private CacheLoader mCacheLoader; - private boolean mFromCache = false; - private HttpAuthHeader mAuthHeader; - private int mErrorID = OK; - private String mErrorDescription; - private SslError mSslError; - private RequestHandle mRequestHandle; - private RequestHandle mSslErrorRequestHandle; - private long mPostIdentifier; - private boolean mSetNativeResponse; - - // Request data. It is only valid when we are doing a load from the - // cache. It is needed if the cache returns a redirect - private String mMethod; - private Map<String, String> mRequestHeaders; - private byte[] mPostData; - // Flag to indicate that this load is synchronous. - private boolean mSynchronous; - private Vector<Message> mMessageQueue; - - // Does this loader correspond to the main-frame top-level page? - private boolean mIsMainPageLoader; - // Does this loader correspond to the main content (as opposed to a supporting resource) - private final boolean mIsMainResourceLoader; - private final boolean mUserGesture; - - private Headers mHeaders; - - private final String mUsername; - private final String mPassword; - - // ========================================================================= - // Public functions - // ========================================================================= - - public static LoadListener getLoadListener(Context context, - BrowserFrame frame, String url, int nativeLoader, - boolean synchronous, boolean isMainPageLoader, - boolean isMainResource, boolean userGesture, long postIdentifier, - String username, String password) { - - sNativeLoaderCount += 1; - return new LoadListener(context, frame, url, nativeLoader, synchronous, - isMainPageLoader, isMainResource, userGesture, postIdentifier, - username, password); - } - - public static int getNativeLoaderCount() { - return sNativeLoaderCount; - } - - LoadListener(Context context, BrowserFrame frame, String url, - int nativeLoader, boolean synchronous, boolean isMainPageLoader, - boolean isMainResource, boolean userGesture, long postIdentifier, - String username, String password) { - assert !JniUtil.useChromiumHttpStack(); - - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener constructor url=" + url); - } - mContext = context; - mBrowserFrame = frame; - setUrl(url); - mNativeLoader = nativeLoader; - mSynchronous = synchronous; - if (synchronous) { - mMessageQueue = new Vector<Message>(); - } - mIsMainPageLoader = isMainPageLoader; - mIsMainResourceLoader = isMainResource; - mUserGesture = userGesture; - mPostIdentifier = postIdentifier; - mUsername = username; - mPassword = password; - } - - /** - * We keep a count of refs to the nativeLoader so we do not create - * so many LoadListeners that the GREFs blow up - */ - private void clearNativeLoader() { - sNativeLoaderCount -= 1; - mNativeLoader = 0; - mSetNativeResponse = false; - } - - /* - * This message handler is to facilitate communication between the network - * thread and the browser thread. - */ - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_CONTENT_HEADERS: - /* - * This message is sent when the LoadListener has headers - * available. The headers are sent onto WebCore to see what we - * should do with them. - */ - handleHeaders((Headers) msg.obj); - break; - - case MSG_CONTENT_DATA: - /* - * This message is sent when the LoadListener has data available - * in it's data buffer. This data buffer could be filled from a - * file (this thread) or from http (Network thread). - */ - if (mNativeLoader != 0 && !ignoreCallbacks()) { - commitLoad(); - } - break; - - case MSG_CONTENT_FINISHED: - /* - * This message is sent when the LoadListener knows that the - * load is finished. This message is not sent in the case of an - * error. - * - */ - handleEndData(); - break; - - case MSG_CONTENT_ERROR: - /* - * This message is sent when a load error has occured. The - * LoadListener will clean itself up. - */ - handleError(msg.arg1, (String) msg.obj); - break; - - case MSG_LOCATION_CHANGED: - /* - * This message is sent from LoadListener.endData to inform the - * browser activity that the location of the top level page - * changed. - */ - doRedirect(); - break; - - case MSG_LOCATION_CHANGED_REQUEST: - /* - * This message is sent from endData on receipt of a 307 - * Temporary Redirect in response to a POST -- the user must - * confirm whether to continue loading. If the user says Yes, - * we simply call MSG_LOCATION_CHANGED. If the user says No, - * we call MSG_CONTENT_FINISHED. - */ - Message contMsg = obtainMessage(MSG_LOCATION_CHANGED); - Message stopMsg = obtainMessage(MSG_CONTENT_FINISHED); - mBrowserFrame.getCallbackProxy().onFormResubmission( - stopMsg, contMsg); - break; - - case MSG_STATUS: - /* - * This message is sent from the network thread when the http - * stack has received the status response from the server. - */ - HashMap status = (HashMap) msg.obj; - handleStatus(((Integer) status.get("major")).intValue(), - ((Integer) status.get("minor")).intValue(), - ((Integer) status.get("code")).intValue(), - (String) status.get("reason")); - break; - - case MSG_SSL_CERTIFICATE: - /* - * This message is sent when the network thread receives a ssl - * certificate. - */ - handleCertificate((SslCertificate) msg.obj); - break; - - case MSG_SSL_ERROR: - /* - * This message is sent when the network thread encounters a - * ssl error. - */ - handleSslError((SslError) msg.obj); - break; - } - } - - /** - * @return The loader's BrowserFrame. - */ - BrowserFrame getFrame() { - return mBrowserFrame; - } - - Context getContext() { - return mContext; - } - - /* package */ boolean isSynchronous() { - return mSynchronous; - } - - /** - * @return True iff the load has been cancelled - */ - public boolean cancelled() { - return mCancelled; - } - - /** - * Parse the headers sent from the server. - * @param headers gives up the HeaderGroup - * IMPORTANT: as this is called from network thread, can't call native - * directly - */ - public void headers(Headers headers) { - if (DebugFlags.LOAD_LISTENER) Log.v(LOGTAG, "LoadListener.headers"); - // call db (setCookie) in the non-WebCore thread - if (mCancelled) return; - ArrayList<String> cookies = headers.getSetCookie(); - for (int i = 0; i < cookies.size(); ++i) { - CookieManager.getInstance().setCookie(mUri, cookies.get(i)); - } - sendMessageInternal(obtainMessage(MSG_CONTENT_HEADERS, headers)); - } - - // This is the same regex that DOMImplementation uses to check for xml - // content. Use this to check if another Activity wants to handle the - // content before giving it to webkit. - private static final String XML_MIME_TYPE = - "^[\\w_\\-+~!$\\^{}|.%'`#&*]+/" + - "[\\w_\\-+~!$\\^{}|.%'`#&*]+\\+xml$"; - - // Does the header parsing work on the WebCore thread. - private void handleHeaders(Headers headers) { - if (mCancelled) return; - - // Note: the headers we care in LoadListeners, like - // content-type/content-length, should not be updated for partial - // content. Just skip here and go ahead with adding data. - if (mStatusCode == HTTP_PARTIAL_CONTENT) { - // we don't support cache for partial content yet - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); - return; - } - - mHeaders = headers; - - long contentLength = headers.getContentLength(); - if (contentLength != Headers.NO_CONTENT_LENGTH) { - mContentLength = contentLength; - } else { - mContentLength = 0; - } - - String contentType = headers.getContentType(); - if (contentType != null) { - parseContentTypeHeader(contentType); - mMimeType = MimeTypeMap.getSingleton().remapGenericMimeType( - mMimeType, mUrl, headers.getContentDisposition()); - } else { - /* Often when servers respond with 304 Not Modified or a - Redirect, then they don't specify a MIMEType. When this - occurs, the function below is called. In the case of - 304 Not Modified, the cached headers are used rather - than the headers that are returned from the server. */ - guessMimeType(); - } - // At this point, mMimeType has been set to non-null. - if (mIsMainPageLoader && mIsMainResourceLoader && mUserGesture && - Pattern.matches(XML_MIME_TYPE, mMimeType) && - !mMimeType.equalsIgnoreCase("application/xhtml+xml")) { - Intent i = new Intent(Intent.ACTION_VIEW); - i.setDataAndType(Uri.parse(url()), mMimeType); - ResolveInfo info = mContext.getPackageManager().resolveActivity(i, - PackageManager.MATCH_DEFAULT_ONLY); - if (info != null && !mContext.getPackageName().equals( - info.activityInfo.packageName)) { - // someone (other than the current app) knows how to - // handle this mime type. - try { - mContext.startActivity(i); - mBrowserFrame.stopLoading(); - return; - } catch (ActivityNotFoundException ex) { - // continue loading internally. - } - } - } - - // is it an authentication request? - boolean mustAuthenticate = (mStatusCode == HTTP_AUTH || - mStatusCode == HTTP_PROXY_AUTH); - // is it a proxy authentication request? - boolean isProxyAuthRequest = (mStatusCode == HTTP_PROXY_AUTH); - // is this authentication request due to a failed attempt to - // authenticate ealier? - mAuthFailed = false; - - // if we tried to authenticate ourselves last time - if (mAuthHeader != null) { - // we failed, if we must authenticate again now and - // we have a proxy-ness match - mAuthFailed = (mustAuthenticate && - isProxyAuthRequest == mAuthHeader.isProxy()); - - // if we did NOT fail and last authentication request was a - // proxy-authentication request - if (!mAuthFailed && mAuthHeader.isProxy()) { - Network network = Network.getInstance(mContext); - // if we have a valid proxy set - if (network.isValidProxySet()) { - /* The proxy credentials can be read in the WebCore thread - */ - synchronized (network) { - // save authentication credentials for pre-emptive proxy - // authentication - network.setProxyUsername(mAuthHeader.getUsername()); - network.setProxyPassword(mAuthHeader.getPassword()); - } - } - } - } - - // it is only here that we can reset the last mAuthHeader object - // (if existed) and start a new one!!! - mAuthHeader = null; - if (mustAuthenticate) { - if (mStatusCode == HTTP_AUTH) { - mAuthHeader = parseAuthHeader( - headers.getWwwAuthenticate()); - } else { - mAuthHeader = parseAuthHeader( - headers.getProxyAuthenticate()); - // if successfully parsed the header - if (mAuthHeader != null) { - // mark the auth-header object as a proxy - mAuthHeader.setProxy(); - } - } - } - - // Only create a cache file if the server has responded positively. - if ((mStatusCode == HTTP_OK || - mStatusCode == HTTP_FOUND || - mStatusCode == HTTP_MOVED_PERMANENTLY || - mStatusCode == HTTP_TEMPORARY_REDIRECT) && - mNativeLoader != 0) { - // for POST request, only cache the result if there is an identifier - // associated with it. postUrl() or form submission should set the - // identifier while XHR POST doesn't. - if (!mFromCache && mRequestHandle != null - && (!mRequestHandle.getMethod().equals("POST") - || mPostIdentifier != 0)) { - WebViewWorker.CacheCreateData data = new WebViewWorker.CacheCreateData(); - data.mListener = this; - data.mUrl = mUrl; - data.mMimeType = mMimeType; - data.mStatusCode = mStatusCode; - data.mPostId = mPostIdentifier; - data.mHeaders = headers; - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_CREATE_CACHE, data).sendToTarget(); - } - WebViewWorker.CacheEncoding ce = new WebViewWorker.CacheEncoding(); - ce.mEncoding = mEncoding; - ce.mListener = this; - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_UPDATE_CACHE_ENCODING, ce).sendToTarget(); - } - commitHeadersCheckRedirect(); - } - - /** - * @return True iff this loader is in the proxy-authenticate state. - */ - boolean proxyAuthenticate() { - if (mAuthHeader != null) { - return mAuthHeader.isProxy(); - } - - return false; - } - - /** - * Report the status of the response. - * TODO: Comments about each parameter. - * IMPORTANT: as this is called from network thread, can't call native - * directly - */ - public void status(int majorVersion, int minorVersion, - int code, /* Status-Code value */ String reasonPhrase) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener: from: " + mUrl - + " major: " + majorVersion - + " minor: " + minorVersion - + " code: " + code - + " reason: " + reasonPhrase); - } - HashMap status = new HashMap(); - status.put("major", majorVersion); - status.put("minor", minorVersion); - status.put("code", code); - status.put("reason", reasonPhrase); - // New status means new data. Clear the old. - mDataBuilder.clear(); - mMimeType = ""; - mEncoding = ""; - mTransferEncoding = ""; - sendMessageInternal(obtainMessage(MSG_STATUS, status)); - } - - // Handle the status callback on the WebCore thread. - private void handleStatus(int major, int minor, int code, String reason) { - if (mCancelled) return; - - mStatusCode = code; - mStatusText = reason; - mPermanent = false; - } - - /** - * Implementation of certificate handler for EventHandler. Called - * before a resource is requested. In this context, can be called - * multiple times if we have redirects - * - * IMPORTANT: as this is called from network thread, can't call - * native directly - * - * @param certificate The SSL certifcate or null if the request - * was not secure - */ - public void certificate(SslCertificate certificate) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.certificate: " + certificate); - } - sendMessageInternal(obtainMessage(MSG_SSL_CERTIFICATE, certificate)); - } - - // Handle the certificate on the WebCore thread. - private void handleCertificate(SslCertificate certificate) { - // if this is main resource of the top frame - if (mIsMainPageLoader && mIsMainResourceLoader) { - // update the browser frame with certificate - mBrowserFrame.certificate(certificate); - } - } - - /** - * Implementation of error handler for EventHandler. - * Subclasses should call this method to have error fields set. - * @param id The error id described by EventHandler. - * @param description A string description of the error. - * IMPORTANT: as this is called from network thread, can't call native - * directly - */ - public void error(int id, String description) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.error url:" + - url() + " id:" + id + " description:" + description); - } - sendMessageInternal(obtainMessage(MSG_CONTENT_ERROR, id, 0, description)); - } - - // Handle the error on the WebCore thread. - private void handleError(int id, String description) { - mErrorID = id; - mErrorDescription = description; - detachRequestHandle(); - notifyError(); - tearDown(); - } - - /** - * Add data to the internal collection of data. This function is used by - * the data: scheme, about: scheme and http/https schemes. - * @param data A byte array containing the content. - * @param length The length of data. - * IMPORTANT: as this is called from network thread, can't call native - * directly - * XXX: Unlike the other network thread methods, this method can do the - * work of decoding the data and appending it to the data builder. - */ - public void data(byte[] data, int length) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.data(): url: " + url()); - } - - // The reason isEmpty() and append() need to synchronized together is - // because it is possible for getFirstChunk() to be called multiple - // times between isEmpty() and append(). This could cause commitLoad() - // to finish before processing the newly appended data and no message - // will be sent. - boolean sendMessage = false; - synchronized (mDataBuilder) { - sendMessage = mDataBuilder.isEmpty(); - mDataBuilder.append(data, 0, length); - } - if (sendMessage) { - // Send a message whenever data comes in after a write to WebCore - sendMessageInternal(obtainMessage(MSG_CONTENT_DATA)); - } - } - - /** - * Event handler's endData call. Send a message to the handler notifying - * them that the data has finished. - * IMPORTANT: as this is called from network thread, can't call native - * directly - */ - public void endData() { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.endData(): url: " + url()); - } - sendMessageInternal(obtainMessage(MSG_CONTENT_FINISHED)); - } - - // Handle the end of data. - private void handleEndData() { - if (mCancelled) return; - - switch (mStatusCode) { - case HTTP_MOVED_PERMANENTLY: - // 301 - permanent redirect - mPermanent = true; - case HTTP_FOUND: - case HTTP_SEE_OTHER: - case HTTP_TEMPORARY_REDIRECT: - // 301, 302, 303, and 307 - redirect - if (mStatusCode == HTTP_TEMPORARY_REDIRECT) { - if (mRequestHandle != null && - mRequestHandle.getMethod().equals("POST")) { - sendMessageInternal(obtainMessage( - MSG_LOCATION_CHANGED_REQUEST)); - } else if (mMethod != null && mMethod.equals("POST")) { - sendMessageInternal(obtainMessage( - MSG_LOCATION_CHANGED_REQUEST)); - } else { - sendMessageInternal(obtainMessage(MSG_LOCATION_CHANGED)); - } - } else { - sendMessageInternal(obtainMessage(MSG_LOCATION_CHANGED)); - } - return; - - case HTTP_AUTH: - case HTTP_PROXY_AUTH: - // According to rfc2616, the response for HTTP_AUTH must include - // WWW-Authenticate header field and the response for - // HTTP_PROXY_AUTH must include Proxy-Authenticate header field. - if (mAuthHeader != null && - (Network.getInstance(mContext).isValidProxySet() || - !mAuthHeader.isProxy())) { - // If this is the first attempt to authenticate, try again with the username and - // password supplied in the URL, if present. - if (!mAuthFailed && mUsername != null && mPassword != null) { - String host = mAuthHeader.isProxy() ? - Network.getInstance(mContext).getProxyHostname() : - mUri.getHost(); - HttpAuthHandlerImpl.onReceivedCredentials(this, host, - mAuthHeader.getRealm(), mUsername, mPassword); - makeAuthResponse(mUsername, mPassword); - } else { - Network.getInstance(mContext).handleAuthRequest(this); - } - return; - } - break; // use default - - case HTTP_NOT_MODIFIED: - // Server could send back NOT_MODIFIED even if we didn't - // ask for it, so make sure we have a valid CacheLoader - // before calling it. - if (mCacheLoader != null) { - if (isSynchronous()) { - mCacheLoader.load(); - } else { - // Load the cached file in a separate thread - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader) - .sendToTarget(); - } - mFromCache = true; - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener cache load url=" + url()); - } - return; - } - break; // use default - - case HTTP_NOT_FOUND: - // Not an error, the server can send back content. - default: - break; - } - detachRequestHandle(); - tearDown(); - } - - /* This method is called from CacheLoader when the initial request is - * serviced by the Cache. */ - /* package */ void setCacheLoader(CacheLoader c) { - mCacheLoader = c; - mFromCache = true; - } - - /** - * Check the cache for the current URL, and load it if it is valid. - * - * @param headers for the request - * @return true if cached response is used. - */ - boolean checkCache(Map<String, String> headers) { - // Get the cache file name for the current URL - CacheResult result = CacheManager.getCacheFile(url(), mPostIdentifier, - headers); - - // Go ahead and set the cache loader to null in case the result is - // null. - mCacheLoader = null; - // reset the flag - mFromCache = false; - - if (result != null) { - // The contents of the cache may need to be revalidated so just - // remember the cache loader in the case that the server responds - // positively to the cached content. This is also used to detect if - // a redirect came from the cache. - mCacheLoader = new CacheLoader(this, result); - - // If I got a cachedUrl and the revalidation header was not - // added, then the cached content valid, we should use it. - if (!headers.containsKey( - CacheManager.HEADER_KEY_IFNONEMATCH) && - !headers.containsKey( - CacheManager.HEADER_KEY_IFMODIFIEDSINCE)) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "FrameLoader: HTTP URL in cache " + - "and usable: " + url()); - } - if (isSynchronous()) { - mCacheLoader.load(); - } else { - // Load the cached file in a separate thread - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_ADD_STREAMLOADER, mCacheLoader) - .sendToTarget(); - } - mFromCache = true; - return true; - } - } - return false; - } - - /** - * SSL certificate error callback. Handles SSL error(s) on the way up - * to the user. - * IMPORTANT: as this is called from network thread, can't call native - * directly - */ - public boolean handleSslErrorRequest(SslError error) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, - "LoadListener.handleSslErrorRequest(): url:" + url() + - " primary error: " + error.getPrimaryError() + - " certificate: " + error.getCertificate()); - } - // Check the cached preference table before sending a message. This - // will prevent waiting for an already available answer. - if (Network.getInstance(mContext).checkSslPrefTable(this, error)) { - return true; - } - // Do not post a message for a synchronous request. This will cause a - // deadlock. Just bail on the request. - if (isSynchronous()) { - mRequestHandle.handleSslErrorResponse(false); - return true; - } - sendMessageInternal(obtainMessage(MSG_SSL_ERROR, error)); - // if it has been canceled, return false so that the network thread - // won't be blocked. If it is not canceled, save the mRequestHandle - // so that if it is canceled when MSG_SSL_ERROR is handled, we can - // still call handleSslErrorResponse which will call restartConnection - // to unblock the network thread. - if (!mCancelled) { - mSslErrorRequestHandle = mRequestHandle; - } - return !mCancelled; - } - - // Handle the ssl error on the WebCore thread. - private void handleSslError(SslError error) { - if (!mCancelled) { - mSslError = error; - Network.getInstance(mContext).handleSslErrorRequest(this); - } else if (mSslErrorRequestHandle != null) { - mSslErrorRequestHandle.handleSslErrorResponse(true); - } - mSslErrorRequestHandle = null; - } - - /** - * @return HTTP authentication realm or null if none. - */ - String realm() { - if (mAuthHeader == null) { - return null; - } else { - return mAuthHeader.getRealm(); - } - } - - /** - * Returns true iff an HTTP authentication problem has - * occured (credentials invalid). - */ - boolean authCredentialsInvalid() { - // if it is digest and the nonce is stale, we just - // resubmit with a new nonce - return (mAuthFailed && - !(mAuthHeader.isDigest() && mAuthHeader.getStale())); - } - - /** - * @return The last SSL error or null if there is none - */ - SslError sslError() { - return mSslError; - } - - /** - * Handles SSL error(s) on the way down from the user - * (the user has already provided their feedback). - */ - void handleSslErrorResponse(boolean proceed) { - if (mRequestHandle != null) { - mRequestHandle.handleSslErrorResponse(proceed); - } - if (!proceed) { - mBrowserFrame.stopLoading(); - tearDown(); - } - } - - /** - * Uses user-supplied credentials to restart a request. If the credentials - * are null, cancel the request. - */ - void handleAuthResponse(String username, String password) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.handleAuthResponse: url: " + mUrl - + " username: " + username - + " password: " + password); - } - if (username != null && password != null) { - makeAuthResponse(username, password); - } else { - // Commit whatever data we have and tear down the loader. - commitLoad(); - tearDown(); - } - } - - void makeAuthResponse(String username, String password) { - if (mAuthHeader == null || mRequestHandle == null) { - return; - } - - mAuthHeader.setUsername(username); - mAuthHeader.setPassword(password); - - int scheme = mAuthHeader.getScheme(); - if (scheme == HttpAuthHeader.BASIC) { - // create a basic response - boolean isProxy = mAuthHeader.isProxy(); - - mRequestHandle.setupBasicAuthResponse(isProxy, username, password); - } else if (scheme == HttpAuthHeader.DIGEST) { - // create a digest response - boolean isProxy = mAuthHeader.isProxy(); - - String realm = mAuthHeader.getRealm(); - String nonce = mAuthHeader.getNonce(); - String qop = mAuthHeader.getQop(); - String algorithm = mAuthHeader.getAlgorithm(); - String opaque = mAuthHeader.getOpaque(); - - mRequestHandle.setupDigestAuthResponse(isProxy, username, password, - realm, nonce, qop, algorithm, opaque); - } - } - - /** - * This is called when a request can be satisfied by the cache, however, - * the cache result could be a redirect. In this case we need to issue - * the network request. - * @param method - * @param headers - * @param postData - */ - void setRequestData(String method, Map<String, String> headers, - byte[] postData) { - mMethod = method; - mRequestHeaders = headers; - mPostData = postData; - } - - /** - * @return The current URL associated with this load. - */ - String url() { - return mUrl; - } - - /** - * @return The current WebAddress associated with this load. - */ - WebAddress getWebAddress() { - return mUri; - } - - /** - * @return URL hostname (current URL). - */ - String host() { - if (mUri != null) { - return mUri.getHost(); - } - - return null; - } - - /** - * @return The original URL associated with this load. - */ - String originalUrl() { - if (mOriginalUrl != null) { - return mOriginalUrl; - } else { - return mUrl; - } - } - - long postIdentifier() { - return mPostIdentifier; - } - - void attachRequestHandle(RequestHandle requestHandle) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.attachRequestHandle(): " + - "requestHandle: " + requestHandle); - } - mRequestHandle = requestHandle; - } - - void detachRequestHandle() { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.detachRequestHandle(): " + - "requestHandle: " + mRequestHandle); - } - mRequestHandle = null; - } - - /* - * This function is called from native WebCore code to - * notify this LoadListener that the content it is currently - * downloading should be saved to a file and not sent to - * WebCore. - */ - void downloadFile() { - // remove the cache - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); - - // Inform the client that they should download a file - mBrowserFrame.getCallbackProxy().onDownloadStart(url(), - mBrowserFrame.getUserAgentString(), - mHeaders.getContentDisposition(), - mMimeType, mContentLength); - - // Cancel the download. We need to stop the http load. - // The native loader object will get cleared by the call to - // cancel() but will also be cleared on the WebCore side - // when this function returns. - cancel(); - } - - /* - * This function is called from native WebCore code to - * find out if the given URL is in the cache, and if it can - * be used. This is just for forward/back navigation to a POST - * URL. - */ - static boolean willLoadFromCache(String url, long identifier) { - assert !JniUtil.useChromiumHttpStack(); - boolean inCache = - CacheManager.getCacheFile(url, identifier, null) != null; - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "willLoadFromCache: " + url + " in cache: " + - inCache); - } - return inCache; - } - - /* - * Reset the cancel flag. This is used when we are resuming a stopped - * download. To suspend a download, we cancel it. It can also be cancelled - * when it has run out of disk space. In this situation, the download - * can be resumed. - */ - void resetCancel() { - mCancelled = false; - } - - String mimeType() { - return mMimeType; - } - - String transferEncoding() { - return mTransferEncoding; - } - - /* - * Return the size of the content being downloaded. This represents the - * full content size, even under the situation where the download has been - * resumed after interruption. - * - * @ return full content size - */ - long contentLength() { - return mContentLength; - } - - // Commit the headers if the status code is not a redirect. - private void commitHeadersCheckRedirect() { - if (mCancelled) return; - - // do not call webcore if it is redirect. According to the code in - // InspectorController::willSendRequest(), the response is only updated - // when it is not redirect. If we received a not-modified response from - // the server and mCacheLoader is not null, do not send the response to - // webkit. This is just a validation response for loading from the - // cache. - if ((mStatusCode >= 301 && mStatusCode <= 303) || mStatusCode == 307 || - (mStatusCode == 304 && mCacheLoader != null)) { - return; - } - - commitHeaders(); - } - - // This commits the headers without checking the response status code. - private void commitHeaders() { - if (mIsMainPageLoader && CertTool.getCertType(mMimeType) != null) { - // In the case of downloading certificate, we will save it to the - // KeyStore in commitLoad. Do not call webcore. - return; - } - - // If the response is an authentication and we've resent the - // request with some credentials then don't commit the headers - // of this response; wait for the response to the request with the - // credentials. - if (mAuthHeader != null) { - return; - } - - setNativeResponse(); - } - - private void setNativeResponse() { - int nativeResponse = createNativeResponse(); - // The native code deletes the native response object. - nativeReceivedResponse(nativeResponse); - mSetNativeResponse = true; - } - - /** - * Create a WebCore response object so that it can be used by - * nativeReceivedResponse or nativeRedirectedToUrl - * @return native response pointer - */ - private int createNativeResponse() { - // If WebCore sends if-modified-since, mCacheLoader is null. If - // CacheManager sends it, mCacheLoader is not null. In this case, if the - // server responds with a 304, then we treat it like it was a 200 code - // and proceed with loading the file from the cache. - int statusCode = (mStatusCode == HTTP_NOT_MODIFIED && - mCacheLoader != null) ? HTTP_OK : mStatusCode; - // pass content-type content-length and content-encoding - final int nativeResponse = nativeCreateResponse( - originalUrl(), statusCode, mStatusText, - mMimeType, mContentLength, mEncoding); - if (mHeaders != null) { - mHeaders.getHeaders(new Headers.HeaderCallback() { - public void header(String name, String value) { - nativeSetResponseHeader(nativeResponse, name, value); - } - }); - } - return nativeResponse; - } - - /** - * Commit the load. It should be ok to call repeatedly but only before - * tearDown is called. - */ - private void commitLoad() { - if (mCancelled) return; - if (!mSetNativeResponse) { - setNativeResponse(); - } - - if (mIsMainPageLoader) { - String type = CertTool.getCertType(mMimeType); - if (type != null) { - // This must be synchronized so that no more data can be added - // after getByteSize returns. - synchronized (mDataBuilder) { - // In the case of downloading certificate, we will save it - // to the KeyStore and stop the current loading so that it - // will not generate a new history page - byte[] cert = new byte[mDataBuilder.getByteSize()]; - int offset = 0; - while (true) { - ByteArrayBuilder.Chunk c = mDataBuilder.getFirstChunk(); - if (c == null) break; - - if (c.mLength != 0) { - System.arraycopy(c.mArray, 0, cert, offset, c.mLength); - offset += c.mLength; - } - c.release(); - } - CertTool.addCertificate(mContext, type, cert); - mBrowserFrame.stopLoading(); - return; - } - } - } - - // Give the data to WebKit now. We don't have to synchronize on - // mDataBuilder here because pulling each chunk removes it from the - // internal list so it cannot be modified. - ByteArrayBuilder.Chunk c; - while (true) { - c = mDataBuilder.getFirstChunk(); - if (c == null) break; - - if (c.mLength != 0) { - nativeAddData(c.mArray, c.mLength); - WebViewWorker.CacheData data = new WebViewWorker.CacheData(); - data.mListener = this; - data.mChunk = c; - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_APPEND_CACHE, data).sendToTarget(); - } else { - c.release(); - } - } - } - - /** - * Tear down the load. Subclasses should clean up any mess because of - * cancellation or errors during the load. - */ - void tearDown() { - if (getErrorID() == OK) { - WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData(); - data.mListener = this; - data.mUrl = mUrl; - data.mPostId = mPostIdentifier; - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget(); - } else { - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); - } - if (mNativeLoader != 0) { - if (!mSetNativeResponse) { - setNativeResponse(); - } - - nativeFinished(); - clearNativeLoader(); - } - } - - /** - * Helper for getting the error ID. - * @return errorID. - */ - private int getErrorID() { - return mErrorID; - } - - /** - * Return the error description. - * @return errorDescription. - */ - private String getErrorDescription() { - return mErrorDescription; - } - - /** - * Notify the loader we encountered an error. - */ - void notifyError() { - if (mNativeLoader != 0) { - String description = getErrorDescription(); - if (description == null) description = ""; - nativeError(getErrorID(), description, url()); - clearNativeLoader(); - } - } - - /** - * Pause the load. For example, if a plugin is unable to accept more data, - * we pause reading from the request. Called directly from the WebCore thread. - */ - void pauseLoad(boolean pause) { - if (mRequestHandle != null) { - mRequestHandle.pauseRequest(pause); - } - } - - /** - * Cancel a request. - * FIXME: This will only work if the request has yet to be handled. This - * is in no way guarenteed if requests are served in a separate thread. - * It also causes major problems if cancel is called during an - * EventHandler's method call. - */ - public void cancel() { - if (DebugFlags.LOAD_LISTENER) { - if (mRequestHandle == null) { - Log.v(LOGTAG, "LoadListener.cancel(): no requestHandle"); - } else { - Log.v(LOGTAG, "LoadListener.cancel()"); - } - } - if (mRequestHandle != null) { - mRequestHandle.cancel(); - mRequestHandle = null; - } - - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); - mCancelled = true; - - clearNativeLoader(); - } - - // This count is transferred from RequestHandle to LoadListener when - // loading from the cache so that we can detect redirect loops that switch - // between the network and the cache. - private int mCacheRedirectCount; - - /* - * Perform the actual redirection. This involves setting up the new URL, - * informing WebCore and then telling the Network to start loading again. - */ - private void doRedirect() { - // as cancel() can cancel the load before doRedirect() is - // called through handleMessage, needs to check to see if we - // are canceled before proceed - if (mCancelled) { - return; - } - - // Do the same check for a redirect loop that - // RequestHandle.setupRedirect does. - if (mCacheRedirectCount >= RequestHandle.MAX_REDIRECT_COUNT) { - handleError(EventHandler.ERROR_REDIRECT_LOOP, mContext.getString( - R.string.httpErrorRedirectLoop)); - return; - } - - String redirectTo = mHeaders.getLocation(); - if (redirectTo != null) { - int nativeResponse = createNativeResponse(); - redirectTo = - nativeRedirectedToUrl(mUrl, redirectTo, nativeResponse); - // nativeRedirectedToUrl() may call cancel(), e.g. when redirect - // from a https site to a http site, check mCancelled again - if (mCancelled) { - return; - } - if (redirectTo == null) { - Log.d(LOGTAG, "Redirection failed for " - + mHeaders.getLocation()); - cancel(); - return; - } else if (!URLUtil.isNetworkUrl(redirectTo)) { - final String text = mContext - .getString(R.string.open_permission_deny) - + "\n" + redirectTo; - if (!mSetNativeResponse) { - setNativeResponse(); - } - nativeAddData(text.getBytes(), text.length()); - nativeFinished(); - clearNativeLoader(); - return; - } - - - // Cache the redirect response - if (getErrorID() == OK) { - WebViewWorker.CacheSaveData data = new WebViewWorker.CacheSaveData(); - data.mListener = this; - data.mUrl = mUrl; - data.mPostId = mPostIdentifier; - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_SAVE_CACHE, data).sendToTarget(); - } else { - WebViewWorker.getHandler().obtainMessage( - WebViewWorker.MSG_REMOVE_CACHE, this).sendToTarget(); - } - - // Saving a copy of the unstripped url for the response - mOriginalUrl = redirectTo; - // This will strip the anchor - setUrl(redirectTo); - - // Redirect may be in the cache - if (mRequestHeaders == null) { - mRequestHeaders = new HashMap<String, String>(); - } - boolean fromCache = false; - if (mCacheLoader != null) { - // This is a redirect from the cache loader. Increment the - // redirect count to avoid redirect loops. - mCacheRedirectCount++; - fromCache = true; - } - if (!checkCache(mRequestHeaders)) { - // mRequestHandle can be null when the request was satisfied - // by the cache, and the cache returned a redirect - if (mRequestHandle != null) { - try { - mRequestHandle.setupRedirect(mUrl, mStatusCode, - mRequestHeaders); - } catch(RuntimeException e) { - Log.e(LOGTAG, e.getMessage()); - // Signal a bad url error if we could not load the - // redirection. - handleError(EventHandler.ERROR_BAD_URL, - mContext.getString(R.string.httpErrorBadUrl)); - return; - } - } else { - // If the original request came from the cache, there is no - // RequestHandle, we have to create a new one through - // Network.requestURL. - Network network = Network.getInstance(getContext()); - if (!network.requestURL(mMethod, mRequestHeaders, - mPostData, this)) { - // Signal a bad url error if we could not load the - // redirection. - handleError(EventHandler.ERROR_BAD_URL, - mContext.getString(R.string.httpErrorBadUrl)); - return; - } - } - if (fromCache) { - // If we are coming from a cache load, we need to transfer - // the redirect count to the new (or old) RequestHandle to - // keep the redirect count in sync. - mRequestHandle.setRedirectCount(mCacheRedirectCount); - } - } else if (!fromCache) { - // Switching from network to cache means we need to grab the - // redirect count from the RequestHandle to keep the count in - // sync. Add 1 to account for the current redirect. - mCacheRedirectCount = mRequestHandle.getRedirectCount() + 1; - } - } else { - commitHeaders(); - commitLoad(); - tearDown(); - } - - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.onRedirect(): redirect to: " + - redirectTo); - } - } - - /** - * Parses the content-type header. - * The first part only allows '-' if it follows x or X. - */ - private static final Pattern CONTENT_TYPE_PATTERN = - Pattern.compile("^((?:[xX]-)?[a-zA-Z\\*]+/[\\w\\+\\*-]+[\\.[\\w\\+-]+]*)$"); - - /* package */ void parseContentTypeHeader(String contentType) { - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "LoadListener.parseContentTypeHeader: " + - "contentType: " + contentType); - } - - if (contentType != null) { - int i = contentType.indexOf(';'); - if (i >= 0) { - mMimeType = contentType.substring(0, i); - - int j = contentType.indexOf('=', i); - if (j > 0) { - i = contentType.indexOf(';', j); - if (i < j) { - i = contentType.length(); - } - mEncoding = contentType.substring(j + 1, i); - } else { - mEncoding = contentType.substring(i + 1); - } - // Trim excess whitespace. - mEncoding = mEncoding.trim().toLowerCase(); - - if (i < contentType.length() - 1) { - // for data: uri the mimeType and encoding have - // the form image/jpeg;base64 or text/plain;charset=utf-8 - // or text/html;charset=utf-8;base64 - mTransferEncoding = - contentType.substring(i + 1).trim().toLowerCase(); - } - } else { - mMimeType = contentType; - } - - // Trim leading and trailing whitespace - mMimeType = mMimeType.trim(); - - try { - Matcher m = CONTENT_TYPE_PATTERN.matcher(mMimeType); - if (m.find()) { - mMimeType = m.group(1); - } else { - guessMimeType(); - } - } catch (IllegalStateException ex) { - guessMimeType(); - } - } - // Ensure mMimeType is lower case. - mMimeType = mMimeType.toLowerCase(); - } - - /** - * @return The HTTP-authentication object or null if there - * is no supported scheme in the header. - * If there are several valid schemes present, we pick the - * strongest one. If there are several schemes of the same - * strength, we pick the one that comes first. - */ - private HttpAuthHeader parseAuthHeader(String header) { - if (header != null) { - int posMax = 256; - int posLen = 0; - int[] pos = new int [posMax]; - - int headerLen = header.length(); - if (headerLen > 0) { - // first, we find all unquoted instances of 'Basic' and 'Digest' - boolean quoted = false; - for (int i = 0; i < headerLen && posLen < posMax; ++i) { - if (header.charAt(i) == '\"') { - quoted = !quoted; - } else { - if (!quoted) { - if (header.regionMatches(true, i, - HttpAuthHeader.BASIC_TOKEN, 0, - HttpAuthHeader.BASIC_TOKEN.length())) { - pos[posLen++] = i; - continue; - } - - if (header.regionMatches(true, i, - HttpAuthHeader.DIGEST_TOKEN, 0, - HttpAuthHeader.DIGEST_TOKEN.length())) { - pos[posLen++] = i; - continue; - } - } - } - } - } - - if (posLen > 0) { - // consider all digest schemes first (if any) - for (int i = 0; i < posLen; i++) { - if (header.regionMatches(true, pos[i], - HttpAuthHeader.DIGEST_TOKEN, 0, - HttpAuthHeader.DIGEST_TOKEN.length())) { - String sub = header.substring(pos[i], - (i + 1 < posLen ? pos[i + 1] : headerLen)); - - HttpAuthHeader rval = new HttpAuthHeader(sub); - if (rval.isSupportedScheme()) { - // take the first match - return rval; - } - } - } - - // ...then consider all basic schemes (if any) - for (int i = 0; i < posLen; i++) { - if (header.regionMatches(true, pos[i], - HttpAuthHeader.BASIC_TOKEN, 0, - HttpAuthHeader.BASIC_TOKEN.length())) { - String sub = header.substring(pos[i], - (i + 1 < posLen ? pos[i + 1] : headerLen)); - - HttpAuthHeader rval = new HttpAuthHeader(sub); - if (rval.isSupportedScheme()) { - // take the first match - return rval; - } - } - } - } - } - - return null; - } - - /** - * If the content is a redirect or not modified we should not send - * any data into WebCore as that will cause it create a document with - * the data, then when we try to provide the real content, it will assert. - * - * @return True iff the callback should be ignored. - */ - private boolean ignoreCallbacks() { - return (mCancelled || mAuthHeader != null || - // Allow 305 (Use Proxy) to call through. - (mStatusCode > 300 && mStatusCode < 400 && mStatusCode != 305)); - } - - /** - * Sets the current URL associated with this load. - */ - void setUrl(String url) { - if (url != null) { - mUri = null; - if (URLUtil.isNetworkUrl(url)) { - mUrl = URLUtil.stripAnchor(url); - try { - mUri = new WebAddress(mUrl); - } catch (ParseException e) { - e.printStackTrace(); - } - } else { - mUrl = url; - } - } - } - - /** - * Guesses MIME type if one was not specified. Defaults to 'text/html'. In - * addition, tries to guess the MIME type based on the extension. - * - */ - private void guessMimeType() { - // Data urls must have a valid mime type or a blank string for the mime - // type (implying text/plain). - if (URLUtil.isDataUrl(mUrl) && mMimeType.length() != 0) { - cancel(); - final String text = mContext.getString(R.string.httpErrorBadUrl); - handleError(EventHandler.ERROR_BAD_URL, text); - } else { - // Note: This is ok because this is used only for the main content - // of frames. If no content-type was specified, it is fine to - // default to text/html. - mMimeType = "text/html"; - String newMimeType = guessMimeTypeFromExtension(mUrl); - if (newMimeType != null) { - mMimeType = newMimeType; - } - } - } - - /** - * guess MIME type based on the file extension. - */ - private String guessMimeTypeFromExtension(String url) { - // PENDING: need to normalize url - if (DebugFlags.LOAD_LISTENER) { - Log.v(LOGTAG, "guessMimeTypeFromExtension: url = " + url); - } - - return MimeTypeMap.getSingleton().getMimeTypeFromExtension( - MimeTypeMap.getFileExtensionFromUrl(url)); - } - - /** - * Either send a message to ourselves or queue the message if this is a - * synchronous load. - */ - private void sendMessageInternal(Message msg) { - if (mSynchronous) { - mMessageQueue.add(msg); - } else { - sendMessage(msg); - } - } - - /** - * Cycle through our messages for synchronous loads. - */ - /* package */ void loadSynchronousMessages() { - if (DebugFlags.LOAD_LISTENER && !mSynchronous) { - throw new AssertionError(); - } - // Note: this can be called twice if it is a synchronous network load, - // and there is a cache, but it needs to go to network to validate. If - // validation succeed, the CacheLoader is used so this is first called - // from http thread. Then it is called again from WebViewCore thread - // after the load is completed. So make sure the queue is cleared but - // don't set it to null. - while (!mMessageQueue.isEmpty()) { - handleMessage(mMessageQueue.remove(0)); - } - } - - //========================================================================= - // native functions - //========================================================================= - - /** - * Create a new native response object. - * @param url The url of the resource. - * @param statusCode The HTTP status code. - * @param statusText The HTTP status text. - * @param mimeType HTTP content-type. - * @param expectedLength An estimate of the content length or the length - * given by the server. - * @param encoding HTTP encoding. - * @return The native response pointer. - */ - private native int nativeCreateResponse(String url, int statusCode, - String statusText, String mimeType, long expectedLength, - String encoding); - - /** - * Add a response header to the native object. - * @param nativeResponse The native pointer. - * @param key String key. - * @param val String value. - */ - private native void nativeSetResponseHeader(int nativeResponse, String key, - String val); - - /** - * Dispatch the response. - * @param nativeResponse The native pointer. - */ - private native void nativeReceivedResponse(int nativeResponse); - - /** - * Add data to the loader. - * @param data Byte array of data. - * @param length Number of objects in data. - */ - private native void nativeAddData(byte[] data, int length); - - /** - * Tell the loader it has finished. - */ - private native void nativeFinished(); - - /** - * tell the loader to redirect - * @param baseUrl The base url. - * @param redirectTo The url to redirect to. - * @param nativeResponse The native pointer. - * @return The new url that the resource redirected to. - */ - private native String nativeRedirectedToUrl(String baseUrl, - String redirectTo, int nativeResponse); - - /** - * Tell the loader there is error - * @param id - * @param desc - * @param failingUrl The url that failed. - */ - private native void nativeError(int id, String desc, String failingUrl); - -} diff --git a/core/java/android/webkit/Network.java b/core/java/android/webkit/Network.java deleted file mode 100644 index ee9b9492ebeb..000000000000 --- a/core/java/android/webkit/Network.java +++ /dev/null @@ -1,394 +0,0 @@ -/* - * Copyright (C) 2006 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.webkit; - -import android.content.BroadcastReceiver; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; -import android.net.ConnectivityManager; -import android.net.NetworkInfo; -import android.net.http.*; -import android.os.*; -import android.util.Log; - -import java.io.ByteArrayInputStream; -import java.io.InputStream; -import java.util.Map; - -import junit.framework.Assert; - -class Network { - - private static final String LOGTAG = "network"; - - /** - * Static instance of a Network object. - */ - private static Network sNetwork; - - /** - * Flag to store the state of platform notifications, for the case - * when the Network object has not been constructed yet - */ - private static boolean sPlatformNotifications; - - /** - * Reference count for platform notifications as the network class is a - * static and can exist over multiple activities, thus over multiple - * onPause/onResume pairs. - */ - private static int sPlatformNotificationEnableRefCount; - - /** - * Proxy username if known (used for pre-emptive proxy authentication). - */ - private String mProxyUsername; - - /** - * Proxy password if known (used for pre-emptive proxy authentication). - */ - private String mProxyPassword; - - /** - * Network request queue (requests are added from the browser thread). - */ - private RequestQueue mRequestQueue; - - /** - * SSL error handler: takes care of synchronization of multiple async - * loaders with SSL-related problems. - */ - private SslErrorHandlerImpl mSslErrorHandler; - - /** - * HTTP authentication handler: takes care of synchronization of HTTP - * authentication requests. - */ - private HttpAuthHandlerImpl mHttpAuthHandler; - - private Context mContext; - - /** - * True if the currently used network connection is a roaming phone - * connection. - */ - private boolean mRoaming; - - /** - * Tracks if we are roaming. - */ - private RoamingMonitor mRoamingMonitor; - - /** - * @return The singleton instance of the network. - */ - public static synchronized Network getInstance(Context context) { - if (sNetwork == null) { - // Note Context of the Application is used here, rather than - // the what is passed in (usually a Context derived from an - // Activity) so the intent receivers belong to the application - // rather than an activity - this fixes the issue where - // Activities are created and destroyed during the lifetime of - // an Application - sNetwork = new Network(context.getApplicationContext()); - if (sPlatformNotifications) { - // Adjust the ref count before calling enable as it is already - // taken into account when the static function was called - // directly - --sPlatformNotificationEnableRefCount; - enablePlatformNotifications(); - } - } - return sNetwork; - } - - - /** - * Enables data state and proxy tracking - */ - public static void enablePlatformNotifications() { - if (++sPlatformNotificationEnableRefCount == 1) { - if (sNetwork != null) { - sNetwork.mRequestQueue.enablePlatformNotifications(); - sNetwork.monitorRoaming(); - } else { - sPlatformNotifications = true; - } - } - } - - /** - * If platform notifications are enabled, this should be called - * from onPause() or onStop() - */ - public static void disablePlatformNotifications() { - if (--sPlatformNotificationEnableRefCount == 0) { - if (sNetwork != null) { - sNetwork.mRequestQueue.disablePlatformNotifications(); - sNetwork.stopMonitoringRoaming(); - } else { - sPlatformNotifications = false; - } - } - } - - /** - * Creates a new Network object. - * XXX: Must be created in the same thread as WebCore!!!!! - */ - private Network(Context context) { - if (DebugFlags.NETWORK) { - Assert.assertTrue(Thread.currentThread(). - getName().equals(WebViewCore.THREAD_NAME)); - } - mContext = context; - mSslErrorHandler = new SslErrorHandlerImpl(); - mHttpAuthHandler = new HttpAuthHandlerImpl(this); - - mRequestQueue = new RequestQueue(context); - } - - private class RoamingMonitor extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - if (!ConnectivityManager.CONNECTIVITY_ACTION.equals(intent.getAction())) - return; - - final ConnectivityManager connManager = (ConnectivityManager) context - .getSystemService(Context.CONNECTIVITY_SERVICE); - final NetworkInfo info = connManager.getActiveNetworkInfo(); - if (info != null) - mRoaming = info.isRoaming(); - }; - }; - - private void monitorRoaming() { - mRoamingMonitor = new RoamingMonitor(); - IntentFilter filter = new IntentFilter(); - filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); - mContext.registerReceiver(sNetwork.mRoamingMonitor, filter); - } - - private void stopMonitoringRoaming() { - if (mRoamingMonitor != null) { - mContext.unregisterReceiver(mRoamingMonitor); - mRoamingMonitor = null; - } - } - - /** - * Request a url from either the network or the file system. - * @param url The url to load. - * @param method The http method. - * @param headers The http headers. - * @param postData The body of the request. - * @param loader A LoadListener for receiving the results of the request. - * @return True if the request was successfully queued. - */ - public boolean requestURL(String method, - Map<String, String> headers, - byte [] postData, - LoadListener loader) { - - String url = loader.url(); - - // Not a valid url, return false because we won't service the request! - if (!URLUtil.isValidUrl(url)) { - return false; - } - - // asset, res, file system or data stream are handled in the other code - // path. This only handles network request. - if (URLUtil.isAssetUrl(url) || URLUtil.isResourceUrl(url) - || URLUtil.isFileUrl(url) || URLUtil.isDataUrl(url)) { - return false; - } - - // If this is a prefetch, abort it if we're roaming. - if (mRoaming && headers.containsKey("X-Moz") && "prefetch".equals(headers.get("X-Moz"))) { - return false; - } - - /* FIXME: this is lame. Pass an InputStream in, rather than - making this lame one here */ - InputStream bodyProvider = null; - int bodyLength = 0; - if (postData != null) { - bodyLength = postData.length; - bodyProvider = new ByteArrayInputStream(postData); - } - - RequestQueue q = mRequestQueue; - RequestHandle handle = null; - if (loader.isSynchronous()) { - handle = q.queueSynchronousRequest(url, loader.getWebAddress(), - method, headers, loader, bodyProvider, bodyLength); - loader.attachRequestHandle(handle); - handle.processRequest(); - loader.loadSynchronousMessages(); - } else { - handle = q.queueRequest(url, loader.getWebAddress(), method, - headers, loader, bodyProvider, bodyLength); - // FIXME: Although this is probably a rare condition, normal network - // requests are processed in a separate thread. This means that it - // is possible to process part of the request before setting the - // request handle on the loader. We should probably refactor this to - // ensure the handle is attached before processing begins. - loader.attachRequestHandle(handle); - } - - return true; - } - - /** - * @return True iff there is a valid proxy set. - */ - public boolean isValidProxySet() { - // The proxy host and port can be set within a different thread during - // an Intent broadcast. - synchronized (mRequestQueue) { - return mRequestQueue.getProxyHost() != null; - } - } - - /** - * Get the proxy hostname. - * @return The proxy hostname obtained from the network queue and proxy - * settings. - */ - public String getProxyHostname() { - return mRequestQueue.getProxyHost().getHostName(); - } - - /** - * @return The proxy username or null if none. - */ - public synchronized String getProxyUsername() { - return mProxyUsername; - } - - /** - * Sets the proxy username. - * @param proxyUsername Username to use when - * connecting through the proxy. - */ - public synchronized void setProxyUsername(String proxyUsername) { - if (DebugFlags.NETWORK) { - Assert.assertTrue(isValidProxySet()); - } - - mProxyUsername = proxyUsername; - } - - /** - * @return The proxy password or null if none. - */ - public synchronized String getProxyPassword() { - return mProxyPassword; - } - - /** - * Sets the proxy password. - * @param proxyPassword Password to use when - * connecting through the proxy. - */ - public synchronized void setProxyPassword(String proxyPassword) { - if (DebugFlags.NETWORK) { - Assert.assertTrue(isValidProxySet()); - } - - mProxyPassword = proxyPassword; - } - - /** - * Saves the state of network handlers (user SSL and HTTP-authentication - * preferences). - * @param outState The out-state to save (write) to. - * @return True iff succeeds. - */ - public boolean saveState(Bundle outState) { - if (DebugFlags.NETWORK) { - Log.v(LOGTAG, "Network.saveState()"); - } - - return mSslErrorHandler.saveState(outState); - } - - /** - * Restores the state of network handlers (user SSL and HTTP-authentication - * preferences). - * @param inState The in-state to load (read) from. - * @return True iff succeeds. - */ - public boolean restoreState(Bundle inState) { - if (DebugFlags.NETWORK) { - Log.v(LOGTAG, "Network.restoreState()"); - } - - return mSslErrorHandler.restoreState(inState); - } - - /** - * Clears user SSL-error preference table. - */ - public void clearUserSslPrefTable() { - mSslErrorHandler.clear(); - } - - /** - * Handles SSL error(s) on the way up to the user: the user must decide - * whether errors should be ignored or not. - * @param loader The loader that resulted in SSL errors. - */ - public void handleSslErrorRequest(LoadListener loader) { - if (DebugFlags.NETWORK) Assert.assertNotNull(loader); - if (loader != null) { - mSslErrorHandler.handleSslErrorRequest(loader); - } - } - - /* package */ boolean checkSslPrefTable(LoadListener loader, - SslError error) { - if (loader != null && error != null) { - return mSslErrorHandler.checkSslPrefTable(loader, error); - } - return false; - } - - /** - * Handles authentication requests on their way up to the user (the user - * must provide credentials). - * @param loader The loader that resulted in an HTTP - * authentication request. - */ - public void handleAuthRequest(LoadListener loader) { - if (DebugFlags.NETWORK) Assert.assertNotNull(loader); - if (loader != null) { - mHttpAuthHandler.handleAuthRequest(loader); - } - } - - // Performance probe - public void startTiming() { - mRequestQueue.startTiming(); - } - - public void stopTiming() { - mRequestQueue.stopTiming(); - } -} diff --git a/core/java/android/webkit/SslErrorHandlerImpl.java b/core/java/android/webkit/SslErrorHandlerImpl.java deleted file mode 100644 index b2e4b131ffc6..000000000000 --- a/core/java/android/webkit/SslErrorHandlerImpl.java +++ /dev/null @@ -1,273 +0,0 @@ -/* - * 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.webkit; - -import android.net.http.SslError; -import android.os.Bundle; -import android.os.Handler; -import android.os.Message; -import android.util.Log; - -import java.util.LinkedList; -import java.util.ListIterator; - -/** - * SslErrorHandler's implementation for Android Java HTTP stack. - * This class is not needed if the Chromium HTTP stack is used. - */ -class SslErrorHandlerImpl extends SslErrorHandler { - /* One problem here is that there may potentially be multiple SSL errors - * coming from multiple loaders. Therefore, we keep a queue of loaders - * that have SSL-related problems and process errors one by one in the - * order they were received. - */ - - private static final String LOGTAG = "network"; - - /** - * Queue of loaders that experience SSL-related problems. - */ - private LinkedList<LoadListener> mLoaderQueue; - - /** - * SSL error preference table. - */ - private Bundle mSslPrefTable; - - // These are only used in the client facing SslErrorHandler. - private final SslErrorHandler mOriginHandler; - private final LoadListener mLoadListener; - - // Message id for handling the response from the client. - private static final int HANDLE_RESPONSE = 100; - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case HANDLE_RESPONSE: - LoadListener loader = (LoadListener) msg.obj; - synchronized (SslErrorHandlerImpl.this) { - handleSslErrorResponse(loader, loader.sslError(), - msg.arg1 == 1); - mLoaderQueue.remove(loader); - fastProcessQueuedSslErrors(); - } - break; - } - } - - /** - * Creates a new error handler with an empty loader queue. - */ - /* package */ SslErrorHandlerImpl() { - mLoaderQueue = new LinkedList<LoadListener>(); - mSslPrefTable = new Bundle(); - - // These are used by client facing SslErrorHandlers. - mOriginHandler = null; - mLoadListener = null; - } - - /** - * Create a new error handler that will be passed to the client. - */ - private SslErrorHandlerImpl(SslErrorHandler origin, LoadListener listener) { - mOriginHandler = origin; - mLoadListener = listener; - } - - /** - * Saves this handler's state into a map. - * @return True iff succeeds. - */ - /* package */ synchronized boolean saveState(Bundle outState) { - boolean success = (outState != null); - if (success) { - // TODO? - outState.putBundle("ssl-error-handler", mSslPrefTable); - } - - return success; - } - - /** - * Restores this handler's state from a map. - * @return True iff succeeds. - */ - /* package */ synchronized boolean restoreState(Bundle inState) { - boolean success = (inState != null); - if (success) { - success = inState.containsKey("ssl-error-handler"); - if (success) { - mSslPrefTable = inState.getBundle("ssl-error-handler"); - } - } - - return success; - } - - /** - * Clears SSL error preference table. - */ - /* package */ synchronized void clear() { - mSslPrefTable.clear(); - } - - /** - * Handles requests from the network stack about whether to proceed with a - * load given an SSL error(s). We may ask the client what to do, or use a - * cached response. - */ - /* package */ synchronized void handleSslErrorRequest(LoadListener loader) { - if (DebugFlags.SSL_ERROR_HANDLER) { - Log.v(LOGTAG, "SslErrorHandler.handleSslErrorRequest(): " + - "url=" + loader.url()); - } - - if (!loader.cancelled()) { - mLoaderQueue.offer(loader); - if (loader == mLoaderQueue.peek()) { - fastProcessQueuedSslErrors(); - } - } - } - - /** - * Check the preference table to see if we already have a 'proceed' decision - * from the client for this host and for an error of equal or greater - * severity than the supplied error. If so, instruct the loader to proceed - * and return true. Otherwise return false. - */ - /* package */ synchronized boolean checkSslPrefTable(LoadListener loader, - SslError error) { - final String host = loader.host(); - final int primary = error.getPrimaryError(); - - if (DebugFlags.SSL_ERROR_HANDLER) { - assert host != null; - assert primary != -1; - } - - if (mSslPrefTable.containsKey(host) && primary <= mSslPrefTable.getInt(host)) { - if (!loader.cancelled()) { - loader.handleSslErrorResponse(true); - } - return true; - } - return false; - } - - /** - * Processes queued SSL-error confirmation requests in - * a tight loop while there is no need to ask the client. - */ - /* package */void fastProcessQueuedSslErrors() { - while (processNextLoader()); - } - - /** - * Processes the next loader in the queue. - * @return True iff should proceed to processing the - * following loader in the queue - */ - private synchronized boolean processNextLoader() { - LoadListener loader = mLoaderQueue.peek(); - if (loader != null) { - // if this loader has been cancelled - if (loader.cancelled()) { - // go to the following loader in the queue. Make sure this - // loader has been removed from the queue. - mLoaderQueue.remove(loader); - return true; - } - - SslError error = loader.sslError(); - - if (DebugFlags.SSL_ERROR_HANDLER) { - assert error != null; - } - - // checkSslPrefTable() will instruct the loader to proceed if we - // have a cached 'proceed' decision. It does not remove the loader - // from the queue. - if (checkSslPrefTable(loader, error)) { - mLoaderQueue.remove(loader); - return true; - } - - // If we can not proceed based on a cached decision, ask the client. - CallbackProxy proxy = loader.getFrame().getCallbackProxy(); - proxy.onReceivedSslError(new SslErrorHandlerImpl(this, loader), error); - } - - // the queue must be empty, stop - return false; - } - - /** - * Proceed with this load. - */ - public void proceed() { - mOriginHandler.sendMessage(mOriginHandler.obtainMessage( - HANDLE_RESPONSE, 1, 0, mLoadListener)); - } - - /** - * Cancel this load and all pending loads for the WebView that had the - * error. - */ - public void cancel() { - mOriginHandler.sendMessage(mOriginHandler.obtainMessage( - HANDLE_RESPONSE, 0, 0, mLoadListener)); - } - - /** - * Handles the response from the client about whether to proceed with this - * load. We save the response to be re-used in the future. - */ - /* package */ synchronized void handleSslErrorResponse(LoadListener loader, - SslError error, boolean proceed) { - if (DebugFlags.SSL_ERROR_HANDLER) { - assert loader != null; - assert error != null; - } - - if (DebugFlags.SSL_ERROR_HANDLER) { - Log.v(LOGTAG, "SslErrorHandler.handleSslErrorResponse():" - + " proceed: " + proceed - + " url:" + loader.url()); - } - - if (!loader.cancelled()) { - if (proceed) { - // Update the SSL error preference table - int primary = error.getPrimaryError(); - String host = loader.host(); - - if (DebugFlags.SSL_ERROR_HANDLER) { - assert host != null; - assert primary != -1; - } - boolean hasKey = mSslPrefTable.containsKey(host); - if (!hasKey || primary > mSslPrefTable.getInt(host)) { - mSslPrefTable.putInt(host, primary); - } - } - loader.handleSslErrorResponse(proceed); - } - } -} diff --git a/core/java/android/webkit/StreamLoader.java b/core/java/android/webkit/StreamLoader.java deleted file mode 100644 index 7bcd50dd4677..000000000000 --- a/core/java/android/webkit/StreamLoader.java +++ /dev/null @@ -1,204 +0,0 @@ -/* - * Copyright (C) 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. - */ - -package android.webkit; - -import android.content.Context; -import android.net.http.EventHandler; -import android.net.http.Headers; -import android.os.Handler; -import android.os.Message; - -import java.io.IOException; -import java.io.InputStream; - -/** - * This abstract class is used for all content loaders that rely on streaming - * content into the rendering engine loading framework. - * - * The class implements a state machine to load the content into the frame in - * a similar manor to the way content arrives from the network. The class uses - * messages to move from one state to the next, which enables async. loading of - * the streamed content. - * - * Classes that inherit from this class must implement two methods, the first - * method is used to setup the InputStream and notify the loading framework if - * it can load it's content. The other method allows the derived class to add - * additional HTTP headers to the response. - * - * By default, content loaded with a StreamLoader is marked with a HTTP header - * that indicates the content should not be cached. - * - */ -abstract class StreamLoader implements Handler.Callback { - - private static final int MSG_STATUS = 100; // Send status to loader - private static final int MSG_HEADERS = 101; // Send headers to loader - private static final int MSG_DATA = 102; // Send data to loader - private static final int MSG_END = 103; // Send endData to loader - - protected final Context mContext; - protected final LoadListener mLoadListener; // loader class - protected InputStream mDataStream; // stream to read data from - protected long mContentLength; // content length of data - private byte [] mData; // buffer to pass data to loader with. - - // Handler which will be initialized in the thread where load() is called. - private Handler mHandler; - - /** - * Constructor. Although this class calls the LoadListener, it only calls - * the EventHandler Interface methods. LoadListener concrete class is used - * to avoid the penality of calling an interface. - * - * @param loadlistener The LoadListener to call with the data. - */ - StreamLoader(LoadListener loadlistener) { - mLoadListener = loadlistener; - mContext = loadlistener.getContext(); - } - - /** - * This method is called when the derived class should setup mDataStream, - * and call mLoadListener.status() to indicate that the load can occur. If it - * fails to setup, it should still call status() with the error code. - * - * @return true if stream was successfully setup - */ - protected abstract boolean setupStreamAndSendStatus(); - - /** - * This method is called when the headers are about to be sent to the - * load framework. The derived class has the opportunity to add addition - * headers. - * - * @param headers Map of HTTP headers that will be sent to the loader. - */ - abstract protected void buildHeaders(Headers headers); - - /** - * Calling this method starts the load of the content for this StreamLoader. - * This method simply creates a Handler in the current thread and posts a - * message to send the status and returns immediately. - */ - final void load() { - synchronized (this) { - if (mHandler == null) { - mHandler = new Handler(this); - } - } - - if (!mLoadListener.isSynchronous()) { - mHandler.sendEmptyMessage(MSG_STATUS); - } else { - // Load the stream synchronously. - if (setupStreamAndSendStatus()) { - // We were able to open the stream, create the array - // to pass data to the loader - mData = new byte[8192]; - sendHeaders(); - while (!sendData() && !mLoadListener.cancelled()); - closeStreamAndSendEndData(); - mLoadListener.loadSynchronousMessages(); - } - } - } - - public boolean handleMessage(Message msg) { - if (mLoadListener.isSynchronous()) { - throw new AssertionError(); - } - if (mLoadListener.cancelled()) { - closeStreamAndSendEndData(); - return true; - } - switch(msg.what) { - case MSG_STATUS: - if (setupStreamAndSendStatus()) { - // We were able to open the stream, create the array - // to pass data to the loader - mData = new byte[8192]; - mHandler.sendEmptyMessage(MSG_HEADERS); - } - break; - case MSG_HEADERS: - sendHeaders(); - mHandler.sendEmptyMessage(MSG_DATA); - break; - case MSG_DATA: - if (sendData()) { - mHandler.sendEmptyMessage(MSG_END); - } else { - mHandler.sendEmptyMessage(MSG_DATA); - } - break; - case MSG_END: - closeStreamAndSendEndData(); - break; - default: - return false; - } - return true; - } - - /** - * Construct the headers and pass them to the EventHandler. - */ - private void sendHeaders() { - Headers headers = new Headers(); - if (mContentLength > 0) { - headers.setContentLength(mContentLength); - } - buildHeaders(headers); - mLoadListener.headers(headers); - } - - /** - * Read data from the stream and pass it to the EventHandler. - * If an error occurs reading the stream, then an error is sent to the - * EventHandler, and moves onto the next state - end of data. - * @return True if all the data has been read. False if sendData should be - * called again. - */ - private boolean sendData() { - if (mDataStream != null) { - try { - int amount = mDataStream.read(mData); - if (amount > 0) { - mLoadListener.data(mData, amount); - return false; - } - } catch (IOException ex) { - mLoadListener.error(EventHandler.FILE_ERROR, ex.getMessage()); - } - } - return true; - } - - /** - * Close the stream and inform the EventHandler that load is complete. - */ - private void closeStreamAndSendEndData() { - if (mDataStream != null) { - try { - mDataStream.close(); - } catch (IOException ex) { - // ignore. - } - } - mLoadListener.endData(); - } -} diff --git a/core/java/android/webkit/WebResourceResponse.java b/core/java/android/webkit/WebResourceResponse.java index e786838615a6..650310e219a4 100644 --- a/core/java/android/webkit/WebResourceResponse.java +++ b/core/java/android/webkit/WebResourceResponse.java @@ -21,40 +21,24 @@ import android.net.http.Headers; import java.io.InputStream; /** - * A WebResourceResponse is return by - * {@link WebViewClient#shouldInterceptRequest} and - * contains the response information for a particular resource. + * Encapsulates a resource response. Applications can return an instance of this + * class from {@link WebViewClient#shouldInterceptRequest} to provide a custom + * response when the WebView requests a particular resource. */ public class WebResourceResponse { - - private class Loader extends StreamLoader { - Loader(LoadListener loadListener) { - super(loadListener); - mDataStream = mInputStream; - } - @Override - protected boolean setupStreamAndSendStatus() { - mLoadListener.status(1, 1, mDataStream != null ? 200 : 404, ""); - return true; - } - @Override - protected void buildHeaders(Headers headers) { - headers.setContentType(mMimeType); - headers.setContentEncoding(mEncoding); - } - } - // Accessed by jni, do not rename without modifying the jni code. private String mMimeType; private String mEncoding; private InputStream mInputStream; /** - * Construct a response with the given mime type, encoding, and data. - * @param mimeType The mime type of the data (i.e. text/html). - * @param encoding The encoding of the bytes read from data. - * @param data An InputStream for reading custom data. The implementation - * must implement {@link InputStream#read(byte[])}. + * Constructs a resource response with the given MIME type, encoding, and + * input stream. Callers must implement + * {@link InputStream#read(byte[]) InputStream.read(byte[])} for the input + * stream. + * @param mimeType The resource response's MIME type, for example text/html + * @param encoding The resource response's encoding + * @param data The input stream that provides the resource response's data */ public WebResourceResponse(String mimeType, String encoding, InputStream data) { @@ -64,53 +48,52 @@ public class WebResourceResponse { } /** - * Set the mime type of the response data (i.e. text/html). - * @param mimeType + * Sets the resource response's MIME type, for example text/html. + * @param mimeType The resource response's MIME type */ public void setMimeType(String mimeType) { mMimeType = mimeType; } /** - * @see #setMimeType + * Gets the resource response's MIME type. + * @return The resource response's MIME type */ public String getMimeType() { return mMimeType; } /** - * Set the encoding of the response data (i.e. utf-8). This will be used to - * decode the raw bytes from the input stream. - * @param encoding + * Sets the resource response's encoding, for example UTF-8. This is used + * to decode the data from the input stream. + * @param encoding The resource response's encoding */ public void setEncoding(String encoding) { mEncoding = encoding; } /** - * @see #setEncoding + * Gets the resource response's encoding. + * @return The resource response's encoding */ public String getEncoding() { return mEncoding; } /** - * Set the input stream containing the data for this resource. - * @param data An InputStream for reading custom data. The implementation - * must implement {@link InputStream#read(byte[])}. + * Sets the input stream that provides the resource respone's data. Callers + * must implement {@link InputStream#read(byte[]) InputStream.read(byte[])}. + * @param data The input stream that provides the resource response's data */ public void setData(InputStream data) { mInputStream = data; } /** - * @see #setData + * Gets the input stream that provides the resource respone's data. + * @return The input stream that provides the resource response's data */ public InputStream getData() { return mInputStream; } - - StreamLoader loader(LoadListener listener) { - return new Loader(listener); - } } diff --git a/core/java/android/webkit/WebTextView.java b/core/java/android/webkit/WebTextView.java index 2b59b80cd889..510c1685bcae 100644 --- a/core/java/android/webkit/WebTextView.java +++ b/core/java/android/webkit/WebTextView.java @@ -160,14 +160,14 @@ import java.util.ArrayList; private MyResultReceiver mReceiver; // Types used with setType. Keep in sync with CachedInput.h - private static final int NORMAL_TEXT_FIELD = 0; - private static final int TEXT_AREA = 1; - private static final int PASSWORD = 2; - private static final int SEARCH = 3; - private static final int EMAIL = 4; - private static final int NUMBER = 5; - private static final int TELEPHONE = 6; - private static final int URL = 7; + static final int NORMAL_TEXT_FIELD = 0; + static final int TEXT_AREA = 1; + static final int PASSWORD = 2; + static final int SEARCH = 3; + static final int EMAIL = 4; + static final int NUMBER = 5; + static final int TELEPHONE = 6; + static final int URL = 7; private static final int AUTOFILL_FORM = 100; private Handler mHandler; diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index 9cd51d0ba664..5601dca8cbe4 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -16,6 +16,7 @@ package android.webkit; +import android.animation.ObjectAnimator; import android.annotation.Widget; import android.app.ActivityManager; import android.app.AlertDialog; @@ -36,6 +37,7 @@ import android.graphics.BitmapFactory; import android.graphics.BitmapShader; import android.graphics.Canvas; import android.graphics.Color; +import android.graphics.ColorFilter; import android.graphics.DrawFilter; import android.graphics.Paint; import android.graphics.PaintFlagsDrawFilter; @@ -59,6 +61,7 @@ import android.os.Message; import android.os.StrictMode; import android.os.SystemClock; import android.provider.Settings; +import android.security.KeyChain; import android.speech.tts.TextToSpeech; import android.text.Editable; import android.text.InputType; @@ -95,6 +98,7 @@ import android.view.inputmethod.InputMethodManager; import android.webkit.WebTextView.AutoCompleteAdapter; import android.webkit.WebViewCore.DrawData; import android.webkit.WebViewCore.EventHub; +import android.webkit.WebViewCore.TextFieldInitData; import android.webkit.WebViewCore.TouchEventData; import android.webkit.WebViewCore.TouchHighlightData; import android.webkit.WebViewCore.WebKitHitTest; @@ -368,6 +372,9 @@ public class WebView extends AbsoluteLayout // Used for mapping characters to keys typed. private KeyCharacterMap mKeyCharacterMap; private boolean mIsKeySentByMe; + private int mInputType; + private int mImeOptions; + private String mHint; public WebViewInputConnection() { super(WebView.this, true); @@ -375,21 +382,24 @@ public class WebView extends AbsoluteLayout @Override public boolean sendKeyEvent(KeyEvent event) { - // Latin IME occasionally sends delete codes directly using - // sendKeyEvents. WebViewInputConnection should treat this - // as a deleteSurroundingText. - if (!mIsKeySentByMe - && event.getKeyCode() == KeyEvent.KEYCODE_DEL) { - Editable editable = getEditable(); - int selectionStart = Selection.getSelectionStart(editable); - int selectionEnd = Selection.getSelectionEnd(editable); - if (selectionEnd > 0 && (selectionStart == selectionEnd)) { - int action = event.getAction(); - if (action == KeyEvent.ACTION_UP) { + // Some IMEs send key events directly using sendKeyEvents. + // WebViewInputConnection should treat these as text changes. + if (!mIsKeySentByMe) { + if (event.getAction() == KeyEvent.ACTION_UP) { + if (event.getKeyCode() == KeyEvent.KEYCODE_DEL) { return deleteSurroundingText(1, 0); - } else if (action == KeyEvent.ACTION_DOWN) { - return true; // the delete will happen in ACTION_UP + } else if (event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL) { + return deleteSurroundingText(0, 1); + } else if (event.getUnicodeChar() != 0){ + String newComposingText = + Character.toString((char)event.getUnicodeChar()); + return commitText(newComposingText, 1); } + } else if (event.getAction() == KeyEvent.ACTION_DOWN && + (event.getKeyCode() == KeyEvent.KEYCODE_DEL + || event.getKeyCode() == KeyEvent.KEYCODE_FORWARD_DEL + || event.getUnicodeChar() != 0)) { + return true; // only act on action_down } } return super.sendKeyEvent(event); @@ -449,6 +459,77 @@ public class WebView extends AbsoluteLayout return super.deleteSurroundingText(leftLength, rightLength); } + public void initEditorInfo(WebViewCore.TextFieldInitData initData) { + int type = initData.mType; + int inputType = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT; + int imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI + | EditorInfo.IME_FLAG_NO_FULLSCREEN; + if (!initData.mIsSpellCheckEnabled) { + inputType |= InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS; + } + if (WebTextView.TEXT_AREA != type + && initData.mIsTextFieldNext) { + imeOptions |= EditorInfo.IME_FLAG_NAVIGATE_NEXT; + } + switch (type) { + case WebTextView.NORMAL_TEXT_FIELD: + imeOptions |= EditorInfo.IME_ACTION_GO; + break; + case WebTextView.TEXT_AREA: + inputType |= InputType.TYPE_TEXT_FLAG_MULTI_LINE + | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES + | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT; + imeOptions |= EditorInfo.IME_ACTION_NONE; + break; + case WebTextView.PASSWORD: + inputType |= EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD; + imeOptions |= EditorInfo.IME_ACTION_GO; + break; + case WebTextView.SEARCH: + imeOptions |= EditorInfo.IME_ACTION_SEARCH; + break; + case WebTextView.EMAIL: + // inputType needs to be overwritten because of the different text variation. + inputType = InputType.TYPE_CLASS_TEXT + | InputType.TYPE_TEXT_VARIATION_WEB_EMAIL_ADDRESS; + imeOptions |= EditorInfo.IME_ACTION_GO; + break; + case WebTextView.NUMBER: + // inputType needs to be overwritten because of the different class. + inputType = InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_VARIATION_NORMAL + | InputType.TYPE_NUMBER_FLAG_SIGNED | InputType.TYPE_NUMBER_FLAG_DECIMAL; + // Number and telephone do not have both a Tab key and an + // action, so set the action to NEXT + imeOptions |= EditorInfo.IME_ACTION_NEXT; + break; + case WebTextView.TELEPHONE: + // inputType needs to be overwritten because of the different class. + inputType = InputType.TYPE_CLASS_PHONE; + imeOptions |= EditorInfo.IME_ACTION_NEXT; + break; + case WebTextView.URL: + // TYPE_TEXT_VARIATION_URI prevents Tab key from showing, so + // exclude it for now. + imeOptions |= EditorInfo.IME_ACTION_GO; + inputType |= InputType.TYPE_TEXT_VARIATION_URI; + break; + default: + imeOptions |= EditorInfo.IME_ACTION_GO; + break; + } + mHint = initData.mLabel; + mInputType = inputType; + mImeOptions = imeOptions; + } + + public void setupEditorInfo(EditorInfo outAttrs) { + outAttrs.inputType = mInputType; + outAttrs.imeOptions = mImeOptions; + outAttrs.hintText = mHint; + outAttrs.initialCapsMode = getCursorCapsMode(InputType.TYPE_CLASS_TEXT); + } + /** * Sends a text change to webkit indirectly. If it is a single- * character add or delete, it sends it as a key stroke. If it cannot @@ -474,13 +555,17 @@ public class WebView extends AbsoluteLayout && TextUtils.regionMatches(text, 0, original, 0, textLength); } + boolean sendChange = false; if (isCharacterAdd) { - sendCharacter(text.charAt(textLength - 1)); + sendChange = !sendCharacter(text.charAt(textLength - 1)); } else if (isCharacterDelete) { sendDeleteKey(); - } else if (textLength != originalLength || - !TextUtils.regionMatches(text, 0, original, 0, - textLength)) { + } else { + sendChange = (textLength != originalLength) || + !TextUtils.regionMatches(text, 0, original, 0, + textLength); + } + if (sendChange) { // Send a message so that key strokes and text replacement // do not come out of order. Message replaceMessage = mPrivateHandler.obtainMessage( @@ -494,18 +579,20 @@ public class WebView extends AbsoluteLayout * Send a single character to the WebView as a key down and up event. * @param c The character to be sent. */ - private void sendCharacter(char c) { + private boolean sendCharacter(char c) { if (mKeyCharacterMap == null) { mKeyCharacterMap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD); } char[] chars = new char[1]; chars[0] = c; KeyEvent[] events = mKeyCharacterMap.getEvents(chars); - if (events != null) { + boolean mapsToKeyEvent = (events != null); + if (mapsToKeyEvent) { for (KeyEvent event : events) { sendKeyEvent(event); } } + return mapsToKeyEvent; } /** @@ -830,14 +917,22 @@ public class WebView extends AbsoluteLayout // know to handle Shift and arrows natively first private boolean mAccessibilityScriptInjected; + + /** + * How long the caret handle will last without being touched. + */ + private static final long CARET_HANDLE_STAMINA_MS = 3000; + private Drawable mSelectHandleLeft; private Drawable mSelectHandleRight; + private Drawable mSelectHandleCenter; private Rect mSelectCursorBase = new Rect(); private int mSelectCursorBaseLayerId; private Rect mSelectCursorExtent = new Rect(); private int mSelectCursorExtentLayerId; private Rect mSelectDraggingCursor; private Point mSelectDraggingOffset = new Point(); + private boolean mIsCaretSelection; static final int HANDLE_ID_START = 0; static final int HANDLE_ID_END = 1; static final int HANDLE_ID_BASE = 2; @@ -913,7 +1008,7 @@ public class WebView extends AbsoluteLayout static final int REPLACE_BASE_CONTENT = 123; static final int FORM_DID_BLUR = 124; static final int RETURN_LABEL = 125; - static final int FIND_AGAIN = 126; + static final int UPDATE_MATCH_COUNT = 126; static final int CENTER_FIT_RECT = 127; static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128; static final int SET_SCROLLBAR_MODES = 129; @@ -934,6 +1029,7 @@ public class WebView extends AbsoluteLayout static final int COPY_TO_CLIPBOARD = 141; static final int INIT_EDIT_FIELD = 142; static final int REPLACE_TEXT = 143; + static final int CLEAR_CARET_HANDLE = 144; private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID; private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT; @@ -978,7 +1074,7 @@ public class WebView extends AbsoluteLayout "REPLACE_BASE_CONTENT", // = 123; "FORM_DID_BLUR", // = 124; "RETURN_LABEL", // = 125; - "FIND_AGAIN", // = 126; + "UPDATE_MATCH_COUNT", // = 126; "CENTER_FIT_RECT", // = 127; "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128; "SET_SCROLLBAR_MODES", // = 129; @@ -1020,9 +1116,8 @@ public class WebView extends AbsoluteLayout // keep these in sync with their counterparts in WebView.cpp private static final int DRAW_EXTRAS_NONE = 0; - private static final int DRAW_EXTRAS_FIND = 1; - private static final int DRAW_EXTRAS_SELECTION = 2; - private static final int DRAW_EXTRAS_CURSOR_RING = 3; + private static final int DRAW_EXTRAS_SELECTION = 1; + private static final int DRAW_EXTRAS_CURSOR_RING = 2; // keep this in sync with WebCore:ScrollbarMode in WebKit private static final int SCROLLBAR_AUTO = 0; @@ -1303,6 +1398,7 @@ public class WebView extends AbsoluteLayout init(); setupPackageListener(context); setupProxyListener(context); + setupTrustStorageListener(context); updateMultiTouchSupport(context); if (privateBrowsing) { @@ -1312,6 +1408,41 @@ public class WebView extends AbsoluteLayout mAutoFillData = new WebViewCore.AutoFillData(); } + private static class TrustStorageListener extends BroadcastReceiver { + @Override + public void onReceive(Context context, Intent intent) { + if (intent.getAction().equals(KeyChain.ACTION_STORAGE_CHANGED)) { + handleCertTrustChanged(); + } + } + } + private static TrustStorageListener sTrustStorageListener; + + /** + * Handles update to the trust storage. + */ + private static void handleCertTrustChanged() { + // send a message for indicating trust storage change + WebViewCore.sendStaticMessage(EventHub.TRUST_STORAGE_UPDATED, null); + } + + /* + * @param context This method expects this to be a valid context. + */ + private static void setupTrustStorageListener(Context context) { + if (sTrustStorageListener != null ) { + return; + } + IntentFilter filter = new IntentFilter(); + filter.addAction(KeyChain.ACTION_STORAGE_CHANGED); + sTrustStorageListener = new TrustStorageListener(); + Intent current = + context.getApplicationContext().registerReceiver(sTrustStorageListener, filter); + if (current != null) { + handleCertTrustChanged(); + } + } + private static class ProxyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { @@ -1703,7 +1834,7 @@ public class WebView extends AbsoluteLayout } /** - * returns the height of the titlebarview (if any). Does not care about + * Returns the height (in pixels) of the embedded title bar (if any). Does not care about * scrolling * @hide */ @@ -1712,12 +1843,15 @@ public class WebView extends AbsoluteLayout } /** - * Return the amount of the titlebarview (if any) that is visible + * Return the visible height (in pixels) of the embedded title bar (if any). * + * @return This method is obsolete and always returns 0. * @deprecated This method is now obsolete. */ @Deprecated public int getVisibleTitleHeight() { + // Actually, this method returns the height of the embedded title bar if one is set via the + // hidden setEmbeddedTitleBar method. checkThread(); return getVisibleTitleHeightImpl(); } @@ -1914,7 +2048,6 @@ public class WebView extends AbsoluteLayout public static void enablePlatformNotifications() { checkThread(); synchronized (WebView.class) { - Network.enablePlatformNotifications(); sNotificationsEnabled = true; Context context = JniUtil.getContext(); if (context != null) @@ -1932,7 +2065,6 @@ public class WebView extends AbsoluteLayout public static void disablePlatformNotifications() { checkThread(); synchronized (WebView.class) { - Network.disablePlatformNotifications(); sNotificationsEnabled = false; Context context = JniUtil.getContext(); if (context != null) @@ -3671,7 +3803,7 @@ public class WebView extends AbsoluteLayout public void findNext(boolean forward) { checkThread(); if (0 == mNativeClass) return; // client isn't initialized - nativeFindNext(forward); + mWebViewCore.sendMessage(EventHub.FIND_NEXT, forward ? 1 : 0); } /* @@ -3681,13 +3813,40 @@ public class WebView extends AbsoluteLayout * that were found. */ public int findAll(String find) { + return findAllBody(find, false); + } + + /** + * @hide + */ + public void findAllAsync(String find) { + findAllBody(find, true); + } + + private int findAllBody(String find, boolean isAsync) { checkThread(); if (0 == mNativeClass) return 0; // client isn't initialized - int result = find != null ? nativeFindAll(find.toLowerCase(), - find.toUpperCase(), find.equalsIgnoreCase(mLastFind)) : 0; - invalidate(); mLastFind = find; - return result; + mWebViewCore.removeMessages(EventHub.FIND_ALL); + WebViewCore.FindAllRequest request = new + WebViewCore.FindAllRequest(find); + if (isAsync) { + mWebViewCore.sendMessage(EventHub.FIND_ALL, request); + return 0; // no need to wait for response + } + synchronized(request) { + try { + mWebViewCore.sendMessageAtFrontOfQueue(EventHub.FIND_ALL, + request); + while (request.mMatchCount == -1) { + request.wait(); + } + } + catch (InterruptedException e) { + return 0; + } + } + return request.mMatchCount; } /** @@ -3723,6 +3882,7 @@ public class WebView extends AbsoluteLayout } if (text != null) { mFindCallback.setText(text); + mFindCallback.findAll(); } return true; } @@ -3742,14 +3902,6 @@ public class WebView extends AbsoluteLayout nativeSetFindIsUp(isUp); } - /** - * Return the index of the currently highlighted match. - */ - int findIndex() { - if (0 == mNativeClass) return -1; - return nativeFindIndex(); - } - // Used to know whether the find dialog is open. Affects whether // or not we draw the highlights for matches. private boolean mFindIsUp; @@ -3816,10 +3968,11 @@ public class WebView extends AbsoluteLayout checkThread(); if (mNativeClass == 0) return; - nativeSetFindIsEmpty(); - invalidate(); + mWebViewCore.removeMessages(EventHub.FIND_ALL); + mWebViewCore.sendMessage(EventHub.FIND_ALL, null); } + /** * Called when the find ActionMode ends. */ @@ -4561,15 +4714,7 @@ public class WebView extends AbsoluteLayout if (mTitleBar != null) { canvas.translate(0, getTitleHeight()); } - boolean drawJavaRings = !mTouchHighlightRegion.isEmpty() - && (mTouchMode == TOUCH_INIT_MODE - || mTouchMode == TOUCH_SHORTPRESS_START_MODE - || mTouchMode == TOUCH_SHORTPRESS_MODE - || mTouchMode == TOUCH_DONE_MODE); - boolean drawNativeRings = !drawJavaRings; - if (sDisableNavcache) { - drawNativeRings = !drawJavaRings && !isInTouchMode(); - } + boolean drawNativeRings = !sDisableNavcache; drawContent(canvas, drawNativeRings); canvas.restoreToCount(saveCount); @@ -4582,18 +4727,13 @@ public class WebView extends AbsoluteLayout invalidate(); } - // paint the highlight in the end - if (drawJavaRings) { - long delay = System.currentTimeMillis() - mTouchHighlightRequested; - if (delay < ViewConfiguration.getTapTimeout()) { - Rect r = mTouchHighlightRegion.getBounds(); - postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom); - } else { - RegionIterator iter = new RegionIterator(mTouchHighlightRegion); - Rect r = new Rect(); - while (iter.next(r)) { - canvas.drawRect(r, mTouchHightlightPaint); - } + if (mFocusTransition != null) { + mFocusTransition.draw(canvas); + } else if (shouldDrawHighlightRect()) { + RegionIterator iter = new RegionIterator(mTouchHighlightRegion); + Rect r = new Rect(); + while (iter.next(r)) { + canvas.drawRect(r, mTouchHightlightPaint); } } if (DEBUG_TOUCH_HIGHLIGHT) { @@ -4914,12 +5054,12 @@ public class WebView extends AbsoluteLayout // decide which adornments to draw int extras = DRAW_EXTRAS_NONE; - if (mFindIsUp) { - extras = DRAW_EXTRAS_FIND; - } else if (mSelectingText) { - extras = DRAW_EXTRAS_SELECTION; - } else if (drawCursorRing) { - extras = DRAW_EXTRAS_CURSOR_RING; + if (!mFindIsUp) { + if (mSelectingText) { + extras = DRAW_EXTRAS_SELECTION; + } else if (drawCursorRing) { + extras = DRAW_EXTRAS_CURSOR_RING; + } } if (DebugFlags.WEB_VIEW) { Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp @@ -4984,31 +5124,45 @@ public class WebView extends AbsoluteLayout } private void drawTextSelectionHandles(Canvas canvas) { - if (mSelectHandleLeft == null) { - mSelectHandleLeft = mContext.getResources().getDrawable( - com.android.internal.R.drawable.text_select_handle_left); - } int[] handles = new int[4]; getSelectionHandles(handles); int start_x = contentToViewDimension(handles[0]); int start_y = contentToViewDimension(handles[1]); int end_x = contentToViewDimension(handles[2]); int end_y = contentToViewDimension(handles[3]); - // Magic formula copied from TextView - start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4; - mSelectHandleLeft.setBounds(start_x, start_y, - start_x + mSelectHandleLeft.getIntrinsicWidth(), - start_y + mSelectHandleLeft.getIntrinsicHeight()); - if (mSelectHandleRight == null) { - mSelectHandleRight = mContext.getResources().getDrawable( - com.android.internal.R.drawable.text_select_handle_right); + + if (mIsCaretSelection) { + if (mSelectHandleCenter == null) { + mSelectHandleCenter = mContext.getResources().getDrawable( + com.android.internal.R.drawable.text_select_handle_middle); + } + // Caret handle is centered + start_x -= (mSelectHandleCenter.getIntrinsicWidth() / 2); + mSelectHandleCenter.setBounds(start_x, start_y, + start_x + mSelectHandleCenter.getIntrinsicWidth(), + start_y + mSelectHandleCenter.getIntrinsicHeight()); + mSelectHandleCenter.draw(canvas); + } else { + if (mSelectHandleLeft == null) { + mSelectHandleLeft = mContext.getResources().getDrawable( + com.android.internal.R.drawable.text_select_handle_left); + } + // Magic formula copied from TextView + start_x -= (mSelectHandleLeft.getIntrinsicWidth() * 3) / 4; + mSelectHandleLeft.setBounds(start_x, start_y, + start_x + mSelectHandleLeft.getIntrinsicWidth(), + start_y + mSelectHandleLeft.getIntrinsicHeight()); + if (mSelectHandleRight == null) { + mSelectHandleRight = mContext.getResources().getDrawable( + com.android.internal.R.drawable.text_select_handle_right); + } + end_x -= mSelectHandleRight.getIntrinsicWidth() / 4; + mSelectHandleRight.setBounds(end_x, end_y, + end_x + mSelectHandleRight.getIntrinsicWidth(), + end_y + mSelectHandleRight.getIntrinsicHeight()); + mSelectHandleLeft.draw(canvas); + mSelectHandleRight.draw(canvas); } - end_x -= mSelectHandleRight.getIntrinsicWidth() / 4; - mSelectHandleRight.setBounds(end_x, end_y, - end_x + mSelectHandleRight.getIntrinsicWidth(), - end_y + mSelectHandleRight.getIntrinsicHeight()); - mSelectHandleLeft.draw(canvas); - mSelectHandleRight.draw(canvas); } /** @@ -5110,18 +5264,10 @@ public class WebView extends AbsoluteLayout @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { - outAttrs.inputType = EditorInfo.IME_FLAG_NO_FULLSCREEN - | EditorInfo.TYPE_CLASS_TEXT - | EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT - | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE - | EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT - | EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; - outAttrs.imeOptions = EditorInfo.IME_ACTION_NONE; - if (mInputConnection == null) { mInputConnection = new WebViewInputConnection(); } - outAttrs.initialCapsMode = mInputConnection.getCursorCapsMode(InputType.TYPE_CLASS_TEXT); + mInputConnection.setupEditorInfo(outAttrs); return mInputConnection; } @@ -5438,6 +5584,9 @@ public class WebView extends AbsoluteLayout + "keyCode=" + keyCode + ", " + event + ", unicode=" + event.getUnicodeChar()); } + if (mIsCaretSelection) { + selectionDone(); + } if (mBlockWebkitViewMessages) { return false; } @@ -5750,6 +5899,7 @@ public class WebView extends AbsoluteLayout private boolean startSelectActionMode() { mSelectCallback = new SelectActionModeCallback(); + mSelectCallback.setTextSelected(!mIsCaretSelection); mSelectCallback.setWebView(this); if (startActionMode(mSelectCallback) == null) { // There is no ActionMode, so do not allow the user to modify a @@ -5770,9 +5920,13 @@ public class WebView extends AbsoluteLayout private boolean setupWebkitSelect() { syncSelectionCursors(); - if (!startSelectActionMode()) { - selectionDone(); - return false; + ClipboardManager cm = (ClipboardManager)(mContext + .getSystemService(Context.CLIPBOARD_SERVICE)); + if (!mIsCaretSelection || cm.hasPrimaryClip()) { + if (!startSelectActionMode()) { + selectionDone(); + return false; + } } mSelectingText = true; mTouchMode = TOUCH_DRAG_MODE; @@ -5781,6 +5935,9 @@ public class WebView extends AbsoluteLayout private void updateWebkitSelection() { int[] handles = null; + if (mIsCaretSelection) { + mSelectCursorExtent.set(mSelectCursorBase); + } if (mSelectingText) { handles = new int[4]; handles[0] = mSelectCursorBase.centerX(); @@ -5794,6 +5951,14 @@ public class WebView extends AbsoluteLayout mWebViewCore.sendMessageAtFrontOfQueue(EventHub.SELECT_TEXT, handles); } + private void resetCaretTimer() { + mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE); + if (!mSelectionStarted) { + mPrivateHandler.sendEmptyMessageDelayed(CLEAR_CARET_HANDLE, + CARET_HANDLE_STAMINA_MS); + } + } + /** * Use this method to put the WebView into text selection mode. * Do not rely on this functionality; it will be deprecated in the future. @@ -5821,9 +5986,14 @@ public class WebView extends AbsoluteLayout mSelectingText = false; // finish is idempotent, so this is fine even if selectionDone was // called by mSelectCallback.onDestroyActionMode - mSelectCallback.finish(); - mSelectCallback = null; - updateWebkitSelection(); + if (mSelectCallback != null) { + mSelectCallback.finish(); + mSelectCallback = null; + } + if (!mIsCaretSelection) { + updateWebkitSelection(); + } + mIsCaretSelection = false; invalidate(); // redraw without selection mAutoScrollX = 0; mAutoScrollY = 0; @@ -6437,18 +6607,26 @@ public class WebView extends AbsoluteLayout (eventTime - mLastTouchUpTime), eventTime); } mSelectionStarted = false; - if (mSelectingText && mSelectHandleLeft != null - && mSelectHandleRight != null) { + if (mSelectingText) { int shiftedY = y - getTitleHeight() + mScrollY; int shiftedX = x + mScrollX; - if (mSelectHandleLeft.getBounds() + if (mSelectHandleCenter != null && mSelectHandleCenter.getBounds() .contains(shiftedX, shiftedY)) { mSelectionStarted = true; mSelectDraggingCursor = mSelectCursorBase; - } else if (mSelectHandleRight.getBounds() + mPrivateHandler.removeMessages(CLEAR_CARET_HANDLE); + } else if (mSelectHandleLeft != null + && mSelectHandleLeft.getBounds() + .contains(shiftedX, shiftedY)) { + mSelectionStarted = true; + mSelectDraggingCursor = mSelectCursorBase; + } else if (mSelectHandleRight != null + && mSelectHandleRight.getBounds() .contains(shiftedX, shiftedY)) { mSelectionStarted = true; mSelectDraggingCursor = mSelectCursorExtent; + } else if (mIsCaretSelection) { + selectionDone(); } if (mSelectDraggingCursor != null) { mSelectDraggingOffset.set( @@ -7099,6 +7277,9 @@ public class WebView extends AbsoluteLayout if (mSelectingText) { mSelectionStarted = false; + if (mIsCaretSelection) { + resetCaretTimer(); + } syncSelectionCursors(); invalidate(); } @@ -8844,13 +9025,6 @@ public class WebView extends AbsoluteLayout } break; - case FIND_AGAIN: - // Ignore if find has been dismissed. - if (mFindIsUp && mFindCallback != null) { - mFindCallback.findAll(); - } - break; - case DRAG_HELD_MOTIONLESS: mHeldMotionless = MOTIONLESS_TRUE; invalidate(); @@ -9039,9 +9213,11 @@ public class WebView extends AbsoluteLayout case INIT_EDIT_FIELD: if (mInputConnection != null) { + TextFieldInitData initData = (TextFieldInitData) msg.obj; mTextGeneration = 0; - mFieldPointer = msg.arg1; - mInputConnection.setTextAndKeepSelection((String) msg.obj); + mFieldPointer = initData.mFieldPointer; + mInputConnection.initEditorInfo(initData); + mInputConnection.setTextAndKeepSelection(initData.mText); } break; @@ -9055,6 +9231,17 @@ public class WebView extends AbsoluteLayout break; } + case UPDATE_MATCH_COUNT: { + if (mFindCallback != null) { + mFindCallback.updateMatchCount(msg.arg1, msg.arg2, + (String) msg.obj); + } + break; + } + case CLEAR_CARET_HANDLE: + selectionDone(); + break; + default: super.handleMessage(msg); break; @@ -9062,10 +9249,122 @@ public class WebView extends AbsoluteLayout } } + private boolean shouldDrawHighlightRect() { + if (mFocusedNode == null || mInitialHitTestResult == null) { + return false; + } + if (mTouchHighlightRegion.isEmpty()) { + return false; + } + if (mFocusedNode.mHasFocus && !isInTouchMode()) { + return !mFocusedNode.mEditable; + } + if (mInitialHitTestResult.mType == HitTestResult.UNKNOWN_TYPE) { + return false; + } + long delay = System.currentTimeMillis() - mTouchHighlightRequested; + if (delay < ViewConfiguration.getTapTimeout()) { + Rect r = mTouchHighlightRegion.getBounds(); + postInvalidateDelayed(delay, r.left, r.top, r.right, r.bottom); + return false; + } + return true; + } + + + private FocusTransitionDrawable mFocusTransition = null; + static class FocusTransitionDrawable extends Drawable { + Region mPreviousRegion; + Region mNewRegion; + float mProgress = 0; + WebView mWebView; + Paint mPaint; + int mMaxAlpha; + Point mTranslate; + + public FocusTransitionDrawable(WebView view) { + mWebView = view; + mPaint = new Paint(mWebView.mTouchHightlightPaint); + mMaxAlpha = mPaint.getAlpha(); + } + + @Override + public void setColorFilter(ColorFilter cf) { + } + + @Override + public void setAlpha(int alpha) { + } + + @Override + public int getOpacity() { + return 0; + } + + public void setProgress(float p) { + mProgress = p; + if (mWebView.mFocusTransition == this) { + if (mProgress == 1f) + mWebView.mFocusTransition = null; + mWebView.invalidate(); + } + } + + public float getProgress() { + return mProgress; + } + + @Override + public void draw(Canvas canvas) { + if (mTranslate == null) { + Rect bounds = mPreviousRegion.getBounds(); + Point from = new Point(bounds.centerX(), bounds.centerY()); + mNewRegion.getBounds(bounds); + Point to = new Point(bounds.centerX(), bounds.centerY()); + mTranslate = new Point(from.x - to.x, from.y - to.y); + } + int alpha = (int) (mProgress * mMaxAlpha); + RegionIterator iter = new RegionIterator(mPreviousRegion); + Rect r = new Rect(); + mPaint.setAlpha(mMaxAlpha - alpha); + float tx = mTranslate.x * mProgress; + float ty = mTranslate.y * mProgress; + int save = canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.translate(-tx, -ty); + while (iter.next(r)) { + canvas.drawRect(r, mPaint); + } + canvas.restoreToCount(save); + iter = new RegionIterator(mNewRegion); + r = new Rect(); + mPaint.setAlpha(alpha); + save = canvas.save(Canvas.MATRIX_SAVE_FLAG); + tx = mTranslate.x - tx; + ty = mTranslate.y - ty; + canvas.translate(tx, ty); + while (iter.next(r)) { + canvas.drawRect(r, mPaint); + } + canvas.restoreToCount(save); + } + }; + + private boolean shouldAnimateTo(WebKitHitTest hit) { + // TODO: Don't be annoying or throw out the animation entirely + return false; + } + private void setTouchHighlightRects(WebKitHitTest hit) { + FocusTransitionDrawable transition = null; + if (shouldAnimateTo(hit)) { + transition = new FocusTransitionDrawable(this); + } Rect[] rects = hit != null ? hit.mTouchRects : null; if (!mTouchHighlightRegion.isEmpty()) { invalidate(mTouchHighlightRegion.getBounds()); + if (transition != null) { + transition.mPreviousRegion = new Region(mTouchHighlightRegion); + } mTouchHighlightRegion.setEmpty(); } if (rects != null) { @@ -9085,6 +9384,13 @@ public class WebView extends AbsoluteLayout } } invalidate(mTouchHighlightRegion.getBounds()); + if (transition != null && transition.mPreviousRegion != null) { + transition.mNewRegion = new Region(mTouchHighlightRegion); + mFocusTransition = transition; + ObjectAnimator animator = ObjectAnimator.ofFloat( + mFocusTransition, "progress", 1f); + animator.start(); + } } } @@ -9185,14 +9491,19 @@ public class WebView extends AbsoluteLayout mInputConnection.setSelection(data.mStart, data.mEnd); } } - nativeSetTextSelection(mNativeClass, data.mSelectTextPtr); if (data.mSelectTextPtr != 0) { + mIsCaretSelection = (mFieldPointer == nodePointer) + && (mFieldPointer != 0) + && (data.mStart == data.mEnd); if (!mSelectingText) { setupWebkitSelect(); } else if (!mSelectionStarted) { syncSelectionCursors(); } + if (mIsCaretSelection) { + resetCaretTimer(); + } } else { selectionDone(); } @@ -9891,9 +10202,6 @@ public class WebView extends AbsoluteLayout private native void nativeUpdateDrawGLFunction(Rect rect, Rect viewRect, RectF visibleRect, float scale); private native void nativeExtendSelection(int x, int y); - private native int nativeFindAll(String findLower, String findUpper, - boolean sameAsLastSearch); - private native void nativeFindNext(boolean forward); /* package */ native int nativeFocusCandidateFramePointer(); /* package */ native boolean nativeFocusCandidateHasNextTextfield(); /* package */ native boolean nativeFocusCandidateIsPassword(); @@ -9951,9 +10259,7 @@ public class WebView extends AbsoluteLayout private native boolean nativePointInNavCache(int x, int y, int slop); private native void nativeSelectBestAt(Rect rect); private native void nativeSelectAt(int x, int y); - private native int nativeFindIndex(); private native void nativeSetExtendSelection(); - private native void nativeSetFindIsEmpty(); private native void nativeSetFindIsUp(boolean isUp); private native void nativeSetHeightCanMeasure(boolean measure); private native boolean nativeSetBaseLayer(int nativeInstance, diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java index 14ecfbd9d992..037e3234f767 100644 --- a/core/java/android/webkit/WebViewCore.java +++ b/core/java/android/webkit/WebViewCore.java @@ -26,6 +26,7 @@ import android.graphics.Region; import android.media.MediaFile; import android.net.ProxyProperties; import android.net.Uri; +import android.net.http.CertificateChainValidator; import android.os.Bundle; import android.os.Handler; import android.os.Looper; @@ -775,6 +776,11 @@ public final class WebViewCore { Message m = (Message)msg.obj; m.sendToTarget(); break; + case EventHub.TRUST_STORAGE_UPDATED: + // post a task to network thread for updating trust manager + nativeCertTrustChanged(); + CertificateChainValidator.handleTrustStorageUpdate(); + break; } } }; @@ -881,6 +887,7 @@ public final class WebViewCore { boolean mEditable; int mTapHighlightColor = WebView.HIGHLIGHT_COLOR; Rect[] mEnclosingParentRects; + boolean mHasFocus; // These are the input values that produced this hit test int mHitTestX; @@ -912,6 +919,25 @@ public final class WebViewCore { private String mPreview; } + static class TextFieldInitData { + public TextFieldInitData(int fieldPointer, + String text, int type, boolean isSpellCheckEnabled, + boolean isTextFieldNext, String label) { + mFieldPointer = fieldPointer; + mText = text; + mType = type; + mIsSpellCheckEnabled = isSpellCheckEnabled; + mIsTextFieldNext = isTextFieldNext; + mLabel = label; + } + int mFieldPointer; + String mText; + int mType; + boolean mIsSpellCheckEnabled; + boolean mIsTextFieldNext; + String mLabel; + } + // mAction of TouchEventData can be MotionEvent.getAction() which uses the // last two bytes or one of the following values static final int ACTION_LONGPRESS = 0x100; @@ -995,6 +1021,15 @@ public final class WebViewCore { "REMOVE_JS_INTERFACE", // = 149; }; + static class FindAllRequest { + public FindAllRequest(String text) { + mSearchText = text; + mMatchCount = -1; + } + public String mSearchText; + public int mMatchCount; + } + /** * @hide */ @@ -1133,6 +1168,13 @@ public final class WebViewCore { static final int SELECT_WORD_AT = 214; static final int SELECT_ALL = 215; + // for updating state on trust storage change + static final int TRUST_STORAGE_UPDATED = 220; + + // find-on-page controls + static final int FIND_ALL = 220; + static final int FIND_NEXT = 221; + // Private handler for WebCore messages. private Handler mHandler; // Message queue for containing messages before the WebCore thread is @@ -1360,21 +1402,12 @@ public final class WebViewCore { Process.setThreadPriority(mTid, Process.THREAD_PRIORITY_BACKGROUND); pauseTimers(); - if (!JniUtil.useChromiumHttpStack()) { - WebViewWorker.getHandler().sendEmptyMessage( - WebViewWorker.MSG_PAUSE_CACHE_TRANSACTION); - } else { - nativeCloseIdleConnections(mNativeClass); - } + nativeCloseIdleConnections(mNativeClass); break; case RESUME_TIMERS: Process.setThreadPriority(mTid, mSavedPriority); resumeTimers(); - if (!JniUtil.useChromiumHttpStack()) { - WebViewWorker.getHandler().sendEmptyMessage( - WebViewWorker.MSG_RESUME_CACHE_TRANSACTION); - } break; case ON_PAUSE: @@ -1448,14 +1481,10 @@ public final class WebViewCore { } case CLEAR_SSL_PREF_TABLE: - if (JniUtil.useChromiumHttpStack()) { - // FIXME: This will not work for connections currently in use, as - // they cache the certificate responses. See http://b/5324235. - SslCertLookupTable.getInstance().clear(); - nativeCloseIdleConnections(mNativeClass); - } else { - Network.getInstance(mContext).clearUserSslPrefTable(); - } + // FIXME: This will not work for connections currently in use, as + // they cache the certificate responses. See http://b/5324235. + SslCertLookupTable.getInstance().clear(); + nativeCloseIdleConnections(mNativeClass); break; case TOUCH_UP: @@ -1776,6 +1805,22 @@ public final class WebViewCore { case SELECT_ALL: nativeSelectAll(mNativeClass); break; + case FIND_ALL: { + FindAllRequest request = (FindAllRequest) msg.obj; + if (request == null) { + nativeFindAll(mNativeClass, null); + } else { + request.mMatchCount = nativeFindAll( + mNativeClass, request.mSearchText); + synchronized(request) { + request.notify(); + } + } + break; + } + case FIND_NEXT: + nativeFindNext(mNativeClass, msg.arg1 != 0); + break; } } }; @@ -2387,14 +2432,6 @@ public final class WebViewCore { // called by JNI private void sendNotifyProgressFinished() { sendUpdateTextEntry(); - if (!JniUtil.useChromiumHttpStack()) { - // as CacheManager can behave based on database transaction, we need to - // call tick() to trigger endTransaction - WebViewWorker.getHandler().removeMessages( - WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); - WebViewWorker.getHandler().sendEmptyMessage( - WebViewWorker.MSG_CACHE_TRANSACTION_TICKER); - } contentDraw(); } @@ -2774,23 +2811,31 @@ public final class WebViewCore { } // called by JNI - private void sendFindAgain() { - if (mWebView == null) return; + private void initEditField(int pointer, String text, int inputType, + boolean isSpellCheckEnabled, boolean nextFieldIsText, + String label, int start, int end, int selectionPtr) { + if (mWebView == null) { + return; + } + TextFieldInitData initData = new TextFieldInitData(pointer, + text, inputType, isSpellCheckEnabled, nextFieldIsText, label); + Message.obtain(mWebView.mPrivateHandler, + WebView.INIT_EDIT_FIELD, initData).sendToTarget(); Message.obtain(mWebView.mPrivateHandler, - WebView.FIND_AGAIN).sendToTarget(); + WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer, + 0, new TextSelectionData(start, end, selectionPtr)) + .sendToTarget(); } // called by JNI - private void initEditField(int pointer, String text, int start, int end) { + private void updateMatchCount(int matchIndex, int matchCount, + String findText) { if (mWebView == null) { return; } Message.obtain(mWebView.mPrivateHandler, - WebView.INIT_EDIT_FIELD, pointer, 0, text).sendToTarget(); - Message.obtain(mWebView.mPrivateHandler, - WebView.REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID, pointer, - 0, new TextSelectionData(start, end, 0)) - .sendToTarget(); + WebView.UPDATE_MATCH_COUNT, matchIndex, matchCount, + findText).sendToTarget(); } private native void nativeUpdateFrameCacheIfLoading(int nativeClass); @@ -3046,6 +3091,8 @@ public final class WebViewCore { private native void nativeAutoFillForm(int nativeClass, int queryId); private native void nativeScrollLayer(int nativeClass, int layer, Rect rect); + private native int nativeFindAll(int nativeClass, String text); + private native void nativeFindNext(int nativeClass, boolean forward); /** * Deletes editable text between two points. Note that the selection may @@ -3082,4 +3129,6 @@ public final class WebViewCore { private native void nativeClearTextSelection(int nativeClass); private native void nativeSelectWordAt(int nativeClass, int x, int y); private native void nativeSelectAll(int nativeClass); + + private static native void nativeCertTrustChanged(); } diff --git a/core/java/android/webkit/WebViewDatabase.java b/core/java/android/webkit/WebViewDatabase.java index 695c154acba0..757a619ffe6b 100644 --- a/core/java/android/webkit/WebViewDatabase.java +++ b/core/java/android/webkit/WebViewDatabase.java @@ -31,9 +31,6 @@ import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteException; import android.database.sqlite.SQLiteStatement; import android.util.Log; -import android.webkit.CookieManager.Cookie; -import android.webkit.CacheManager.CacheResult; -import android.webkit.JniUtil; public class WebViewDatabase { private static final String DATABASE_FILE = "webview.db"; @@ -55,39 +52,25 @@ public class WebViewDatabase { // 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 - // 3 -> 4 Add crossdomain (For x-permitted-cross-domain-policies header) private static WebViewDatabase mInstance = null; private static SQLiteDatabase mDatabase = null; - private static SQLiteDatabase mCacheDatabase = null; // synchronize locks - private final Object mCookieLock = new Object(); private final Object mPasswordLock = new Object(); private final Object mFormLock = new Object(); private final Object mHttpAuthLock = new Object(); - // TODO: The Chromium HTTP stack handles cookies independently. - // We should consider removing the cookies table if and when we switch to - // the Chromium HTTP stack for good. private static final String mTableNames[] = { - "cookies", "password", "formurl", "formdata", "httpauth" + "password", "formurl", "formdata", "httpauth" }; // Table ids (they are index to mTableNames) - private static final int TABLE_COOKIES_ID = 0; - - private static final int TABLE_PASSWORD_ID = 1; - - private static final int TABLE_FORMURL_ID = 2; - - private static final int TABLE_FORMDATA_ID = 3; - - private static final int TABLE_HTTPAUTH_ID = 4; + private static final int TABLE_PASSWORD_ID = 0; + private static final int TABLE_FORMURL_ID = 1; + private static final int TABLE_FORMDATA_ID = 2; + private static final int TABLE_HTTPAUTH_ID = 3; // column id strings for "_id" which can be used by any table private static final String ID_COL = "_id"; @@ -96,51 +79,9 @@ public class WebViewDatabase { "_id" }; - // column id strings for "cookies" table - private static final String COOKIES_NAME_COL = "name"; - - private static final String COOKIES_VALUE_COL = "value"; - - private static final String COOKIES_DOMAIN_COL = "domain"; - - private static final String COOKIES_PATH_COL = "path"; - - private static final String COOKIES_EXPIRES_COL = "expires"; - - private static final String COOKIES_SECURE_COL = "secure"; - - // column id strings for "cache" table - private static final String CACHE_URL_COL = "url"; - - private static final String CACHE_FILE_PATH_COL = "filepath"; - - private static final String CACHE_LAST_MODIFY_COL = "lastmodify"; - - private static final String CACHE_ETAG_COL = "etag"; - - private static final String CACHE_EXPIRES_COL = "expires"; - - private static final String CACHE_EXPIRES_STRING_COL = "expiresstring"; - - private static final String CACHE_MIMETYPE_COL = "mimetype"; - - private static final String CACHE_ENCODING_COL = "encoding"; - - private static final String CACHE_HTTP_STATUS_COL = "httpstatus"; - - private static final String CACHE_LOCATION_COL = "location"; - - private static final String CACHE_CONTENTLENGTH_COL = "contentlength"; - - private static final String CACHE_CONTENTDISPOSITION_COL = "contentdisposition"; - - private static final String CACHE_CROSSDOMAIN_COL = "crossdomain"; - // column id strings for "password" table private static final String PASSWORD_HOST_COL = "host"; - private static final String PASSWORD_USERNAME_COL = "username"; - private static final String PASSWORD_PASSWORD_COL = "password"; // column id strings for "formurl" table @@ -148,38 +89,15 @@ public class WebViewDatabase { // column id strings for "formdata" table private static final String FORMDATA_URLID_COL = "urlid"; - private static final String FORMDATA_NAME_COL = "name"; - private static final String FORMDATA_VALUE_COL = "value"; // column id strings for "httpauth" table private static final String HTTPAUTH_HOST_COL = "host"; - private static final String HTTPAUTH_REALM_COL = "realm"; - private static final String HTTPAUTH_USERNAME_COL = "username"; - private static final String HTTPAUTH_PASSWORD_COL = "password"; - // use InsertHelper to improve insert performance by 40% - private static DatabaseUtils.InsertHelper mCacheInserter; - private static int mCacheUrlColIndex; - private static int mCacheFilePathColIndex; - private static int mCacheLastModifyColIndex; - private static int mCacheETagColIndex; - private static int mCacheExpiresColIndex; - private static int mCacheExpiresStringColIndex; - private static int mCacheMimeTypeColIndex; - private static int mCacheEncodingColIndex; - private static int mCacheHttpStatusColIndex; - private static int mCacheLocationColIndex; - private static int mCacheContentLengthColIndex; - private static int mCacheContentDispositionColIndex; - private static int mCacheCrossDomainColIndex; - - private static int mCacheTransactionRefcount; - // Initially true until the background thread completes. private boolean mInitialized = false; @@ -207,11 +125,9 @@ public class WebViewDatabase { } initDatabase(context); - if (JniUtil.useChromiumHttpStack()) { - context.deleteDatabase(CACHE_DATABASE_FILE); - } else { - initCacheDatabase(context); - } + // Before using the Chromium HTTP stack, we stored the WebKit cache in + // our own DB. Clean up the DB file if it's still around. + context.deleteDatabase(CACHE_DATABASE_FILE); // Thread done, notify. mInitialized = true; @@ -254,83 +170,6 @@ public class WebViewDatabase { mDatabase.setLockingEnabled(false); } - private void initCacheDatabase(Context context) { - assert !JniUtil.useChromiumHttpStack(); - - try { - mCacheDatabase = context.openOrCreateDatabase( - CACHE_DATABASE_FILE, 0, null); - } catch (SQLiteException e) { - // try again by deleting the old db and create a new one - if (context.deleteDatabase(CACHE_DATABASE_FILE)) { - mCacheDatabase = context.openOrCreateDatabase( - CACHE_DATABASE_FILE, 0, null); - } - } - mCacheDatabase.enableWriteAheadLogging(); - - // mCacheDatabase should not be null, - // the only case is RequestAPI test has problem to create db - if (mCacheDatabase == null) { - mInitialized = true; - notify(); - return; - } - - if (mCacheDatabase.getVersion() != CACHE_DATABASE_VERSION) { - mCacheDatabase.beginTransactionNonExclusive(); - try { - upgradeCacheDatabase(); - bootstrapCacheDatabase(); - mCacheDatabase.setTransactionSuccessful(); - } finally { - mCacheDatabase.endTransaction(); - } - // Erase the files from the file system in the - // case that the database was updated and the - // there were existing cache content - CacheManager.removeAllCacheFiles(); - } - - // use read_uncommitted to speed up READ - mCacheDatabase.execSQL("PRAGMA read_uncommitted = true;"); - // as only READ can be called in the - // non-WebViewWorkerThread, and read_uncommitted is used, - // we can turn off database lock to use transaction. - mCacheDatabase.setLockingEnabled(false); - - // use InsertHelper for faster insertion - mCacheInserter = - new DatabaseUtils.InsertHelper(mCacheDatabase, - "cache"); - mCacheUrlColIndex = mCacheInserter - .getColumnIndex(CACHE_URL_COL); - mCacheFilePathColIndex = mCacheInserter - .getColumnIndex(CACHE_FILE_PATH_COL); - mCacheLastModifyColIndex = mCacheInserter - .getColumnIndex(CACHE_LAST_MODIFY_COL); - mCacheETagColIndex = mCacheInserter - .getColumnIndex(CACHE_ETAG_COL); - mCacheExpiresColIndex = mCacheInserter - .getColumnIndex(CACHE_EXPIRES_COL); - mCacheExpiresStringColIndex = mCacheInserter - .getColumnIndex(CACHE_EXPIRES_STRING_COL); - mCacheMimeTypeColIndex = mCacheInserter - .getColumnIndex(CACHE_MIMETYPE_COL); - mCacheEncodingColIndex = mCacheInserter - .getColumnIndex(CACHE_ENCODING_COL); - mCacheHttpStatusColIndex = mCacheInserter - .getColumnIndex(CACHE_HTTP_STATUS_COL); - mCacheLocationColIndex = mCacheInserter - .getColumnIndex(CACHE_LOCATION_COL); - mCacheContentLengthColIndex = mCacheInserter - .getColumnIndex(CACHE_CONTENTLENGTH_COL); - mCacheContentDispositionColIndex = mCacheInserter - .getColumnIndex(CACHE_CONTENTDISPOSITION_COL); - mCacheCrossDomainColIndex = mCacheInserter - .getColumnIndex(CACHE_CROSSDOMAIN_COL); - } - private static void upgradeDatabase() { upgradeDatabaseToV10(); upgradeDatabaseFromV10ToV11(); @@ -347,14 +186,12 @@ public class WebViewDatabase { 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]); + // 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 cookies"); - // Likewise for the old cache table. - mDatabase.execSQL("DROP TABLE IF EXISTS cache"); - } + // 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, @@ -397,8 +234,7 @@ public class WebViewDatabase { return; } - mDatabase.execSQL("DROP TABLE IF EXISTS " - + mTableNames[TABLE_COOKIES_ID]); + mDatabase.execSQL("DROP TABLE IF EXISTS cookies"); mDatabase.execSQL("DROP TABLE IF EXISTS cache"); mDatabase.execSQL("DROP TABLE IF EXISTS " + mTableNames[TABLE_FORMURL_ID]); @@ -409,16 +245,6 @@ public class WebViewDatabase { mDatabase.execSQL("DROP TABLE IF EXISTS " + mTableNames[TABLE_PASSWORD_ID]); - // 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 @@ -449,36 +275,6 @@ public class WebViewDatabase { + ") ON CONFLICT REPLACE);"); } - private static void upgradeCacheDatabase() { - int oldVersion = mCacheDatabase.getVersion(); - if (oldVersion != 0) { - Log.i(LOGTAG, "Upgrading cache database from version " - + oldVersion + " to " - + CACHE_DATABASE_VERSION + ", which will destroy all old data"); - } - mCacheDatabase.execSQL("DROP TABLE IF EXISTS cache"); - mCacheDatabase.setVersion(CACHE_DATABASE_VERSION); - } - - private static void bootstrapCacheDatabase() { - if (mCacheDatabase != null) { - mCacheDatabase.execSQL("CREATE TABLE cache" - + " (" + ID_COL + " INTEGER PRIMARY KEY, " + CACHE_URL_COL - + " TEXT, " + CACHE_FILE_PATH_COL + " TEXT, " - + CACHE_LAST_MODIFY_COL + " TEXT, " + CACHE_ETAG_COL - + " TEXT, " + CACHE_EXPIRES_COL + " INTEGER, " - + CACHE_EXPIRES_STRING_COL + " TEXT, " - + CACHE_MIMETYPE_COL + " TEXT, " + CACHE_ENCODING_COL - + " TEXT," + CACHE_HTTP_STATUS_COL + " INTEGER, " - + CACHE_LOCATION_COL + " TEXT, " + CACHE_CONTENTLENGTH_COL - + " INTEGER, " + CACHE_CONTENTDISPOSITION_COL + " TEXT, " - + CACHE_CROSSDOMAIN_COL + " TEXT," - + " UNIQUE (" + CACHE_URL_COL + ") ON CONFLICT REPLACE);"); - mCacheDatabase.execSQL("CREATE INDEX cacheUrlIndex ON cache (" - + CACHE_URL_COL + ")"); - } - } - // Wait for the background initialization thread to complete and check the // database creation status. private boolean checkInitialized() { @@ -516,422 +312,6 @@ public class WebViewDatabase { } // - // cookies functions - // - - /** - * Get cookies in the format of CookieManager.Cookie inside an ArrayList for - * a given domain - * - * @return ArrayList<Cookie> If nothing is found, return an empty list. - */ - ArrayList<Cookie> getCookiesForDomain(String domain) { - ArrayList<Cookie> list = new ArrayList<Cookie>(); - if (domain == null || !checkInitialized()) { - return list; - } - - synchronized (mCookieLock) { - final String[] columns = new String[] { - ID_COL, COOKIES_DOMAIN_COL, COOKIES_PATH_COL, - COOKIES_NAME_COL, COOKIES_VALUE_COL, COOKIES_EXPIRES_COL, - COOKIES_SECURE_COL - }; - final String selection = "(" + COOKIES_DOMAIN_COL - + " GLOB '*' || ?)"; - Cursor cursor = null; - try { - cursor = mDatabase.query(mTableNames[TABLE_COOKIES_ID], - columns, selection, new String[] { domain }, null, null, - null); - if (cursor.moveToFirst()) { - int domainCol = cursor.getColumnIndex(COOKIES_DOMAIN_COL); - int pathCol = cursor.getColumnIndex(COOKIES_PATH_COL); - int nameCol = cursor.getColumnIndex(COOKIES_NAME_COL); - int valueCol = cursor.getColumnIndex(COOKIES_VALUE_COL); - int expiresCol = cursor.getColumnIndex(COOKIES_EXPIRES_COL); - int secureCol = cursor.getColumnIndex(COOKIES_SECURE_COL); - do { - Cookie cookie = new Cookie(); - cookie.domain = cursor.getString(domainCol); - cookie.path = cursor.getString(pathCol); - cookie.name = cursor.getString(nameCol); - cookie.value = cursor.getString(valueCol); - if (cursor.isNull(expiresCol)) { - cookie.expires = -1; - } else { - cookie.expires = cursor.getLong(expiresCol); - } - cookie.secure = cursor.getShort(secureCol) != 0; - cookie.mode = Cookie.MODE_NORMAL; - list.add(cookie); - } while (cursor.moveToNext()); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getCookiesForDomain", e); - } finally { - if (cursor != null) cursor.close(); - } - return list; - } - } - - /** - * Delete cookies which matches (domain, path, name). - * - * @param domain If it is null, nothing happens. - * @param path If it is null, all the cookies match (domain) will be - * deleted. - * @param name If it is null, all the cookies match (domain, path) will be - * deleted. - */ - void deleteCookies(String domain, String path, String name) { - if (domain == null || !checkInitialized()) { - return; - } - - synchronized (mCookieLock) { - final String where = "(" + COOKIES_DOMAIN_COL + " == ?) AND (" - + COOKIES_PATH_COL + " == ?) AND (" + COOKIES_NAME_COL - + " == ?)"; - mDatabase.delete(mTableNames[TABLE_COOKIES_ID], where, - new String[] { domain, path, name }); - } - } - - /** - * Add a cookie to the database - * - * @param cookie - */ - void addCookie(Cookie cookie) { - if (cookie.domain == null || cookie.path == null || cookie.name == null - || !checkInitialized()) { - return; - } - - synchronized (mCookieLock) { - ContentValues cookieVal = new ContentValues(); - cookieVal.put(COOKIES_DOMAIN_COL, cookie.domain); - cookieVal.put(COOKIES_PATH_COL, cookie.path); - cookieVal.put(COOKIES_NAME_COL, cookie.name); - cookieVal.put(COOKIES_VALUE_COL, cookie.value); - if (cookie.expires != -1) { - cookieVal.put(COOKIES_EXPIRES_COL, cookie.expires); - } - cookieVal.put(COOKIES_SECURE_COL, cookie.secure); - mDatabase.insert(mTableNames[TABLE_COOKIES_ID], null, cookieVal); - } - } - - /** - * Whether there is any cookies in the database - * - * @return TRUE if there is cookie. - */ - boolean hasCookies() { - synchronized (mCookieLock) { - return hasEntries(TABLE_COOKIES_ID); - } - } - - /** - * Clear cookie database - */ - void clearCookies() { - if (!checkInitialized()) { - return; - } - - synchronized (mCookieLock) { - mDatabase.delete(mTableNames[TABLE_COOKIES_ID], null, null); - } - } - - /** - * Clear session cookies, which means cookie doesn't have EXPIRES. - */ - void clearSessionCookies() { - if (!checkInitialized()) { - return; - } - - final String sessionExpired = COOKIES_EXPIRES_COL + " ISNULL"; - synchronized (mCookieLock) { - mDatabase.delete(mTableNames[TABLE_COOKIES_ID], sessionExpired, - null); - } - } - - /** - * Clear expired cookies - * - * @param now Time for now - */ - void clearExpiredCookies(long now) { - if (!checkInitialized()) { - return; - } - - final String expires = COOKIES_EXPIRES_COL + " <= ?"; - synchronized (mCookieLock) { - mDatabase.delete(mTableNames[TABLE_COOKIES_ID], expires, - new String[] { Long.toString(now) }); - } - } - - // - // cache functions - // - - // only called from WebViewWorkerThread - boolean startCacheTransaction() { - if (++mCacheTransactionRefcount == 1) { - if (!Thread.currentThread().equals( - WebViewWorker.getHandler().getLooper().getThread())) { - Log.w(LOGTAG, "startCacheTransaction should be called from " - + "WebViewWorkerThread instead of from " - + Thread.currentThread().getName()); - } - mCacheDatabase.beginTransactionNonExclusive(); - return true; - } - return false; - } - - // only called from WebViewWorkerThread - boolean endCacheTransaction() { - if (--mCacheTransactionRefcount == 0) { - if (!Thread.currentThread().equals( - WebViewWorker.getHandler().getLooper().getThread())) { - Log.w(LOGTAG, "endCacheTransaction should be called from " - + "WebViewWorkerThread instead of from " - + Thread.currentThread().getName()); - } - try { - mCacheDatabase.setTransactionSuccessful(); - } finally { - mCacheDatabase.endTransaction(); - } - return true; - } - return false; - } - - /** - * Get a cache item. - * - * @param url The url - * @return CacheResult The CacheManager.CacheResult - */ - CacheResult getCache(String url) { - assert !JniUtil.useChromiumHttpStack(); - - if (url == null || !checkInitialized()) { - return null; - } - - Cursor cursor = null; - final String query = "SELECT filepath, lastmodify, etag, expires, " - + "expiresstring, mimetype, encoding, httpstatus, location, contentlength, " - + "contentdisposition, crossdomain FROM cache WHERE url = ?"; - try { - cursor = mCacheDatabase.rawQuery(query, new String[] { url }); - if (cursor.moveToFirst()) { - CacheResult ret = new CacheResult(); - ret.localPath = cursor.getString(0); - ret.lastModified = cursor.getString(1); - ret.etag = cursor.getString(2); - ret.expires = cursor.getLong(3); - ret.expiresString = cursor.getString(4); - ret.mimeType = cursor.getString(5); - ret.encoding = cursor.getString(6); - ret.httpStatusCode = cursor.getInt(7); - ret.location = cursor.getString(8); - ret.contentLength = cursor.getLong(9); - ret.contentdisposition = cursor.getString(10); - ret.crossDomain = cursor.getString(11); - return ret; - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getCache", e); - } finally { - if (cursor != null) cursor.close(); - } - return null; - } - - /** - * Remove a cache item. - * - * @param url The url - */ - void removeCache(String url) { - assert !JniUtil.useChromiumHttpStack(); - - if (url == null || !checkInitialized()) { - return; - } - - mCacheDatabase.execSQL("DELETE FROM cache WHERE url = ?", new String[] { url }); - } - - /** - * Add or update a cache. CACHE_URL_COL is unique in the table. - * - * @param url The url - * @param c The CacheManager.CacheResult - */ - void addCache(String url, CacheResult c) { - assert !JniUtil.useChromiumHttpStack(); - - if (url == null || !checkInitialized()) { - return; - } - - mCacheInserter.prepareForInsert(); - mCacheInserter.bind(mCacheUrlColIndex, url); - mCacheInserter.bind(mCacheFilePathColIndex, c.localPath); - mCacheInserter.bind(mCacheLastModifyColIndex, c.lastModified); - mCacheInserter.bind(mCacheETagColIndex, c.etag); - mCacheInserter.bind(mCacheExpiresColIndex, c.expires); - mCacheInserter.bind(mCacheExpiresStringColIndex, c.expiresString); - mCacheInserter.bind(mCacheMimeTypeColIndex, c.mimeType); - mCacheInserter.bind(mCacheEncodingColIndex, c.encoding); - mCacheInserter.bind(mCacheHttpStatusColIndex, c.httpStatusCode); - mCacheInserter.bind(mCacheLocationColIndex, c.location); - mCacheInserter.bind(mCacheContentLengthColIndex, c.contentLength); - mCacheInserter.bind(mCacheContentDispositionColIndex, - c.contentdisposition); - mCacheInserter.bind(mCacheCrossDomainColIndex, c.crossDomain); - mCacheInserter.execute(); - } - - /** - * Clear cache database - */ - void clearCache() { - if (!checkInitialized()) { - return; - } - - mCacheDatabase.delete("cache", null, null); - } - - boolean hasCache() { - if (!checkInitialized()) { - return false; - } - - Cursor cursor = null; - boolean ret = false; - try { - cursor = mCacheDatabase.query("cache", ID_PROJECTION, - null, null, null, null, null); - ret = cursor.moveToFirst() == true; - } catch (IllegalStateException e) { - Log.e(LOGTAG, "hasCache", e); - } finally { - if (cursor != null) cursor.close(); - } - return ret; - } - - long getCacheTotalSize() { - if (mCacheDatabase == null) { - return 0; - } - long size = 0; - Cursor cursor = null; - final String query = "SELECT SUM(contentlength) as sum FROM cache"; - try { - cursor = mCacheDatabase.rawQuery(query, null); - if (cursor.moveToFirst()) { - size = cursor.getLong(0); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getCacheTotalSize", e); - } finally { - if (cursor != null) cursor.close(); - } - return size; - } - - List<String> trimCache(long amount) { - ArrayList<String> pathList = new ArrayList<String>(100); - Cursor cursor = null; - final String query = "SELECT contentlength, filepath FROM cache ORDER BY expires ASC"; - try { - cursor = mCacheDatabase.rawQuery(query, null); - if (cursor.moveToFirst()) { - int batchSize = 100; - StringBuilder pathStr = new StringBuilder(20 + 16 * batchSize); - pathStr.append("DELETE FROM cache WHERE filepath IN (?"); - for (int i = 1; i < batchSize; i++) { - pathStr.append(", ?"); - } - pathStr.append(")"); - SQLiteStatement statement = null; - try { - statement = mCacheDatabase.compileStatement( - pathStr.toString()); - // as bindString() uses 1-based index, initialize index to 1 - int index = 1; - do { - long length = cursor.getLong(0); - if (length == 0) { - continue; - } - amount -= length; - String filePath = cursor.getString(1); - statement.bindString(index, filePath); - pathList.add(filePath); - if (index++ == batchSize) { - statement.execute(); - statement.clearBindings(); - index = 1; - } - } while (cursor.moveToNext() && amount > 0); - if (index > 1) { - // there may be old bindings from the previous statement - // if index is less than batchSize, which is Ok. - statement.execute(); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "trimCache SQLiteStatement", e); - } finally { - if (statement != null) statement.close(); - } - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "trimCache Cursor", e); - } finally { - if (cursor != null) cursor.close(); - } - return pathList; - } - - List<String> getAllCacheFileNames() { - ArrayList<String> pathList = null; - Cursor cursor = null; - try { - cursor = mCacheDatabase.rawQuery("SELECT filepath FROM cache", - null); - if (cursor != null && cursor.moveToFirst()) { - pathList = new ArrayList<String>(cursor.getCount()); - do { - pathList.add(cursor.getString(0)); - } while (cursor.moveToNext()); - } - } catch (IllegalStateException e) { - Log.e(LOGTAG, "getAllCacheFileNames", e); - } finally { - if (cursor != null) cursor.close(); - } - return pathList; - } - - // // password functions // diff --git a/core/java/android/webkit/WebViewWorker.java b/core/java/android/webkit/WebViewWorker.java deleted file mode 100644 index 6a4ca2918f1c..000000000000 --- a/core/java/android/webkit/WebViewWorker.java +++ /dev/null @@ -1,233 +0,0 @@ -/* - * 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.webkit; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import android.net.http.Headers; -import android.os.Handler; -import android.os.HandlerThread; -import android.os.Looper; -import android.os.Message; - -/** - * WebViewWorker executes in a separate thread other than UI and WebViewCore. To - * avoid blocking UI or WebKit's execution, the caller can send a message to - * WebViewWorker.getHandler() and it will be handled in the WebViewWorkerThread. - */ -final class WebViewWorker extends Handler { - - private static final String THREAD_NAME = "WebViewWorkerThread"; - - private static WebViewWorker sWorkerHandler; - - private static Map<LoadListener, CacheManager.CacheResult> mCacheResultMap - = new HashMap<LoadListener, CacheManager.CacheResult>(); - - /** - * Package level class to be used while creating a cache entry. - */ - static class CacheCreateData { - LoadListener mListener; - String mUrl; - String mMimeType; - int mStatusCode; - long mPostId; - Headers mHeaders; - } - - /** - * Package level class to be used while saving a cache entry. - */ - static class CacheSaveData { - LoadListener mListener; - String mUrl; - long mPostId; - } - - /** - * Package level class to be used while updating a cache entry's encoding. - */ - static class CacheEncoding { - LoadListener mListener; - String mEncoding; - } - - /** - * Package level class to be used while appending data to a cache entry. - */ - static class CacheData { - LoadListener mListener; - ByteArrayBuilder.Chunk mChunk; - } - - static synchronized WebViewWorker getHandler() { - if (sWorkerHandler == null) { - HandlerThread thread = new HandlerThread(THREAD_NAME, - android.os.Process.THREAD_PRIORITY_DEFAULT - + android.os.Process.THREAD_PRIORITY_LESS_FAVORABLE); - thread.start(); - sWorkerHandler = new WebViewWorker(thread.getLooper()); - } - return sWorkerHandler; - } - - private WebViewWorker(Looper looper) { - super(looper); - } - - // trigger transaction once a minute - private static final int CACHE_TRANSACTION_TICKER_INTERVAL = 60 * 1000; - - private static boolean mCacheTickersBlocked = true; - - // message ids - static final int MSG_ADD_STREAMLOADER = 101; - static final int MSG_ADD_HTTPLOADER = 102; - static final int MSG_CREATE_CACHE = 103; - static final int MSG_UPDATE_CACHE_ENCODING = 104; - static final int MSG_APPEND_CACHE = 105; - static final int MSG_SAVE_CACHE = 106; - static final int MSG_REMOVE_CACHE = 107; - static final int MSG_TRIM_CACHE = 108; - static final int MSG_CLEAR_CACHE = 109; - static final int MSG_CACHE_TRANSACTION_TICKER = 110; - static final int MSG_PAUSE_CACHE_TRANSACTION = 111; - static final int MSG_RESUME_CACHE_TRANSACTION = 112; - - @Override - public void handleMessage(Message msg) { - switch(msg.what) { - case MSG_ADD_STREAMLOADER: { - StreamLoader loader = (StreamLoader) msg.obj; - loader.load(); - break; - } - case MSG_ADD_HTTPLOADER: { - FrameLoader loader = (FrameLoader) msg.obj; - loader.handleHTTPLoad(); - break; - } - case MSG_CREATE_CACHE: { - assert !JniUtil.useChromiumHttpStack(); - CacheCreateData data = (CacheCreateData) msg.obj; - CacheManager.CacheResult cache = CacheManager.createCacheFile( - data.mUrl, data.mStatusCode, data.mHeaders, - data.mMimeType, data.mPostId, false); - if (cache != null) { - mCacheResultMap.put(data.mListener, cache); - } else { - mCacheResultMap.remove(data.mListener); - } - break; - } - case MSG_UPDATE_CACHE_ENCODING: { - assert !JniUtil.useChromiumHttpStack(); - CacheEncoding data = (CacheEncoding) msg.obj; - CacheManager.CacheResult cache = mCacheResultMap - .get(data.mListener); - if (cache != null) { - cache.encoding = data.mEncoding; - } - break; - } - case MSG_APPEND_CACHE: { - assert !JniUtil.useChromiumHttpStack(); - CacheData data = (CacheData) msg.obj; - CacheManager.CacheResult cache = mCacheResultMap - .get(data.mListener); - if (cache != null) { - cache.contentLength += data.mChunk.mLength; - if (cache.contentLength > CacheManager.CACHE_MAX_SIZE) { - CacheManager.cleanupCacheFile(cache); - mCacheResultMap.remove(data.mListener); - } else { - try { - cache.outStream.write(data.mChunk.mArray, 0, - data.mChunk.mLength); - } catch (IOException e) { - CacheManager.cleanupCacheFile(cache); - mCacheResultMap.remove(data.mListener); - } - } - } - data.mChunk.release(); - break; - } - case MSG_SAVE_CACHE: { - assert !JniUtil.useChromiumHttpStack(); - CacheSaveData data = (CacheSaveData) msg.obj; - CacheManager.CacheResult cache = mCacheResultMap - .get(data.mListener); - if (cache != null) { - CacheManager.saveCacheFile(data.mUrl, data.mPostId, cache); - mCacheResultMap.remove(data.mListener); - } - break; - } - case MSG_REMOVE_CACHE: { - assert !JniUtil.useChromiumHttpStack(); - LoadListener listener = (LoadListener) msg.obj; - CacheManager.CacheResult cache = mCacheResultMap.get(listener); - if (cache != null) { - CacheManager.cleanupCacheFile(cache); - mCacheResultMap.remove(listener); - } - break; - } - case MSG_TRIM_CACHE: { - assert !JniUtil.useChromiumHttpStack(); - CacheManager.trimCacheIfNeeded(); - break; - } - case MSG_CLEAR_CACHE: { - assert !JniUtil.useChromiumHttpStack(); - CacheManager.clearCache(); - break; - } - case MSG_CACHE_TRANSACTION_TICKER: { - assert !JniUtil.useChromiumHttpStack(); - if (!mCacheTickersBlocked) { - CacheManager.endTransaction(); - CacheManager.startTransaction(); - sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, - CACHE_TRANSACTION_TICKER_INTERVAL); - } - break; - } - case MSG_PAUSE_CACHE_TRANSACTION: { - assert !JniUtil.useChromiumHttpStack(); - if (CacheManager.disableTransaction()) { - mCacheTickersBlocked = true; - removeMessages(MSG_CACHE_TRANSACTION_TICKER); - } - break; - } - case MSG_RESUME_CACHE_TRANSACTION: { - assert !JniUtil.useChromiumHttpStack(); - if (CacheManager.enableTransaction()) { - mCacheTickersBlocked = false; - sendEmptyMessageDelayed(MSG_CACHE_TRANSACTION_TICKER, - CACHE_TRANSACTION_TICKER_INTERVAL); - } - break; - } - } - } -} diff --git a/core/java/android/widget/AbsListView.java b/core/java/android/widget/AbsListView.java index e94b1cb12d36..2602523c283f 100644 --- a/core/java/android/widget/AbsListView.java +++ b/core/java/android/widget/AbsListView.java @@ -36,6 +36,7 @@ import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.util.LongSparseArray; +import android.util.SparseArray; import android.util.SparseBooleanArray; import android.util.StateSet; import android.view.ActionMode; @@ -87,6 +88,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te ViewTreeObserver.OnTouchModeChangeListener, RemoteViewsAdapter.RemoteAdapterConnectionCallback { + private static final String TAG = "AbsListView"; + /** * Disables the transcript mode. * @@ -263,6 +266,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private RemoteViewsAdapter mRemoteAdapter; /** + * If mAdapter != null, whenever this is true the adapter has stable IDs. + */ + boolean mAdapterHasStableIds; + + /** * This flag indicates the a full notify is required when the RemoteViewsAdapter connects */ private boolean mDeferNotifyDataSetChanged = false; @@ -812,7 +820,8 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te @Override public void setAdapter(ListAdapter adapter) { if (adapter != null) { - if (mChoiceMode != CHOICE_MODE_NONE && mAdapter.hasStableIds() && + mAdapterHasStableIds = mAdapter.hasStableIds(); + if (mChoiceMode != CHOICE_MODE_NONE && mAdapterHasStableIds && mCheckedIdStates == null) { mCheckedIdStates = new LongSparseArray<Integer>(); } @@ -2011,6 +2020,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te isScrap[0] = false; View scrapView; + scrapView = mRecycler.getTransientStateView(position); + if (scrapView != null) { + return scrapView; + } + scrapView = mRecycler.getScrapView(position); View child; @@ -2051,6 +2065,20 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } + if (mAdapterHasStableIds) { + final ViewGroup.LayoutParams vlp = child.getLayoutParams(); + LayoutParams lp; + if (vlp == null) { + lp = (LayoutParams) generateDefaultLayoutParams(); + } else if (!checkLayoutParams(vlp)) { + lp = (LayoutParams) generateLayoutParams(vlp); + } else { + lp = (LayoutParams) vlp; + } + lp.itemId = mAdapter.getItemId(position); + child.setLayoutParams(lp); + } + return child; } @@ -4543,7 +4571,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te if (count > 0) { detachViewsFromParent(start, count); + mRecycler.removeSkippedScrap(); } + offsetChildrenTopAndBottom(incrementalDeltaY); if (down) { @@ -4853,6 +4883,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te confirmCheckedPositionsById(); } + // TODO: In the future we can recycle these views based on stable ID instead. + mRecycler.clearTransientStateViews(); + if (count > 0) { int newPos; int selectablePos; @@ -5357,6 +5390,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } @Override + protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + return new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT, 0); + } + + @Override protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { return new LayoutParams(p); } @@ -5735,6 +5774,11 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te */ int scrappedFromPosition; + /** + * The ID the view represents + */ + long itemId = -1; + public LayoutParams(Context c, AttributeSet attrs) { super(c, attrs); } @@ -5807,6 +5851,10 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te private ArrayList<View> mCurrentScrap; + private ArrayList<View> mSkippedScrap; + + private SparseArray<View> mTransientStateViews; + public void setViewTypeCount(int viewTypeCount) { if (viewTypeCount < 1) { throw new IllegalArgumentException("Can't have a viewTypeCount < 1"); @@ -5838,6 +5886,12 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } + if (mTransientStateViews != null) { + final int count = mTransientStateViews.size(); + for (int i = 0; i < count; i++) { + mTransientStateViews.valueAt(i).forceLayout(); + } + } } public boolean shouldRecycleViewType(int viewType) { @@ -5864,6 +5918,9 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } } } + if (mTransientStateViews != null) { + mTransientStateViews.clear(); + } } /** @@ -5910,6 +5967,28 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return null; } + View getTransientStateView(int position) { + if (mTransientStateViews == null) { + return null; + } + final int index = mTransientStateViews.indexOfKey(position); + if (index < 0) { + return null; + } + final View result = mTransientStateViews.valueAt(index); + mTransientStateViews.removeAt(index); + return result; + } + + /** + * Dump any currently saved views with transient state. + */ + void clearTransientStateViews() { + if (mTransientStateViews != null) { + mTransientStateViews.clear(); + } + } + /** * @return A view from the ScrapViews collection. These are unordered. */ @@ -5926,7 +6005,7 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** - * Put a view into the ScapViews list. These views are unordered. + * Put a view into the ScrapViews list. These views are unordered. * * @param scrap The view to add */ @@ -5936,23 +6015,32 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te return; } + lp.scrappedFromPosition = position; + // Don't put header or footer views or views that should be ignored // into the scrap heap int viewType = lp.viewType; - if (!shouldRecycleViewType(viewType)) { - if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { - removeDetachedView(scrap, false); + final boolean scrapHasTransientState = scrap.hasTransientState(); + if (!shouldRecycleViewType(viewType) || scrapHasTransientState) { + if (viewType != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || scrapHasTransientState) { + if (mSkippedScrap == null) { + mSkippedScrap = new ArrayList<View>(); + } + mSkippedScrap.add(scrap); + } + if (scrapHasTransientState) { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArray<View>(); + } + mTransientStateViews.put(position, scrap); } return; } - lp.scrappedFromPosition = position; - + scrap.dispatchStartTemporaryDetach(); if (mViewTypeCount == 1) { - scrap.dispatchStartTemporaryDetach(); mCurrentScrap.add(scrap); } else { - scrap.dispatchStartTemporaryDetach(); mScrapViews[viewType].add(scrap); } @@ -5962,6 +6050,20 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te } /** + * Finish the removal of any views that skipped the scrap heap. + */ + void removeSkippedScrap() { + if (mSkippedScrap == null) { + return; + } + final int count = mSkippedScrap.size(); + for (int i = 0; i < count; i++) { + removeDetachedView(mSkippedScrap.get(i), false); + } + mSkippedScrap.clear(); + } + + /** * Move all views remaining in mActiveViews to mScrapViews. */ void scrapActiveViews() { @@ -5980,11 +6082,19 @@ public abstract class AbsListView extends AdapterView<ListAdapter> implements Te activeViews[i] = null; - if (!shouldRecycleViewType(whichScrap)) { + final boolean scrapHasTransientState = victim.hasTransientState(); + if (!shouldRecycleViewType(whichScrap) || scrapHasTransientState) { // Do not move views that should be ignored - if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { + if (whichScrap != ITEM_VIEW_TYPE_HEADER_OR_FOOTER || + scrapHasTransientState) { removeDetachedView(victim, false); } + if (scrapHasTransientState) { + if (mTransientStateViews == null) { + mTransientStateViews = new SparseArray<View>(); + } + mTransientStateViews.put(mFirstActivePosition + i, victim); + } continue; } diff --git a/core/java/android/widget/CheckedTextView.java b/core/java/android/widget/CheckedTextView.java index 5c7e5a36c31c..dd5332586b3d 100644 --- a/core/java/android/widget/CheckedTextView.java +++ b/core/java/android/widget/CheckedTextView.java @@ -142,12 +142,8 @@ public class CheckedTextView extends TextView implements Checkable { resolvePadding(); } - /** - * @hide - */ @Override - protected void resolvePadding() { - super.resolvePadding(); + public void onResolvePadding(int layoutDirection) { int newPadding = (mCheckMarkDrawable != null) ? mCheckMarkWidth + mBasePadding : mBasePadding; mNeedRequestlayout |= (mPaddingRight != newPadding); diff --git a/core/java/android/widget/EdgeEffect.java b/core/java/android/widget/EdgeEffect.java index 326587e6e7cb..83aa8ba3a908 100644 --- a/core/java/android/widget/EdgeEffect.java +++ b/core/java/android/widget/EdgeEffect.java @@ -56,7 +56,7 @@ public class EdgeEffect { // Time it will take in ms for a pulled glow to decay to partial strength before release private static final int PULL_DECAY_TIME = 1000; - private static final float MAX_ALPHA = 0.8f; + private static final float MAX_ALPHA = 1.f; private static final float HELD_EDGE_ALPHA = 0.7f; private static final float HELD_EDGE_SCALE_Y = 0.5f; private static final float HELD_GLOW_ALPHA = 0.5f; diff --git a/core/java/android/widget/GridLayout.java b/core/java/android/widget/GridLayout.java index ea40dc24f1b7..fc08cc5bfeab 100644 --- a/core/java/android/widget/GridLayout.java +++ b/core/java/android/widget/GridLayout.java @@ -127,8 +127,7 @@ import static java.lang.Math.min; * * GridLayout does not provide support for the principle of <em>weight</em>, as defined in * {@link LinearLayout.LayoutParams#weight}. In general, it is not therefore possible - * to configure a GridLayout to distribute excess space in non-trivial proportions between - * multiple rows or columns. + * to configure a GridLayout to distribute excess space between multiple components. * <p> * Some common use-cases may nevertheless be accommodated as follows. * To place equal amounts of space around a component in a cell group; @@ -209,7 +208,6 @@ public class GridLayout extends ViewGroup { static final String TAG = GridLayout.class.getName(); static final boolean DEBUG = false; - static final int PRF = 1; static final int MAX_SIZE = 100000; static final int DEFAULT_CONTAINER_MARGIN = 0; static final int UNINITIALIZED_HASH = 0; @@ -779,7 +777,7 @@ public class GridLayout extends ViewGroup { } } - private void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) { + private static void drawRect(Canvas canvas, int x1, int y1, int x2, int y2, Paint paint) { canvas.drawRect(x1, y1, x2 - 1, y2 - 1, paint); } @@ -957,10 +955,6 @@ public class GridLayout extends ViewGroup { resolveSizeAndState(measuredHeight, heightSpec, 0)); } - private int protect(int alignment) { - return (alignment == UNDEFINED) ? 0 : alignment; - } - private int getMeasurement(View c, boolean horizontal) { return horizontal ? c.getMeasuredWidth() : c.getMeasuredHeight(); } @@ -1040,42 +1034,31 @@ public class GridLayout extends ViewGroup { Alignment hAlign = getAlignment(columnSpec.alignment, true); Alignment vAlign = getAlignment(rowSpec.alignment, false); - Bounds colBounds = horizontalAxis.getGroupBounds().getValue(i); - Bounds rowBounds = verticalAxis.getGroupBounds().getValue(i); + Bounds boundsX = horizontalAxis.getGroupBounds().getValue(i); + Bounds boundsY = verticalAxis.getGroupBounds().getValue(i); // Gravity offsets: the location of the alignment group relative to its cell group. - //noinspection NullableProblems - int gravityOffsetX = protect(hAlign.getAlignmentValue(null, - cellWidth - colBounds.size(true))); - //noinspection NullableProblems - int gravityOffsetY = protect(vAlign.getAlignmentValue(null, - cellHeight - rowBounds.size(true))); - - boolean rtl = isLayoutRtl(); - int startMargin = getMargin(c, true, !rtl); + int gravityOffsetX = hAlign.getGravityOffset(c, cellWidth - boundsX.size(true)); + int gravityOffsetY = vAlign.getGravityOffset(c, cellHeight - boundsY.size(true)); + + int leftMargin = getMargin(c, true, true); int topMargin = getMargin(c, false, true); - int endMargin = getMargin(c, true, rtl); + int rightMargin = getMargin(c, true, false); int bottomMargin = getMargin(c, false, false); - // Same calculation as getMeasurementIncludingMargin() - int mWidth = startMargin + pWidth + endMargin; - int mHeight = topMargin + pHeight + bottomMargin; - // Alignment offsets: the location of the view relative to its alignment group. - int alignmentOffsetX = colBounds.getOffset(c, hAlign, mWidth); - int alignmentOffsetY = rowBounds.getOffset(c, vAlign, mHeight); + int alignmentOffsetX = boundsX.getOffset(c, hAlign, leftMargin + pWidth + rightMargin); + int alignmentOffsetY = boundsY.getOffset(c, vAlign, topMargin + pHeight + bottomMargin); - int dx = gravityOffsetX + alignmentOffsetX + startMargin; - int dy = gravityOffsetY + alignmentOffsetY + topMargin; + int width = hAlign.getSizeInCell(c, pWidth, cellWidth - leftMargin - rightMargin); + int height = vAlign.getSizeInCell(c, pHeight, cellHeight - topMargin - bottomMargin); - cellWidth -= startMargin + endMargin; - cellHeight -= topMargin + bottomMargin; + int dx = x1 + gravityOffsetX + alignmentOffsetX; - int width = hAlign.getSizeInCell(c, pWidth, cellWidth, PRF); - int height = vAlign.getSizeInCell(c, pHeight, cellHeight, PRF); + int cx = !isLayoutRtl() ? paddingLeft + leftMargin + dx : + targetWidth - width - paddingRight - rightMargin - dx; + int cy = paddingTop + y1 + gravityOffsetY + alignmentOffsetY + topMargin; - int cx = rtl ? targetWidth - paddingRight - x1 - dx - width : paddingLeft + x1 + dx; - int cy = paddingTop + y1 + dy; if (width != c.getMeasuredWidth() || height != c.getMeasuredHeight()) { c.measure(makeMeasureSpec(width, EXACTLY), makeMeasureSpec(height, EXACTLY)); } @@ -1694,7 +1677,7 @@ public class GridLayout extends ViewGroup { * each cell group. The fundamental parameters associated with each cell group are * gathered into their vertical and horizontal components and stored * in the {@link #rowSpec} and {@link #columnSpec} layout parameters. - * {@link android.widget.GridLayout.Spec Specs} are immutable structures + * {@link GridLayout.Spec Specs} are immutable structures * and may be shared between the layout parameters of different children. * <p> * The row and column specs contain the leading and trailing indices along each axis @@ -1747,7 +1730,7 @@ public class GridLayout extends ViewGroup { * <li>{@link #rowSpec}<code>.alignment</code> = {@link #BASELINE} </li> * <li>{@link #columnSpec}<code>.column</code> = {@link #UNDEFINED} </li> * <li>{@link #columnSpec}<code>.columnSpan</code> = 1 </li> - * <li>{@link #columnSpec}<code>.alignment</code> = {@link #LEFT} </li> + * <li>{@link #columnSpec}<code>.alignment</code> = {@link #START} </li> * </ul> * * See {@link GridLayout} for a more complete description of the conventions @@ -1936,7 +1919,7 @@ public class GridLayout extends ViewGroup { /** * Describes how the child views are positioned. Default is {@code LEFT | BASELINE}. - * See {@link android.view.Gravity}. + * See {@link Gravity}. * * @param gravity the new gravity value * @@ -2426,8 +2409,8 @@ public class GridLayout extends ViewGroup { * group is specified by the two alignments which act along each axis independently. * <p> * The GridLayout class defines the most common alignments used in general layout: - * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #CENTER}, {@link - * #BASELINE} and {@link #FILL}. + * {@link #TOP}, {@link #LEFT}, {@link #BOTTOM}, {@link #RIGHT}, {@link #START}, + * {@link #END}, {@link #CENTER}, {@link #BASELINE} and {@link #FILL}. */ /* * An Alignment implementation must define {@link #getAlignmentValue(View, int, int)}, @@ -2441,6 +2424,8 @@ public class GridLayout extends ViewGroup { Alignment() { } + abstract int getGravityOffset(View view, int cellDelta); + /** * Returns an alignment value. In the case of vertical alignments the value * returned should indicate the distance from the top of the view to the @@ -2463,12 +2448,9 @@ public class GridLayout extends ViewGroup { * @param view the view to which this alignment should be applied * @param viewSize the measured size of the view * @param cellSize the size of the cell into which this view will be placed - * @param measurementType This parameter is currently unused as GridLayout only supports - * one type of measurement: {@link View#measure(int, int)}. - * * @return the aligned size */ - int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) { + int getSizeInCell(View view, int viewSize, int cellSize) { return viewSize; } @@ -2479,6 +2461,11 @@ public class GridLayout extends ViewGroup { static final Alignment UNDEFINED_ALIGNMENT = new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return UNDEFINED; + } + + @Override public int getAlignmentValue(View view, int viewSize) { return UNDEFINED; } @@ -2490,6 +2477,11 @@ public class GridLayout extends ViewGroup { */ private static final Alignment LEADING = new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return 0; + } + + @Override public int getAlignmentValue(View view, int viewSize) { return 0; } @@ -2501,6 +2493,11 @@ public class GridLayout extends ViewGroup { */ private static final Alignment TRAILING = new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return cellDelta; + } + + @Override public int getAlignmentValue(View view, int viewSize) { return viewSize; } @@ -2530,15 +2527,16 @@ public class GridLayout extends ViewGroup { */ public static final Alignment END = TRAILING; - private static Alignment getAbsoluteAlignment(final Alignment a1, final Alignment a2) { + private static Alignment createSwitchingAlignment(final Alignment ltr, final Alignment rtl) { return new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return (!view.isLayoutRtl() ? ltr : rtl).getGravityOffset(view, cellDelta); + } + + @Override public int getAlignmentValue(View view, int viewSize) { - if (view == null) { - return UNDEFINED; - } - Alignment alignment = view.isLayoutRtl() ? a2 : a1; - return alignment.getAlignmentValue(view, viewSize); + return (!view.isLayoutRtl() ? ltr : rtl).getAlignmentValue(view, viewSize); } }; } @@ -2547,13 +2545,13 @@ public class GridLayout extends ViewGroup { * Indicates that a view should be aligned with the <em>left</em> * edges of the other views in its cell group. */ - public static final Alignment LEFT = getAbsoluteAlignment(START, END); + public static final Alignment LEFT = createSwitchingAlignment(START, END); /** * Indicates that a view should be aligned with the <em>right</em> * edges of the other views in its cell group. */ - public static final Alignment RIGHT = getAbsoluteAlignment(END, START); + public static final Alignment RIGHT = createSwitchingAlignment(END, START); /** * Indicates that a view should be <em>centered</em> with the other views in its cell group. @@ -2562,6 +2560,11 @@ public class GridLayout extends ViewGroup { */ public static final Alignment CENTER = new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return cellDelta >> 1; + } + + @Override public int getAlignmentValue(View view, int viewSize) { return viewSize >> 1; } @@ -2576,10 +2579,12 @@ public class GridLayout extends ViewGroup { */ public static final Alignment BASELINE = new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return 0; // baseline gravity is top + } + + @Override public int getAlignmentValue(View view, int viewSize) { - if (view == null) { - return UNDEFINED; - } int baseline = view.getBaseline(); return (baseline == -1) ? UNDEFINED : baseline; } @@ -2627,12 +2632,17 @@ public class GridLayout extends ViewGroup { */ public static final Alignment FILL = new Alignment() { @Override + int getGravityOffset(View view, int cellDelta) { + return 0; + } + + @Override public int getAlignmentValue(View view, int viewSize) { return UNDEFINED; } @Override - public int getSizeInCell(View view, int viewSize, int cellSize, int measurementType) { + public int getSizeInCell(View view, int viewSize, int cellSize) { return cellSize; } }; diff --git a/core/java/android/widget/GridView.java b/core/java/android/widget/GridView.java index be2df8ef4607..0dedf8b61d32 100644 --- a/core/java/android/widget/GridView.java +++ b/core/java/android/widget/GridView.java @@ -1029,10 +1029,9 @@ public class GridView extends AbsListView { if (count > 0) { final View child = obtainView(0, mIsScrap); - AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); + AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams(); if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); + p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); child.setLayoutParams(p); } p.viewType = mAdapter.getItemViewType(0); @@ -1205,6 +1204,7 @@ public class GridView extends AbsListView { // Clear out old views //removeAllViewsInLayout(); detachAllViewsFromParent(); + recycleBin.removeSkippedScrap(); switch (mLayoutMode) { case LAYOUT_SET_SELECTION: @@ -1361,10 +1361,9 @@ public class GridView extends AbsListView { // Respect layout params that are already in the view. Otherwise make // some up... - AbsListView.LayoutParams p = (AbsListView.LayoutParams)child.getLayoutParams(); + AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams(); if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); + p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); } p.viewType = mAdapter.getItemViewType(position); diff --git a/core/java/android/widget/ListView.java b/core/java/android/widget/ListView.java index 67fd059f1955..71700b367b40 100644 --- a/core/java/android/widget/ListView.java +++ b/core/java/android/widget/ListView.java @@ -1163,8 +1163,7 @@ public class ListView extends AbsListView { private void measureScrapChild(View child, int position, int widthMeasureSpec) { LayoutParams p = (LayoutParams) child.getLayoutParams(); if (p == null) { - p = new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); + p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); child.setLayoutParams(p); } p.viewType = mAdapter.getItemViewType(position); @@ -1591,6 +1590,7 @@ public class ListView extends AbsListView { // Clear out old views detachAllViewsFromParent(); + recycleBin.removeSkippedScrap(); switch (mLayoutMode) { case LAYOUT_SET_SELECTION: @@ -1807,8 +1807,7 @@ public class ListView extends AbsListView { // noinspection unchecked AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams(); if (p == null) { - p = new AbsListView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT, 0); + p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); } p.viewType = mAdapter.getItemViewType(position); diff --git a/core/java/android/widget/SpellChecker.java b/core/java/android/widget/SpellChecker.java index 909f383f1171..a1cf205378a1 100644 --- a/core/java/android/widget/SpellChecker.java +++ b/core/java/android/widget/SpellChecker.java @@ -24,6 +24,7 @@ import android.text.Spanned; import android.text.method.WordIterator; import android.text.style.SpellCheckSpan; import android.text.style.SuggestionSpan; +import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SpellCheckerSession; import android.view.textservice.SpellCheckerSession.SpellCheckerSessionListener; import android.view.textservice.SuggestionsInfo; @@ -277,9 +278,9 @@ public class SpellChecker implements SpellCheckerSessionListener { } @Override - public void onGetSuggestionsForSentence(SuggestionsInfo[] results) { + public void onGetSentenceSuggestions(SentenceSuggestionsInfo[] results) { // TODO: Handle the position and length for each suggestion - onGetSuggestions(results); + // do nothing for now } @Override diff --git a/core/java/android/widget/TableLayout.java b/core/java/android/widget/TableLayout.java index f5d374656d89..b870ceeac4de 100644 --- a/core/java/android/widget/TableLayout.java +++ b/core/java/android/widget/TableLayout.java @@ -735,8 +735,7 @@ public class TableLayout extends LinearLayout { * @param heightAttr the height attribute to fetch */ @Override - protected void setBaseAttributes(TypedArray a, - int widthAttr, int heightAttr) { + protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) { this.width = MATCH_PARENT; if (a.hasValue(heightAttr)) { this.height = a.getLayoutDimension(heightAttr, "layout_height"); diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index 13798ef1da38..a4087d518514 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -232,25 +232,72 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener static final String LOG_TAG = "TextView"; static final boolean DEBUG_EXTRACT = false; - private static final int PRIORITY = 100; - private int mCurrentAlpha = 255; + // Enum for the "typeface" XML parameter. + // TODO: How can we get this from the XML instead of hardcoding it here? + private static final int SANS = 1; + private static final int SERIF = 2; + private static final int MONOSPACE = 3; - final int[] mTempCoords = new int[2]; - Rect mTempRect; + // Bitfield for the "numeric" XML parameter. + // TODO: How can we get this from the XML instead of hardcoding it here? + private static final int SIGNED = 2; + private static final int DECIMAL = 4; + + private static enum TEXT_ALIGN { + INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END; + } + + /** + * Draw marquee text with fading edges as usual + */ + private static final int MARQUEE_FADE_NORMAL = 0; + + /** + * Draw marquee text as ellipsize end while inactive instead of with the fade. + * (Useful for devices where the fade can be expensive if overdone) + */ + private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1; + + /** + * Draw marquee text with fading edges because it is currently active/animating. + */ + private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2; + + private static final int LINES = 1; + private static final int EMS = LINES; + private static final int PIXELS = 2; + + private static final RectF TEMP_RECTF = new RectF(); + private static final float[] TEMP_POSITION = new float[2]; + + // XXX should be much larger + private static final int VERY_WIDE = 1024*1024; + private static final int BLINK = 500; + private static final int ANIMATED_SCROLL_GAP = 250; + + private static final InputFilter[] NO_FILTERS = new InputFilter[0]; + private static final Spanned EMPTY_SPANNED = new SpannedString(""); + + private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20; + private static final int CHANGE_WATCHER_PRIORITY = 100; + + // New state used to change background based on whether this TextView is multiline. + private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline }; + + // System wide time for last cut or copy action. + private static long LAST_CUT_OR_COPY_TIME; + + private int mCurrentAlpha = 255; private ColorStateList mTextColor; - private int mCurTextColor; private ColorStateList mHintTextColor; private ColorStateList mLinkTextColor; + private int mCurTextColor; private int mCurHintTextColor; private boolean mFreezesText; - private boolean mFrozenWithFocus; private boolean mTemporaryDetach; private boolean mDispatchTemporaryDetach; - private boolean mDiscardNextActionUp = false; - private boolean mIgnoreActionUpEvent = false; - private Editable.Factory mEditableFactory = Editable.Factory.getInstance(); private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); @@ -258,18 +305,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean mPreDrawRegistered; - private TextUtils.TruncateAt mEllipsize = null; - - // Enum for the "typeface" XML parameter. - // TODO: How can we get this from the XML instead of hardcoding it here? - private static final int SANS = 1; - private static final int SERIF = 2; - private static final int MONOSPACE = 3; - - // Bitfield for the "numeric" XML parameter. - // TODO: How can we get this from the XML instead of hardcoding it here? - private static final int SIGNED = 2; - private static final int DECIMAL = 4; + private TextUtils.TruncateAt mEllipsize; static class Drawables { final Rect mCompoundRect = new Rect(); @@ -283,96 +319,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private Drawables mDrawables; - private DisplayList mTextDisplayList; - private boolean mTextDisplayListIsValid; - - private CharSequence mError; - private boolean mErrorWasChanged; - private ErrorPopup mPopup; - /** - * This flag is set if the TextView tries to display an error before it - * is attached to the window (so its position is still unknown). - * It causes the error to be shown later, when onAttachedToWindow() - * is called. - */ - private boolean mShowErrorAfterAttach; - - private CharWrapper mCharWrapper = null; - - private boolean mSelectionMoved = false; - private boolean mTouchFocusSelected = false; + private CharWrapper mCharWrapper; private Marquee mMarquee; private boolean mRestartMarquee; private int mMarqueeRepeatLimit = 3; - static class InputContentType { - int imeOptions = EditorInfo.IME_NULL; - String privateImeOptions; - CharSequence imeActionLabel; - int imeActionId; - Bundle extras; - OnEditorActionListener onEditorActionListener; - boolean enterDown; - } - InputContentType mInputContentType; - - static class InputMethodState { - Rect mCursorRectInWindow = new Rect(); - RectF mTmpRectF = new RectF(); - float[] mTmpOffset = new float[2]; - ExtractedTextRequest mExtracting; - final ExtractedText mTmpExtracted = new ExtractedText(); - int mBatchEditNesting; - boolean mCursorChanged; - boolean mSelectionModeChanged; - boolean mContentChanged; - int mChangedStart, mChangedEnd, mChangedDelta; - } - InputMethodState mInputMethodState; - - private int mTextSelectHandleLeftRes; - private int mTextSelectHandleRightRes; - private int mTextSelectHandleRes; - - private int mTextEditSuggestionItemLayout; - private SuggestionsPopupWindow mSuggestionsPopupWindow; - private SuggestionRangeSpan mSuggestionRangeSpan; - private Runnable mShowSuggestionRunnable; - - private int mCursorDrawableRes; - private final Drawable[] mCursorDrawable = new Drawable[2]; - private int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split) - - private Drawable mSelectHandleLeft; - private Drawable mSelectHandleRight; - private Drawable mSelectHandleCenter; - - // Global listener that detects changes in the global position of the TextView - private PositionListener mPositionListener; - - private float mLastDownPositionX, mLastDownPositionY; - private Callback mCustomSelectionActionModeCallback; - - // Set when this TextView gained focus with some text selected. Will start selection mode. - private boolean mCreatedWithASelection = false; - - private WordIterator mWordIterator; - - private SpellChecker mSpellChecker; - // The alignment to pass to Layout, or null if not resolved. private Layout.Alignment mLayoutAlignment; // The default value for mTextAlign. - private TextAlign mTextAlign = TextAlign.INHERIT; - - private static enum TextAlign { - INHERIT, GRAVITY, TEXT_START, TEXT_END, CENTER, VIEW_START, VIEW_END; - } + private TEXT_ALIGN mTextAlign = TEXT_ALIGN.INHERIT; - private boolean mResolvedDrawables = false; + private boolean mResolvedDrawables; /** * On some devices the fading edges add a performance penalty if used @@ -387,21 +347,84 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ private Layout mSavedMarqueeModeLayout; - /** - * Draw marquee text with fading edges as usual - */ - private static final int MARQUEE_FADE_NORMAL = 0; + @ViewDebug.ExportedProperty(category = "text") + private CharSequence mText; + private CharSequence mTransformed; + private BufferType mBufferType = BufferType.NORMAL; - /** - * Draw marquee text as ellipsize end while inactive instead of with the fade. - * (Useful for devices where the fade can be expensive if overdone) - */ - private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1; + private CharSequence mHint; + private Layout mHintLayout; + + private MovementMethod mMovement; + + private TransformationMethod mTransformation; + private boolean mAllowTransformationLengthChange; + private ChangeWatcher mChangeWatcher; + + private ArrayList<TextWatcher> mListeners; + + // display attributes + private final TextPaint mTextPaint; + private boolean mUserSetTextScaleX; + private Layout mLayout; + + private int mGravity = Gravity.TOP | Gravity.START; + private boolean mHorizontallyScrolling; + + private int mAutoLinkMask; + private boolean mLinksClickable = true; + + private float mSpacingMult = 1.0f; + private float mSpacingAdd = 0.0f; + + private int mMaximum = Integer.MAX_VALUE; + private int mMaxMode = LINES; + private int mMinimum = 0; + private int mMinMode = LINES; + + private int mOldMaximum = mMaximum; + private int mOldMaxMode = mMaxMode; + + private int mMaxWidth = Integer.MAX_VALUE; + private int mMaxWidthMode = PIXELS; + private int mMinWidth = 0; + private int mMinWidthMode = PIXELS; + + private boolean mSingleLine; + private int mDesiredHeightAtMeasure = -1; + private boolean mIncludePad = true; + + // tmp primitives, so we don't alloc them on each draw + private Rect mTempRect; + private long mLastScroll; + private Scroller mScroller; + + private BoringLayout.Metrics mBoring, mHintBoring; + private BoringLayout mSavedLayout, mSavedHintLayout; + + private TextDirectionHeuristic mTextDir; + + private InputFilter[] mFilters = NO_FILTERS; + + // Although these fields are specific to editable text, they are not added to Editor because + // they are defined by the TextView's style and are theme-dependent. + private int mHighlightColor = 0x6633B5E5; + private int mCursorDrawableRes; + // These four fields, could be moved to Editor, since we know their default values and we + // could condition the creation of the Editor to a non standard value. This is however + // brittle since the hardcoded values here (such as + // com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the + // default style is modified. + private int mTextSelectHandleLeftRes; + private int mTextSelectHandleRightRes; + private int mTextSelectHandleRes; + private int mTextEditSuggestionItemLayout; /** - * Draw marquee text with fading edges because it is currently active/animating. + * EditText specific data, created on demand when one of the Editor fields is used. + * See {@link #createEditorIfNeeded(String)}. */ - private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2; + private Editor mEditor; /* * Kick-start the font cache for the zygote process (to pay the cost of @@ -454,14 +477,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(compat.applicationScale); - // If we get the paint from the skin, we should set it to left, since - // the layout always wants it to be left. - // mTextPaint.setTextAlign(Paint.Align.LEFT); - - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - mHighlightPaint.setCompatibilityScaling(compat.applicationScale); - mMovement = getDefaultMovementMethod(); + mTransformation = null; int textColorHighlight = 0; @@ -608,12 +625,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mLinksClickable = a.getBoolean(attr, true); break; -// TODO uncomment when this attribute is made public in the next release -// also add TextView_showSoftInputOnFocus to the list of attributes above -// case com.android.internal.R.styleable.TextView_showSoftInputOnFocus: -// setShowSoftInputOnFocus(a.getBoolean(attr, true)); -// break; - case com.android.internal.R.styleable.TextView_drawableLeft: drawableLeft = a.getDrawable(attr); break; @@ -805,30 +816,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_inputType: - inputType = a.getInt(attr, mInputType); + inputType = a.getInt(attr, EditorInfo.TYPE_NULL); break; case com.android.internal.R.styleable.TextView_imeOptions: - if (mInputContentType == null) { - mInputContentType = new InputContentType(); + createEditorIfNeeded("IME options specified in constructor"); + if (getEditor().mInputContentType == null) { + getEditor().mInputContentType = new InputContentType(); } - mInputContentType.imeOptions = a.getInt(attr, - mInputContentType.imeOptions); + getEditor().mInputContentType.imeOptions = a.getInt(attr, + getEditor().mInputContentType.imeOptions); break; case com.android.internal.R.styleable.TextView_imeActionLabel: - if (mInputContentType == null) { - mInputContentType = new InputContentType(); + createEditorIfNeeded("IME action label specified in constructor"); + if (getEditor().mInputContentType == null) { + getEditor().mInputContentType = new InputContentType(); } - mInputContentType.imeActionLabel = a.getText(attr); + getEditor().mInputContentType.imeActionLabel = a.getText(attr); break; case com.android.internal.R.styleable.TextView_imeActionId: - if (mInputContentType == null) { - mInputContentType = new InputContentType(); + createEditorIfNeeded("IME action id specified in constructor"); + if (getEditor().mInputContentType == null) { + getEditor().mInputContentType = new InputContentType(); } - mInputContentType.imeActionId = a.getInt(attr, - mInputContentType.imeActionId); + getEditor().mInputContentType.imeActionId = a.getInt(attr, + getEditor().mInputContentType.imeActionId); break; case com.android.internal.R.styleable.TextView_privateImeOptions: @@ -866,7 +880,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; case com.android.internal.R.styleable.TextView_textIsSelectable: - mTextIsSelectable = a.getBoolean(attr, false); + setTextIsSelectable(a.getBoolean(attr, false)); break; case com.android.internal.R.styleable.TextView_textAllCaps: @@ -897,35 +911,39 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } try { - mInput = (KeyListener) c.newInstance(); + createEditorIfNeeded("inputMethod in ctor"); + getEditor().mKeyListener = (KeyListener) c.newInstance(); } catch (InstantiationException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } try { - mInputType = inputType != EditorInfo.TYPE_NULL + getEditor().mInputType = inputType != EditorInfo.TYPE_NULL ? inputType - : mInput.getInputType(); + : getEditor().mKeyListener.getInputType(); } catch (IncompatibleClassChangeError e) { - mInputType = EditorInfo.TYPE_CLASS_TEXT; + getEditor().mInputType = EditorInfo.TYPE_CLASS_TEXT; } } else if (digits != null) { - mInput = DigitsKeyListener.getInstance(digits.toString()); + createEditorIfNeeded("digits in ctor"); + getEditor().mKeyListener = DigitsKeyListener.getInstance(digits.toString()); // If no input type was specified, we will default to generic // text, since we can't tell the IME about the set of digits // that was selected. - mInputType = inputType != EditorInfo.TYPE_NULL + getEditor().mInputType = inputType != EditorInfo.TYPE_NULL ? inputType : EditorInfo.TYPE_CLASS_TEXT; } else if (inputType != EditorInfo.TYPE_NULL) { setInputType(inputType, true); // If set, the input type overrides what was set using the deprecated singleLine flag. singleLine = !isMultilineInputType(inputType); } else if (phone) { - mInput = DialerKeyListener.getInstance(); - mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE; + createEditorIfNeeded("dialer in ctor"); + getEditor().mKeyListener = DialerKeyListener.getInstance(); + getEditor().mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE; } else if (numeric != 0) { - mInput = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, + createEditorIfNeeded("numeric in ctor"); + getEditor().mKeyListener = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, (numeric & DECIMAL) != 0); inputType = EditorInfo.TYPE_CLASS_NUMBER; if ((numeric & SIGNED) != 0) { @@ -934,7 +952,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if ((numeric & DECIMAL) != 0) { inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL; } - mInputType = inputType; + getEditor().mInputType = inputType; } else if (autotext || autocap != -1) { TextKeyListener.Capitalize cap; @@ -961,22 +979,24 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; } - mInput = TextKeyListener.getInstance(autotext, cap); - mInputType = inputType; - } else if (mTextIsSelectable) { + createEditorIfNeeded("text input in ctor"); + getEditor().mKeyListener = TextKeyListener.getInstance(autotext, cap); + getEditor().mInputType = inputType; + } else if (isTextSelectable()) { // Prevent text changes from keyboard. - mInputType = EditorInfo.TYPE_NULL; - mInput = null; + if (mEditor != null) { + getEditor().mKeyListener = null; + getEditor().mInputType = EditorInfo.TYPE_NULL; + } bufferType = BufferType.SPANNABLE; - // Required to request focus while in touch mode. - setFocusableInTouchMode(true); // So that selection can be changed using arrow keys and touch is handled. setMovementMethod(ArrowKeyMovementMethod.getInstance()); } else if (editable) { - mInput = TextKeyListener.getInstance(); - mInputType = EditorInfo.TYPE_CLASS_TEXT; + createEditorIfNeeded("editable input in ctor"); + getEditor().mKeyListener = TextKeyListener.getInstance(); + getEditor().mInputType = EditorInfo.TYPE_CLASS_TEXT; } else { - mInput = null; + if (mEditor != null) getEditor().mKeyListener = null; switch (buffertype) { case 0: @@ -991,27 +1011,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - // mInputType has been set from inputType, possibly modified by mInputMethod. - // Specialize mInputType to [web]password if we have a text class and the original input - // type was a password. - if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { - if (password || passwordInputType) { - mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) - | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD; - } - if (webPasswordInputType) { - mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) - | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD; - } - } else if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_NUMBER) { - if (numberPasswordInputType) { - mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) - | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD; - } - } + if (mEditor != null) getEditor().adjustInputType(password, passwordInputType, webPasswordInputType, + numberPasswordInputType); if (selectallonfocus) { - mSelectAllOnFocus = true; + createEditorIfNeeded("selectallonfocus in constructor"); + getEditor().mSelectAllOnFocus = true; if (bufferType == BufferType.NORMAL) bufferType = BufferType.SPANNABLE; @@ -1027,7 +1032,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setInputTypeSingleLine(singleLine); applySingleLine(singleLine, singleLine, singleLine); - if (singleLine && mInput == null && ellipsize < 0) { + if (singleLine && getKeyListener() == null && ellipsize < 0) { ellipsize = 3; // END } @@ -1068,7 +1073,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (password || passwordInputType || webPasswordInputType || numberPasswordInputType) { setTransformationMethod(PasswordTransformationMethod.getInstance()); typefaceIndex = MONOSPACE; - } else if ((mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) + } else if (mEditor != null && (getEditor().mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) { typefaceIndex = MONOSPACE; } @@ -1097,7 +1102,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener com.android.internal.R.styleable.View, defStyle, 0); - boolean focusable = mMovement != null || mInput != null; + boolean focusable = mMovement != null || getKeyListener() != null; boolean clickable = focusable; boolean longClickable = focusable; @@ -1205,7 +1210,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (imm != null) imm.restartInput(this); } - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; prepareCursorControllers(); // start or stop the cursor blinking as appropriate @@ -1310,7 +1315,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * This will frequently be null for non-EditText TextViews. */ public final KeyListener getKeyListener() { - return mInput; + return mEditor == null ? null : getEditor().mKeyListener; } /** @@ -1340,16 +1345,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener fixFocusableAndClickableSettings(); if (input != null) { + createEditorIfNeeded("input is not null"); try { - mInputType = mInput.getInputType(); + getEditor().mInputType = getEditor().mKeyListener.getInputType(); } catch (IncompatibleClassChangeError e) { - mInputType = EditorInfo.TYPE_CLASS_TEXT; + getEditor().mInputType = EditorInfo.TYPE_CLASS_TEXT; } // Change inputType, without affecting transformation. // No need to applySingleLine since mSingleLine is unchanged. setInputTypeSingleLine(mSingleLine); } else { - mInputType = EditorInfo.TYPE_NULL; + if (mEditor != null) getEditor().mInputType = EditorInfo.TYPE_NULL; } InputMethodManager imm = InputMethodManager.peekInstance(); @@ -1357,11 +1363,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void setKeyListenerOnly(KeyListener input) { - mInput = input; - if (mInput != null && !(mText instanceof Editable)) - setText(mText); + if (mEditor == null && input == null) return; // null is the default value + + createEditorIfNeeded("setKeyListenerOnly"); + if (getEditor().mKeyListener != input) { + getEditor().mKeyListener = input; + if (input != null && !(mText instanceof Editable)) { + setText(mText); + } - setFilters((Editable) mText, mFilters); + setFilters((Editable) mText, mFilters); + } } /** @@ -1384,19 +1396,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * back the way you want it. */ public final void setMovementMethod(MovementMethod movement) { - mMovement = movement; + if (mMovement != movement) { + mMovement = movement; - if (mMovement != null && !(mText instanceof Spannable)) - setText(mText); + if (movement != null && !(mText instanceof Spannable)) { + setText(mText); + } - fixFocusableAndClickableSettings(); + fixFocusableAndClickableSettings(); - // SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement - prepareCursorControllers(); + // SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement + prepareCursorControllers(); + } } private void fixFocusableAndClickableSettings() { - if ((mMovement != null) || mInput != null) { + if (mMovement != null || (mEditor != null && getEditor().mKeyListener != null)) { setFocusable(true); setClickable(true); setLongClickable(true); @@ -1439,7 +1454,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (method instanceof TransformationMethod2) { TransformationMethod2 method2 = (TransformationMethod2) method; - mAllowTransformationLengthChange = !mTextIsSelectable && !(mText instanceof Editable); + mAllowTransformationLengthChange = !isTextSelectable() && !(mText instanceof Editable); method2.setLengthChangesAllowed(mAllowTransformationLengthChange); } else { mAllowTransformationLengthChange = false; @@ -2311,7 +2326,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void setHighlightColor(int color) { if (mHighlightColor != color) { mHighlightColor = color; - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; invalidate(); } } @@ -2332,7 +2347,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mShadowDx = dx; mShadowDy = dy; - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; invalidate(); } @@ -2824,7 +2839,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } if (inval) { - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; invalidate(); } } @@ -2862,73 +2877,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - /** - * User interface state that is stored by TextView for implementing - * {@link View#onSaveInstanceState}. - */ - public static class SavedState extends BaseSavedState { - int selStart; - int selEnd; - CharSequence text; - boolean frozenWithFocus; - CharSequence error; - - SavedState(Parcelable superState) { - super(superState); - } - - @Override - public void writeToParcel(Parcel out, int flags) { - super.writeToParcel(out, flags); - out.writeInt(selStart); - out.writeInt(selEnd); - out.writeInt(frozenWithFocus ? 1 : 0); - TextUtils.writeToParcel(text, out, flags); - - if (error == null) { - out.writeInt(0); - } else { - out.writeInt(1); - TextUtils.writeToParcel(error, out, flags); - } - } - - @Override - public String toString() { - String str = "TextView.SavedState{" - + Integer.toHexString(System.identityHashCode(this)) - + " start=" + selStart + " end=" + selEnd; - if (text != null) { - str += " text=" + text; - } - return str + "}"; - } - - @SuppressWarnings("hiding") - public static final Parcelable.Creator<SavedState> CREATOR - = new Parcelable.Creator<SavedState>() { - public SavedState createFromParcel(Parcel in) { - return new SavedState(in); - } - - public SavedState[] newArray(int size) { - return new SavedState[size]; - } - }; - - private SavedState(Parcel in) { - super(in); - selStart = in.readInt(); - selEnd = in.readInt(); - frozenWithFocus = (in.readInt() != 0); - text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - - if (in.readInt() != 0) { - error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); - } - } - } - @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); @@ -2968,8 +2916,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sp.removeSpan(cw); } - removeMisspelledSpans(sp); - sp.removeSpan(mSuggestionRangeSpan); + if (mEditor != null) { + removeMisspelledSpans(sp); + sp.removeSpan(getEditor().mSuggestionRangeSpan); + } ss.text = sp; } else { @@ -2980,7 +2930,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ss.frozenWithFocus = true; } - ss.error = mError; + ss.error = getError(); return ss; } @@ -3034,7 +2984,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener ss.selEnd); if (ss.frozenWithFocus) { - mFrozenWithFocus = true; + createEditorIfNeeded("restore instance with focus"); + getEditor().mFrozenWithFocus = true; } } } @@ -3192,7 +3143,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener needEditableForNotification = true; } - if (type == BufferType.EDITABLE || mInput != null || needEditableForNotification) { + if (type == BufferType.EDITABLE || getKeyListener() != null || needEditableForNotification) { + createEditorIfNeeded("setText with BufferType.EDITABLE or non null mInput"); Editable t = mEditableFactory.newEditable(text); text = t; setFilters(t, mFilters); @@ -3257,10 +3209,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mChangeWatcher = new ChangeWatcher(); sp.setSpan(mChangeWatcher, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE | - (PRIORITY << Spanned.SPAN_PRIORITY_SHIFT)); + (CHANGE_WATCHER_PRIORITY << Spanned.SPAN_PRIORITY_SHIFT)); - if (mInput != null) { - sp.setSpan(mInput, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE); + if (mEditor != null && getEditor().mKeyListener != null) { + sp.setSpan(getEditor().mKeyListener, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE); } if (mTransformation != null) { @@ -3275,7 +3227,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * selection, so reset mSelectionMoved to keep that from * interfering with the normal on-focus selection-setting. */ - mSelectionMoved = false; + if (mEditor != null) getEditor().mSelectionMoved = false; } } @@ -3329,100 +3281,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener setText(mCharWrapper, mBufferType, false, oldlen); } - private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations { - private char[] mChars; - private int mStart, mLength; - - public CharWrapper(char[] chars, int start, int len) { - mChars = chars; - mStart = start; - mLength = len; - } - - /* package */ void set(char[] chars, int start, int len) { - mChars = chars; - mStart = start; - mLength = len; - } - - public int length() { - return mLength; - } - - public char charAt(int off) { - return mChars[off + mStart]; - } - - @Override - public String toString() { - return new String(mChars, mStart, mLength); - } - - public CharSequence subSequence(int start, int end) { - if (start < 0 || end < 0 || start > mLength || end > mLength) { - throw new IndexOutOfBoundsException(start + ", " + end); - } - - return new String(mChars, start + mStart, end - start); - } - - public void getChars(int start, int end, char[] buf, int off) { - if (start < 0 || end < 0 || start > mLength || end > mLength) { - throw new IndexOutOfBoundsException(start + ", " + end); - } - - System.arraycopy(mChars, start + mStart, buf, off, end - start); - } - - public void drawText(Canvas c, int start, int end, - float x, float y, Paint p) { - c.drawText(mChars, start + mStart, end - start, x, y, p); - } - - public void drawTextRun(Canvas c, int start, int end, - int contextStart, int contextEnd, float x, float y, int flags, Paint p) { - int count = end - start; - int contextCount = contextEnd - contextStart; - c.drawTextRun(mChars, start + mStart, count, contextStart + mStart, - contextCount, x, y, flags, p); - } - - public float measureText(int start, int end, Paint p) { - return p.measureText(mChars, start + mStart, end - start); - } - - public int getTextWidths(int start, int end, float[] widths, Paint p) { - return p.getTextWidths(mChars, start + mStart, end - start, widths); - } - - public float getTextRunAdvances(int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex, - Paint p) { - int count = end - start; - int contextCount = contextEnd - contextStart; - return p.getTextRunAdvances(mChars, start + mStart, count, - contextStart + mStart, contextCount, flags, advances, - advancesIndex); - } - - public float getTextRunAdvances(int start, int end, int contextStart, - int contextEnd, int flags, float[] advances, int advancesIndex, - Paint p, int reserved) { - int count = end - start; - int contextCount = contextEnd - contextStart; - return p.getTextRunAdvances(mChars, start + mStart, count, - contextStart + mStart, contextCount, flags, advances, - advancesIndex, reserved); - } - - public int getTextRunCursor(int contextStart, int contextEnd, int flags, - int offset, int cursorOpt, Paint p) { - int contextCount = contextEnd - contextStart; - return p.getTextRunCursor(mChars, contextStart + mStart, - contextCount, flags, offset + mStart, cursorOpt); - } - } - /** * Like {@link #setText(CharSequence, android.widget.TextView.BufferType)}, * except that the cursor position (if any) is retained in the new text. @@ -3474,7 +3332,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Invalidate display list if hint will be used - if (mText.length() == 0 && mHint != null) mTextDisplayListIsValid = false; + if (mEditor != null && mText.length() == 0 && mHint != null) { + getEditor().mTextDisplayListIsValid = false; + } } /** @@ -3520,8 +3380,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_inputType */ public void setInputType(int type) { - final boolean wasPassword = isPasswordInputType(mInputType); - final boolean wasVisiblePassword = isVisiblePasswordInputType(mInputType); + final boolean wasPassword = isPasswordInputType(getInputType()); + final boolean wasVisiblePassword = isVisiblePasswordInputType(getInputType()); setInputType(type, false); final boolean isPassword = isPasswordInputType(type); final boolean isVisiblePassword = isVisiblePasswordInputType(type); @@ -3605,7 +3465,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_inputType */ public void setRawInputType(int type) { - mInputType = type; + if (type == InputType.TYPE_NULL && mEditor == null) return; //TYPE_NULL is the default value + createEditorIfNeeded("non null input type"); + getEditor().mInputType = type; } private void setInputType(int type, boolean direct) { @@ -3646,20 +3508,22 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener input = TextKeyListener.getInstance(); } setRawInputType(type); - if (direct) mInput = input; - else { + if (direct) { + createEditorIfNeeded("setInputType"); + getEditor().mKeyListener = input; + } else { setKeyListenerOnly(input); } } /** - * Get the type of the content. + * Get the type of the editable content. * * @see #setInputType(int) * @see android.text.InputType */ public int getInputType() { - return mInputType; + return mEditor == null ? EditorInfo.TYPE_NULL : getEditor().mInputType; } /** @@ -3671,10 +3535,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_imeOptions */ public void setImeOptions(int imeOptions) { - if (mInputContentType == null) { - mInputContentType = new InputContentType(); + createEditorIfNeeded("IME options specified"); + if (getEditor().mInputContentType == null) { + getEditor().mInputContentType = new InputContentType(); } - mInputContentType.imeOptions = imeOptions; + getEditor().mInputContentType.imeOptions = imeOptions; } /** @@ -3684,8 +3549,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see android.view.inputmethod.EditorInfo */ public int getImeOptions() { - return mInputContentType != null - ? mInputContentType.imeOptions : EditorInfo.IME_NULL; + return mEditor != null && getEditor().mInputContentType != null + ? getEditor().mInputContentType.imeOptions : EditorInfo.IME_NULL; } /** @@ -3699,11 +3564,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_imeActionId */ public void setImeActionLabel(CharSequence label, int actionId) { - if (mInputContentType == null) { - mInputContentType = new InputContentType(); + createEditorIfNeeded("IME action label specified"); + if (getEditor().mInputContentType == null) { + getEditor().mInputContentType = new InputContentType(); } - mInputContentType.imeActionLabel = label; - mInputContentType.imeActionId = actionId; + getEditor().mInputContentType.imeActionLabel = label; + getEditor().mInputContentType.imeActionId = actionId; } /** @@ -3713,8 +3579,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see android.view.inputmethod.EditorInfo */ public CharSequence getImeActionLabel() { - return mInputContentType != null - ? mInputContentType.imeActionLabel : null; + return mEditor != null && getEditor().mInputContentType != null + ? getEditor().mInputContentType.imeActionLabel : null; } /** @@ -3724,8 +3590,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see android.view.inputmethod.EditorInfo */ public int getImeActionId() { - return mInputContentType != null - ? mInputContentType.imeActionId : 0; + return mEditor != null && getEditor().mInputContentType != null + ? getEditor().mInputContentType.imeActionId : 0; } /** @@ -3737,12 +3603,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * modifier will, however, allow the user to insert a newline character. */ public void setOnEditorActionListener(OnEditorActionListener l) { - if (mInputContentType == null) { - mInputContentType = new InputContentType(); + createEditorIfNeeded("Editor action listener set"); + if (getEditor().mInputContentType == null) { + getEditor().mInputContentType = new InputContentType(); } - mInputContentType.onEditorActionListener = l; + getEditor().mInputContentType.onEditorActionListener = l; } - + /** * Called when an attached input method calls * {@link InputConnection#performEditorAction(int) @@ -3764,7 +3631,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see #setOnEditorActionListener */ public void onEditorAction(int actionCode) { - final InputContentType ict = mInputContentType; + final InputContentType ict = mEditor == null ? null : getEditor().mInputContentType; if (ict != null) { if (ict.onEditorActionListener != null) { if (ict.onEditorActionListener.onEditorAction(this, @@ -3807,21 +3674,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - Handler h = getHandler(); - if (h != null) { + ViewRootImpl viewRootImpl = getViewRootImpl(); + if (viewRootImpl != null) { long eventTime = SystemClock.uptimeMillis(); - h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, + viewRootImpl.dispatchKeyFromIme( new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE - | KeyEvent.FLAG_EDITOR_ACTION))); - h.sendMessage(h.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, + | KeyEvent.FLAG_EDITOR_ACTION)); + viewRootImpl.dispatchKeyFromIme( new KeyEvent(SystemClock.uptimeMillis(), eventTime, KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE - | KeyEvent.FLAG_EDITOR_ACTION))); + | KeyEvent.FLAG_EDITOR_ACTION)); } } @@ -3835,8 +3702,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_privateImeOptions */ public void setPrivateImeOptions(String type) { - if (mInputContentType == null) mInputContentType = new InputContentType(); - mInputContentType.privateImeOptions = type; + createEditorIfNeeded("Private IME option set"); + if (getEditor().mInputContentType == null) + getEditor().mInputContentType = new InputContentType(); + getEditor().mInputContentType.privateImeOptions = type; } /** @@ -3846,8 +3715,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see EditorInfo#privateImeOptions */ public String getPrivateImeOptions() { - return mInputContentType != null - ? mInputContentType.privateImeOptions : null; + return mEditor != null && getEditor().mInputContentType != null + ? getEditor().mInputContentType.privateImeOptions : null; } /** @@ -3861,12 +3730,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @see EditorInfo#extras * @attr ref android.R.styleable#TextView_editorExtras */ - public void setInputExtras(int xmlResId) - throws XmlPullParserException, IOException { + public void setInputExtras(int xmlResId) throws XmlPullParserException, IOException { + createEditorIfNeeded("Input extra set"); XmlResourceParser parser = getResources().getXml(xmlResId); - if (mInputContentType == null) mInputContentType = new InputContentType(); - mInputContentType.extras = new Bundle(); - getResources().parseBundleExtras(parser, mInputContentType.extras); + if (getEditor().mInputContentType == null) + getEditor().mInputContentType = new InputContentType(); + getEditor().mInputContentType.extras = new Bundle(); + getResources().parseBundleExtras(parser, getEditor().mInputContentType.extras); } /** @@ -3880,15 +3750,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_editorExtras */ public Bundle getInputExtras(boolean create) { - if (mInputContentType == null) { + if (mEditor == null && !create) return null; + createEditorIfNeeded("get Input extra"); + if (getEditor().mInputContentType == null) { if (!create) return null; - mInputContentType = new InputContentType(); + getEditor().mInputContentType = new InputContentType(); } - if (mInputContentType.extras == null) { + if (getEditor().mInputContentType.extras == null) { if (!create) return null; - mInputContentType.extras = new Bundle(); + getEditor().mInputContentType.extras = new Bundle(); } - return mInputContentType.extras; + return getEditor().mInputContentType.extras; } /** @@ -3897,7 +3769,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * or if it the error was cleared by the widget after user input. */ public CharSequence getError() { - return mError; + return mEditor == null ? null : getEditor().mError; } /** @@ -3931,10 +3803,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * be cleared (and you should provide a <code>null</code> icon as well). */ public void setError(CharSequence error, Drawable icon) { + createEditorIfNeeded("setError"); error = TextUtils.stringOrSpannedString(error); - mError = error; - mErrorWasChanged = true; + getEditor().mError = error; + getEditor().mErrorWasChanged = true; final Drawables dr = mDrawables; if (dr != null) { switch (getResolvedLayoutDirection()) { @@ -3953,12 +3826,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (error == null) { - if (mPopup != null) { - if (mPopup.isShowing()) { - mPopup.dismiss(); + if (getEditor().mErrorPopup != null) { + if (getEditor().mErrorPopup.isShowing()) { + getEditor().mErrorPopup.dismiss(); } - mPopup = null; + getEditor().mErrorPopup = null; } } else { if (isFocused()) { @@ -3969,83 +3842,29 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void showError() { if (getWindowToken() == null) { - mShowErrorAfterAttach = true; + getEditor().mShowErrorAfterAttach = true; return; } - if (mPopup == null) { + if (getEditor().mErrorPopup == null) { LayoutInflater inflater = LayoutInflater.from(getContext()); final TextView err = (TextView) inflater.inflate( com.android.internal.R.layout.textview_hint, null); final float scale = getResources().getDisplayMetrics().density; - mPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), (int) (50 * scale + 0.5f)); - mPopup.setFocusable(false); + getEditor().mErrorPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), (int) (50 * scale + 0.5f)); + getEditor().mErrorPopup.setFocusable(false); // The user is entering text, so the input method is needed. We // don't want the popup to be displayed on top of it. - mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); - } - - TextView tv = (TextView) mPopup.getContentView(); - chooseSize(mPopup, mError, tv); - tv.setText(mError); - - mPopup.showAsDropDown(this, getErrorX(), getErrorY()); - mPopup.fixDirection(mPopup.isAboveAnchor()); - } - - private static class ErrorPopup extends PopupWindow { - private boolean mAbove = false; - private final TextView mView; - private int mPopupInlineErrorBackgroundId = 0; - private int mPopupInlineErrorAboveBackgroundId = 0; - - ErrorPopup(TextView v, int width, int height) { - super(v, width, height); - mView = v; - // Make sure the TextView has a background set as it will be used the first time it is - // shown and positionned. Initialized with below background, which should have - // dimensions identical to the above version for this to work (and is more likely). - mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId, - com.android.internal.R.styleable.Theme_errorMessageBackground); - mView.setBackgroundResource(mPopupInlineErrorBackgroundId); - } - - void fixDirection(boolean above) { - mAbove = above; - - if (above) { - mPopupInlineErrorAboveBackgroundId = - getResourceId(mPopupInlineErrorAboveBackgroundId, - com.android.internal.R.styleable.Theme_errorMessageAboveBackground); - } else { - mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId, - com.android.internal.R.styleable.Theme_errorMessageBackground); - } - - mView.setBackgroundResource(above ? mPopupInlineErrorAboveBackgroundId : - mPopupInlineErrorBackgroundId); - } - - private int getResourceId(int currentId, int index) { - if (currentId == 0) { - TypedArray styledAttributes = mView.getContext().obtainStyledAttributes( - R.styleable.Theme); - currentId = styledAttributes.getResourceId(index, 0); - styledAttributes.recycle(); - } - return currentId; + getEditor().mErrorPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); } - @Override - public void update(int x, int y, int w, int h, boolean force) { - super.update(x, y, w, h, force); + TextView tv = (TextView) getEditor().mErrorPopup.getContentView(); + chooseSize(getEditor().mErrorPopup, getEditor().mError, tv); + tv.setText(getEditor().mError); - boolean above = isAboveAnchor(); - if (above != mAbove) { - fixDirection(above); - } - } + getEditor().mErrorPopup.showAsDropDown(this, getErrorX(), getErrorY()); + getEditor().mErrorPopup.fixDirection(getEditor().mErrorPopup.isAboveAnchor()); } /** @@ -4060,7 +3879,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final float scale = getResources().getDisplayMetrics().density; final Drawables dr = mDrawables; - return getWidth() - mPopup.getWidth() - getPaddingRight() - + return getWidth() - getEditor().mErrorPopup.getWidth() - getPaddingRight() - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f); } @@ -4090,13 +3909,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void hideError() { - if (mPopup != null) { - if (mPopup.isShowing()) { - mPopup.dismiss(); + if (getEditor().mErrorPopup != null) { + if (getEditor().mErrorPopup.isShowing()) { + getEditor().mErrorPopup.dismiss(); } } - mShowErrorAfterAttach = false; + getEditor().mShowErrorAfterAttach = false; } private void chooseSize(PopupWindow pop, CharSequence text, TextView tv) { @@ -4125,12 +3944,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected boolean setFrame(int l, int t, int r, int b) { boolean result = super.setFrame(l, t, r, b); - if (mPopup != null) { - TextView tv = (TextView) mPopup.getContentView(); - chooseSize(mPopup, mError, tv); - mPopup.update(this, getErrorX(), getErrorY(), - mPopup.getWidth(), mPopup.getHeight()); - } + if (mEditor != null) getEditor().setFrame(); restartMarqueeIfNeeded(); @@ -4146,7 +3960,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener /** * Sets the list of input filters that will be used if the buffer is - * Editable. Has no effect otherwise. + * Editable. Has no effect otherwise. * * @attr ref android.R.styleable#TextView_maxLength */ @@ -4167,11 +3981,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * and includes mInput in the list if it is an InputFilter. */ private void setFilters(Editable e, InputFilter[] filters) { - if (mInput instanceof InputFilter) { + if (mEditor != null && getEditor().mKeyListener instanceof InputFilter) { InputFilter[] nf = new InputFilter[filters.length + 1]; System.arraycopy(filters, 0, nf, 0, filters.length); - nf[filters.length] = (InputFilter) mInput; + nf[filters.length] = (InputFilter) getEditor().mKeyListener; e.setFilters(nf); } else { @@ -4251,14 +4065,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void invalidateCursorPath() { - if (mHighlightPathBogus) { + if (getEditor().mHighlightPathBogus) { invalidateCursor(); } else { final int horizontalPadding = getCompoundPaddingLeft(); final int verticalPadding = getExtendedPaddingTop() + getVerticalOffset(true); - if (mCursorCount == 0) { - synchronized (sTempRect) { + if (getEditor().mCursorCount == 0) { + synchronized (TEMP_RECTF) { /* * The reason for this concern about the thickness of the * cursor and doing the floor/ceil on the coordinates is that @@ -4275,16 +4089,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener thick /= 2.0f; - mHighlightPath.computeBounds(sTempRect, false); + getEditor().mHighlightPath.computeBounds(TEMP_RECTF, false); - invalidate((int) FloatMath.floor(horizontalPadding + sTempRect.left - thick), - (int) FloatMath.floor(verticalPadding + sTempRect.top - thick), - (int) FloatMath.ceil(horizontalPadding + sTempRect.right + thick), - (int) FloatMath.ceil(verticalPadding + sTempRect.bottom + thick)); + invalidate((int) FloatMath.floor(horizontalPadding + TEMP_RECTF.left - thick), + (int) FloatMath.floor(verticalPadding + TEMP_RECTF.top - thick), + (int) FloatMath.ceil(horizontalPadding + TEMP_RECTF.right + thick), + (int) FloatMath.ceil(verticalPadding + TEMP_RECTF.bottom + thick)); } } else { - for (int i = 0; i < mCursorCount; i++) { - Rect bounds = mCursorDrawable[i].getBounds(); + for (int i = 0; i < getEditor().mCursorCount; i++) { + Rect bounds = getEditor().mCursorDrawable[i].getBounds(); invalidate(bounds.left + horizontalPadding, bounds.top + verticalPadding, bounds.right + horizontalPadding, bounds.bottom + verticalPadding); } @@ -4338,8 +4152,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); if (invalidateCursor) { - for (int i = 0; i < mCursorCount; i++) { - Rect bounds = mCursorDrawable[i].getBounds(); + for (int i = 0; i < getEditor().mCursorCount; i++) { + Rect bounds = getEditor().mCursorDrawable[i].getBounds(); top = Math.min(top, bounds.top); bottom = Math.max(bottom, bounds.bottom); } @@ -4389,8 +4203,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ int curs = getSelectionEnd(); // Do not create the controller if it is not already created. - if (mSelectionModifierCursorController != null && - mSelectionModifierCursorController.isSelectionStartDragged()) { + if (mEditor != null && getEditor().mSelectionModifierCursorController != null && + getEditor().mSelectionModifierCursorController.isSelectionStartDragged()) { curs = getSelectionStart(); } @@ -4399,8 +4213,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * it already was before the text changed. I'm not sure * of a good way to tell from here if it was. */ - if (curs < 0 && - (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { + if (curs < 0 && (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { curs = mText.length(); } @@ -4414,9 +4227,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // This has to be checked here since: // - onFocusChanged cannot start it when focus is given to a view with selected text (after // a screen rotation) since layout is not yet initialized at that point. - if (mCreatedWithASelection) { + if (mEditor != null && getEditor().mCreatedWithASelection) { startSelectionActionMode(); - mCreatedWithASelection = false; + getEditor().mCreatedWithASelection = false; } // Phone specific code (there is no ExtractEditText on tablets). @@ -4438,25 +4251,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTemporaryDetach = false; - if (mShowErrorAfterAttach) { + if (mEditor != null && getEditor().mShowErrorAfterAttach) { showError(); - mShowErrorAfterAttach = false; - } - - final ViewTreeObserver observer = getViewTreeObserver(); - // No need to create the controller. - // The get method will add the listener on controller creation. - if (mInsertionPointCursorController != null) { - observer.addOnTouchModeChangeListener(mInsertionPointCursorController); - } - if (mSelectionModifierCursorController != null) { - observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); + getEditor().mShowErrorAfterAttach = false; } // Resolve drawables as the layout direction has been resolved resolveDrawables(); - updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */); + if (mEditor != null) getEditor().onAttachedToWindow(); } @Override @@ -4468,40 +4271,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mPreDrawRegistered = false; } - if (mError != null) { - hideError(); - } - - if (mBlink != null) { - mBlink.removeCallbacks(mBlink); - } - - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.onDetached(); - } - - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.onDetached(); - } - - if (mShowSuggestionRunnable != null) { - removeCallbacks(mShowSuggestionRunnable); - } - - hideControllers(); - resetResolvedDrawables(); - if (mTextDisplayList != null) { - mTextDisplayList.invalidate(); - } - - if (mSpellChecker != null) { - mSpellChecker.closeSession(); - // Forces the creation of a new SpellChecker next time this window is created. - // Will handle the cases where the settings has been changed in the meantime. - mSpellChecker = null; - } + if (mEditor != null) getEditor().onDetachedFromWindow(); } @Override @@ -4648,13 +4420,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (dr.mDrawableStart != null) dr.mDrawableStart.mutate().setAlpha(alpha); if (dr.mDrawableEnd != null) dr.mDrawableEnd.mutate().setAlpha(alpha); } - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; } return true; } if (mCurrentAlpha != 255) { - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; } mCurrentAlpha = 255; return false; @@ -4678,12 +4450,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @attr ref android.R.styleable#TextView_textIsSelectable */ public boolean isTextSelectable() { - return mTextIsSelectable; + return mEditor == null ? false : getEditor().mTextIsSelectable; } /** * Sets whether or not (default) the content of this view is selectable by the user. - * + * * Note that this methods affect the {@link #setFocusable(boolean)}, * {@link #setFocusableInTouchMode(boolean)} {@link #setClickable(boolean)} and * {@link #setLongClickable(boolean)} states and you may want to restore these if they were @@ -4694,16 +4466,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @param selectable Whether or not the content of this TextView should be selectable. */ public void setTextIsSelectable(boolean selectable) { - if (mTextIsSelectable == selectable) return; + if (!selectable && mEditor == null) return; // false is default value with no edit data - mTextIsSelectable = selectable; + createEditorIfNeeded("setTextIsSelectable"); + if (getEditor().mTextIsSelectable == selectable) return; + getEditor().mTextIsSelectable = selectable; setFocusableInTouchMode(selectable); setFocusable(selectable); setClickable(selectable); setLongClickable(selectable); - // mInputType is already EditorInfo.TYPE_NULL and mInput is null; + // mInputType should already be EditorInfo.TYPE_NULL and mInput should be null setMovementMethod(selectable ? ArrowKeyMovementMethod.getInstance() : null); setText(getText(), selectable ? BufferType.SPANNABLE : BufferType.NORMAL); @@ -4723,7 +4497,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mergeDrawableStates(drawableState, MULTILINE_STATE_SET); } - if (mTextIsSelectable) { + if (isTextSelectable()) { // Disable pressed state, which was introduced when TextView was made clickable. // Prevents text color change. // setClickable(false) would have a similar effect, but it also disables focus changes @@ -4822,7 +4596,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } Layout layout = mLayout; - int cursorcolor = color; if (mHint != null && mText.length() == 0) { if (mHintTextColor != null) { @@ -4870,14 +4643,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int voffsetCursor = 0; // translate in by our padding - { - /* shortcircuit calling getVerticaOffset() */ - if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { - voffsetText = getVerticalOffset(false); - voffsetCursor = getVerticalOffset(true); - } - canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); + /* shortcircuit calling getVerticaOffset() */ + if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { + voffsetText = getVerticalOffset(false); + voffsetCursor = getVerticalOffset(true); } + canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); final int layoutDirection = getResolvedLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); @@ -4894,150 +4665,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - Path highlight = null; - int selStart = -1, selEnd = -1; - boolean drawCursor = false; - - // If there is no movement method, then there can be no selection. - // Check that first and attempt to skip everything having to do with - // the cursor. - // XXX This is not strictly true -- a program could set the - // selection manually if it really wanted to. - if (mMovement != null && (isFocused() || isPressed())) { - selStart = getSelectionStart(); - selEnd = getSelectionEnd(); - - if (selStart >= 0) { - if (mHighlightPath == null) mHighlightPath = new Path(); - - if (selStart == selEnd) { - if (isCursorVisible() && - (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) { - if (mHighlightPathBogus) { - mHighlightPath.reset(); - mLayout.getCursorPath(selStart, mHighlightPath, mText); - updateCursorsPositions(); - mHighlightPathBogus = false; - } - - // XXX should pass to skin instead of drawing directly - mHighlightPaint.setColor(cursorcolor); - if (mCurrentAlpha != 255) { - mHighlightPaint.setAlpha( - (mCurrentAlpha * Color.alpha(cursorcolor)) / 255); - } - mHighlightPaint.setStyle(Paint.Style.STROKE); - highlight = mHighlightPath; - drawCursor = mCursorCount > 0; - } - } else if (textCanBeSelected()) { - if (mHighlightPathBogus) { - mHighlightPath.reset(); - mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); - mHighlightPathBogus = false; - } - - // XXX should pass to skin instead of drawing directly - mHighlightPaint.setColor(mHighlightColor); - if (mCurrentAlpha != 255) { - mHighlightPaint.setAlpha( - (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255); - } - mHighlightPaint.setStyle(Paint.Style.FILL); - - highlight = mHighlightPath; - } - } - } - - final InputMethodState ims = mInputMethodState; final int cursorOffsetVertical = voffsetCursor - voffsetText; - if (ims != null && ims.mBatchEditNesting == 0) { - InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - if (imm.isActive(this)) { - boolean reported = false; - if (ims.mContentChanged || ims.mSelectionModeChanged) { - // We are in extract mode and the content has changed - // in some way... just report complete new text to the - // input method. - reported = reportExtractedText(); - } - if (!reported && highlight != null) { - int candStart = -1; - int candEnd = -1; - if (mText instanceof Spannable) { - Spannable sp = (Spannable)mText; - candStart = EditableInputConnection.getComposingSpanStart(sp); - candEnd = EditableInputConnection.getComposingSpanEnd(sp); - } - imm.updateSelection(this, selStart, selEnd, candStart, candEnd); - } - } - - if (imm.isWatchingCursor(this) && highlight != null) { - highlight.computeBounds(ims.mTmpRectF, true); - ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; - - canvas.getMatrix().mapPoints(ims.mTmpOffset); - ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); - - ims.mTmpRectF.offset(0, cursorOffsetVertical); - - ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), - (int)(ims.mTmpRectF.top + 0.5), - (int)(ims.mTmpRectF.right + 0.5), - (int)(ims.mTmpRectF.bottom + 0.5)); - - imm.updateCursor(this, - ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, - ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); - } - } - } - - if (mCorrectionHighlighter != null) { - mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); - } - - if (drawCursor) { - drawCursor(canvas, cursorOffsetVertical); - // Rely on the drawable entirely, do not draw the cursor line. - // Has to be done after the IMM related code above which relies on the highlight. - highlight = null; - } - - if (canHaveDisplayList() && canvas.isHardwareAccelerated()) { - final int width = mRight - mLeft; - final int height = mBottom - mTop; - - if (mTextDisplayList == null || !mTextDisplayList.isValid() || - !mTextDisplayListIsValid) { - if (mTextDisplayList == null) { - mTextDisplayList = getHardwareRenderer().createDisplayList("Text"); - } - final HardwareCanvas hardwareCanvas = mTextDisplayList.start(); - try { - hardwareCanvas.setViewport(width, height); - // The dirty rect should always be null for a display list - hardwareCanvas.onPreDraw(null); - layout.draw(hardwareCanvas, highlight, mHighlightPaint, cursorOffsetVertical); - } finally { - hardwareCanvas.onPostDraw(); - mTextDisplayList.end(); - mTextDisplayListIsValid = true; - } - } - ((HardwareCanvas) canvas).drawDisplayList(mTextDisplayList, - mScrollX + width, mScrollY + height, null); + if (mEditor != null) { + getEditor().onDraw(canvas, layout, cursorOffsetVertical); } else { - layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); - } + layout.draw(canvas, null, null, cursorOffsetVertical); - if (mMarquee != null && mMarquee.shouldDrawGhost()) { - canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); - layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); + if (mMarquee != null && mMarquee.shouldDrawGhost()) { + canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); + layout.draw(canvas, null, null, cursorOffsetVertical); + } } canvas.restore(); @@ -5045,7 +4683,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void updateCursorsPositions() { if (mCursorDrawableRes == 0) { - mCursorCount = 0; + getEditor().mCursorCount = 0; return; } @@ -5054,40 +4692,39 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int top = mLayout.getLineTop(line); final int bottom = mLayout.getLineTop(line + 1); - mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1; + getEditor().mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1; int middle = bottom; - if (mCursorCount == 2) { + if (getEditor().mCursorCount == 2) { // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)} middle = (top + bottom) >> 1; } updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset)); - if (mCursorCount == 2) { + if (getEditor().mCursorCount == 2) { updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset)); } } private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) { - if (mCursorDrawable[cursorIndex] == null) - mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes); + if (getEditor().mCursorDrawable[cursorIndex] == null) + getEditor().mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes); if (mTempRect == null) mTempRect = new Rect(); - - mCursorDrawable[cursorIndex].getPadding(mTempRect); - final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth(); + getEditor().mCursorDrawable[cursorIndex].getPadding(mTempRect); + final int width = getEditor().mCursorDrawable[cursorIndex].getIntrinsicWidth(); horizontal = Math.max(0.5f, horizontal - 0.5f); final int left = (int) (horizontal) - mTempRect.left; - mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width, + getEditor().mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width, bottom + mTempRect.bottom); } private void drawCursor(Canvas canvas, int cursorOffsetVertical) { final boolean translate = cursorOffsetVertical != 0; if (translate) canvas.translate(0, cursorOffsetVertical); - for (int i = 0; i < mCursorCount; i++) { - mCursorDrawable[i].draw(canvas); + for (int i = 0; i < getEditor().mCursorCount; i++) { + getEditor().mCursorDrawable[i].draw(canvas); } if (translate) canvas.translate(0, -cursorOffsetVertical); } @@ -5121,18 +4758,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener r.left = (int) mLayout.getPrimaryHorizontal(selStart); r.right = (int) mLayout.getPrimaryHorizontal(selEnd); } else { - // Selection extends across multiple lines -- the focused - // rect covers the entire width. - if (mHighlightPath == null) mHighlightPath = new Path(); - if (mHighlightPathBogus) { - mHighlightPath.reset(); - mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); - mHighlightPathBogus = false; - } - synchronized (sTempRect) { - mHighlightPath.computeBounds(sTempRect, true); - r.left = (int)sTempRect.left-1; - r.right = (int)sTempRect.right+1; + // Selection extends across multiple lines -- make the focused + // rect cover the entire width. + if (mEditor != null) { + if (getEditor().mHighlightPath == null) getEditor().mHighlightPath = new Path(); + if (getEditor().mHighlightPathBogus) { + getEditor().mHighlightPath.reset(); + mLayout.getSelectionPath(selStart, selEnd, getEditor().mHighlightPath); + getEditor().mHighlightPathBogus = false; + } + synchronized (TEMP_RECTF) { + getEditor().mHighlightPath.computeBounds(TEMP_RECTF, true); + r.left = (int)TEMP_RECTF.left-1; + r.right = (int)TEMP_RECTF.right+1; + } + } else { + r.left = 0; + r.right = getMeasuredWidth(); } } } @@ -5144,6 +4786,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener paddingTop += getVerticalOffset(false); } r.offset(paddingLeft, paddingTop); + int paddingBottom = getExtendedPaddingBottom(); + r.bottom += paddingBottom; } /** @@ -5228,7 +4872,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onKeyPreIme(int keyCode, KeyEvent event) { if (keyCode == KeyEvent.KEYCODE_BACK) { - boolean isInSelectionMode = mSelectionActionMode != null; + boolean isInSelectionMode = mEditor != null && getEditor().mSelectionActionMode != null; if (isInSelectionMode) { if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { @@ -5286,14 +4930,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // but adding that is a more complicated change. KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); if (which == 1) { - mInput.onKeyUp(this, (Editable)mText, keyCode, up); + // mEditor and getEditor().mInput are not null from doKeyDown + getEditor().mKeyListener.onKeyUp(this, (Editable)mText, keyCode, up); while (--repeatCount > 0) { - mInput.onKeyDown(this, (Editable)mText, keyCode, down); - mInput.onKeyUp(this, (Editable)mText, keyCode, up); + getEditor().mKeyListener.onKeyDown(this, (Editable)mText, keyCode, down); + getEditor().mKeyListener.onKeyUp(this, (Editable)mText, keyCode, up); } hideErrorIfUnchanged(); } else if (which == 2) { + // mMovement is not null from doKeyDown mMovement.onKeyUp(this, (Spannable)mText, keyCode, up); while (--repeatCount > 0) { mMovement.onKeyDown(this, (Spannable)mText, keyCode, down); @@ -5311,7 +4957,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * lines but where it doesn't make sense to insert newlines. */ private boolean shouldAdvanceFocusOnEnter() { - if (mInput == null) { + if (getKeyListener() == null) { return false; } @@ -5319,8 +4965,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return true; } - if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { - int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; + if (mEditor != null && (getEditor().mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { + int variation = getEditor().mInputType & EditorInfo.TYPE_MASK_VARIATION; if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS || variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT) { return true; @@ -5335,9 +4981,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * of inserting the character. Insert tabs only in multi-line editors. */ private boolean shouldAdvanceFocusOnTab() { - if (mInput != null && !mSingleLine) { - if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { - int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; + if (getKeyListener() != null && !mSingleLine) { + if (mEditor != null && (getEditor().mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { + int variation = getEditor().mInputType & EditorInfo.TYPE_MASK_VARIATION; if (variation == EditorInfo.TYPE_TEXT_FLAG_IME_MULTI_LINE || variation == EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE) { return false; @@ -5359,13 +5005,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // running in a "modern" cupcake environment, so don't need // to worry about the application trying to capture // enter key events. - if (mInputContentType != null) { + if (mEditor != null && getEditor().mInputContentType != null) { // If there is an action listener, given them a // chance to consume the event. - if (mInputContentType.onEditorActionListener != null && - mInputContentType.onEditorActionListener.onEditorAction( + if (getEditor().mInputContentType.onEditorActionListener != null && + getEditor().mInputContentType.onEditorActionListener.onEditorAction( this, EditorInfo.IME_NULL, event)) { - mInputContentType.enterDown = true; + getEditor().mInputContentType.enterDown = true; // We are consuming the enter key for them. return -1; } @@ -5402,21 +5048,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Has to be done on key down (and not on key up) to correctly be intercepted. case KeyEvent.KEYCODE_BACK: - if (mSelectionActionMode != null) { + if (mEditor != null && getEditor().mSelectionActionMode != null) { stopSelectionActionMode(); return -1; } break; } - if (mInput != null) { + if (mEditor != null && getEditor().mKeyListener != null) { resetErrorChangedFlag(); boolean doDown = true; if (otherEvent != null) { try { beginBatchEdit(); - final boolean handled = mInput.onKeyOther(this, (Editable) mText, otherEvent); + final boolean handled = getEditor().mKeyListener.onKeyOther(this, (Editable) mText, otherEvent); hideErrorIfUnchanged(); doDown = false; if (handled) { @@ -5432,7 +5078,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (doDown) { beginBatchEdit(); - final boolean handled = mInput.onKeyDown(this, (Editable) mText, keyCode, event); + final boolean handled = getEditor().mKeyListener.onKeyDown(this, (Editable) mText, keyCode, event); endBatchEdit(); hideErrorIfUnchanged(); if (handled) return 1; @@ -5478,14 +5124,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * that error showing. Otherwise, we take down whatever * error was showing when the user types something. */ - mErrorWasChanged = false; + if (mEditor != null) getEditor().mErrorWasChanged = false; } /** * @hide */ public void hideErrorIfUnchanged() { - if (mError != null && !mErrorWasChanged) { + if (mEditor != null && getEditor().mError != null && !getEditor().mErrorWasChanged) { setError(null, null); } } @@ -5523,11 +5169,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener case KeyEvent.KEYCODE_ENTER: if (event.hasNoModifiers()) { - if (mInputContentType != null - && mInputContentType.onEditorActionListener != null - && mInputContentType.enterDown) { - mInputContentType.enterDown = false; - if (mInputContentType.onEditorActionListener.onEditorAction( + if (mEditor != null && getEditor().mInputContentType != null + && getEditor().mInputContentType.onEditorActionListener != null + && getEditor().mInputContentType.enterDown) { + getEditor().mInputContentType.enterDown = false; + if (getEditor().mInputContentType.onEditorActionListener.onEditorAction( this, EditorInfo.IME_NULL, event)) { return true; } @@ -5578,8 +5224,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener break; } - if (mInput != null) - if (mInput.onKeyUp(this, (Editable) mText, keyCode, event)) + if (mEditor != null && getEditor().mKeyListener != null) + if (getEditor().mKeyListener.onKeyUp(this, (Editable) mText, keyCode, event)) return true; if (mMovement != null && mLayout != null) @@ -5591,22 +5237,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onCheckIsTextEditor() { - return mInputType != EditorInfo.TYPE_NULL; + return mEditor != null && getEditor().mInputType != EditorInfo.TYPE_NULL; } @Override public InputConnection onCreateInputConnection(EditorInfo outAttrs) { + createEditorIfNeeded("onCreateInputConnection"); if (onCheckIsTextEditor() && isEnabled()) { - if (mInputMethodState == null) { - mInputMethodState = new InputMethodState(); - } - outAttrs.inputType = mInputType; - if (mInputContentType != null) { - outAttrs.imeOptions = mInputContentType.imeOptions; - outAttrs.privateImeOptions = mInputContentType.privateImeOptions; - outAttrs.actionLabel = mInputContentType.imeActionLabel; - outAttrs.actionId = mInputContentType.imeActionId; - outAttrs.extras = mInputContentType.extras; + if (getEditor().mInputMethodState == null) { + getEditor().mInputMethodState = new InputMethodState(); + } + outAttrs.inputType = getInputType(); + if (getEditor().mInputContentType != null) { + outAttrs.imeOptions = getEditor().mInputContentType.imeOptions; + outAttrs.privateImeOptions = getEditor().mInputContentType.privateImeOptions; + outAttrs.actionLabel = getEditor().mInputContentType.imeActionLabel; + outAttrs.actionId = getEditor().mInputContentType.imeActionId; + outAttrs.extras = getEditor().mInputContentType.extras; } else { outAttrs.imeOptions = EditorInfo.IME_NULL; } @@ -5640,7 +5287,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener InputConnection ic = new EditableInputConnection(this); outAttrs.initialSelStart = getSelectionStart(); outAttrs.initialSelEnd = getSelectionEnd(); - outAttrs.initialCapsMode = ic.getCursorCapsMode(mInputType); + outAttrs.initialCapsMode = ic.getCursorCapsMode(getInputType()); return ic; } } @@ -5732,13 +5379,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } boolean reportExtractedText() { - final InputMethodState ims = mInputMethodState; + final InputMethodState ims = getEditor().mInputMethodState; if (ims != null) { final boolean contentChanged = ims.mContentChanged; if (contentChanged || ims.mSelectionModeChanged) { ims.mContentChanged = false; ims.mSelectionModeChanged = false; - final ExtractedTextRequest req = mInputMethodState.mExtracting; + final ExtractedTextRequest req = ims.mExtracting; if (req != null) { InputMethodManager imm = InputMethodManager.peekInstance(); if (imm != null) { @@ -5754,8 +5401,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener + ims.mTmpExtracted.partialStartOffset + " end=" + ims.mTmpExtracted.partialEndOffset + ": " + ims.mTmpExtracted.text); - imm.updateExtractedText(this, req.token, - mInputMethodState.mTmpExtracted); + imm.updateExtractedText(this, req.token, ims.mTmpExtracted); ims.mChangedStart = EXTRACT_UNKNOWN; ims.mChangedEnd = EXTRACT_UNKNOWN; ims.mChangedDelta = 0; @@ -5832,8 +5478,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @hide */ public void setExtracting(ExtractedTextRequest req) { - if (mInputMethodState != null) { - mInputMethodState.mExtracting = req; + if (getEditor().mInputMethodState != null) { + getEditor().mInputMethodState.mExtracting = req; } // This would stop a possible selection mode, but no such mode is started in case // extracted mode will start. Some text is selected though, and will trigger an action mode @@ -5864,109 +5510,20 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @param info The auto correct info about the text that was corrected. */ public void onCommitCorrection(CorrectionInfo info) { - if (mCorrectionHighlighter == null) { - mCorrectionHighlighter = new CorrectionHighlighter(); + if (mEditor == null) return; + if (getEditor().mCorrectionHighlighter == null) { + getEditor().mCorrectionHighlighter = new CorrectionHighlighter(); } else { - mCorrectionHighlighter.invalidate(false); - } - - mCorrectionHighlighter.highlight(info); - } - - private class CorrectionHighlighter { - private final Path mPath = new Path(); - private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - private int mStart, mEnd; - private long mFadingStartTime; - private final static int FADE_OUT_DURATION = 400; - - public CorrectionHighlighter() { - mPaint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); - mPaint.setStyle(Paint.Style.FILL); - } - - public void highlight(CorrectionInfo info) { - mStart = info.getOffset(); - mEnd = mStart + info.getNewText().length(); - mFadingStartTime = SystemClock.uptimeMillis(); - - if (mStart < 0 || mEnd < 0) { - stopAnimation(); - } - } - - public void draw(Canvas canvas, int cursorOffsetVertical) { - if (updatePath() && updatePaint()) { - if (cursorOffsetVertical != 0) { - canvas.translate(0, cursorOffsetVertical); - } - - canvas.drawPath(mPath, mPaint); - - if (cursorOffsetVertical != 0) { - canvas.translate(0, -cursorOffsetVertical); - } - invalidate(true); // TODO invalidate cursor region only - } else { - stopAnimation(); - invalidate(false); // TODO invalidate cursor region only - } - } - - private boolean updatePaint() { - final long duration = SystemClock.uptimeMillis() - mFadingStartTime; - if (duration > FADE_OUT_DURATION) return false; - - final float coef = 1.0f - (float) duration / FADE_OUT_DURATION; - final int highlightColorAlpha = Color.alpha(mHighlightColor); - final int color = (mHighlightColor & 0x00FFFFFF) + - ((int) (highlightColorAlpha * coef) << 24); - mPaint.setColor(color); - return true; - } - - private boolean updatePath() { - final Layout layout = TextView.this.mLayout; - if (layout == null) return false; - - // Update in case text is edited while the animation is run - final int length = mText.length(); - int start = Math.min(length, mStart); - int end = Math.min(length, mEnd); - - mPath.reset(); - TextView.this.mLayout.getSelectionPath(start, end, mPath); - return true; - } - - private void invalidate(boolean delayed) { - if (TextView.this.mLayout == null) return; - - synchronized (sTempRect) { - mPath.computeBounds(sTempRect, false); - - int left = getCompoundPaddingLeft(); - int top = getExtendedPaddingTop() + getVerticalOffset(true); - - if (delayed) { - TextView.this.postInvalidateDelayed(16, // 60 Hz update - left + (int) sTempRect.left, top + (int) sTempRect.top, - left + (int) sTempRect.right, top + (int) sTempRect.bottom); - } else { - TextView.this.postInvalidate((int) sTempRect.left, (int) sTempRect.top, - (int) sTempRect.right, (int) sTempRect.bottom); - } - } + getEditor().mCorrectionHighlighter.invalidate(false); } - private void stopAnimation() { - TextView.this.mCorrectionHighlighter = null; - } + getEditor().mCorrectionHighlighter.highlight(info); } public void beginBatchEdit() { - mInBatchEditControllers = true; - final InputMethodState ims = mInputMethodState; + if (mEditor == null) return; + getEditor().mInBatchEditControllers = true; + final InputMethodState ims = getEditor().mInputMethodState; if (ims != null) { int nesting = ++ims.mBatchEditNesting; if (nesting == 1) { @@ -5988,8 +5545,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public void endBatchEdit() { - mInBatchEditControllers = false; - final InputMethodState ims = mInputMethodState; + if (mEditor == null) return; + getEditor().mInBatchEditControllers = false; + final InputMethodState ims = getEditor().mInputMethodState; if (ims != null) { int nesting = --ims.mBatchEditNesting; if (nesting == 0) { @@ -5999,7 +5557,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } void ensureEndedBatchEdit() { - final InputMethodState ims = mInputMethodState; + final InputMethodState ims = getEditor().mInputMethodState; if (ims != null && ims.mBatchEditNesting != 0) { ims.mBatchEditNesting = 0; finishBatchEdit(ims); @@ -6027,7 +5585,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (curs >= 0) { - mHighlightPathBogus = true; + getEditor().mHighlightPathBogus = true; makeBlink(); bringPointIntoView(curs); } @@ -6042,7 +5600,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void onBeginBatchEdit() { // intentionally empty } - + /** * Called by the framework in response to a request to end a batch * of edit operations through a call to link {@link #endBatchEdit}. @@ -6107,8 +5665,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.resetResolvedLayoutDirection(); if (mLayoutAlignment != null && - (mTextAlign == TextAlign.VIEW_START || - mTextAlign == TextAlign.VIEW_END)) { + (mTextAlign == TEXT_ALIGN.VIEW_START || + mTextAlign == TEXT_ALIGN.VIEW_END)) { mLayoutAlignment = null; } } @@ -6116,7 +5674,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private Layout.Alignment getLayoutAlignment() { if (mLayoutAlignment == null) { Layout.Alignment alignment; - TextAlign textAlign = mTextAlign; + TEXT_ALIGN textAlign = mTextAlign; switch (textAlign) { case INHERIT: // fall through to gravity temporarily @@ -6184,7 +5742,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mOldMaximum = mMaximum; mOldMaxMode = mMaxMode; - mHighlightPathBogus = true; + if (mEditor != null) getEditor().mHighlightPathBogus = true; if (wantWidth < 0) { wantWidth = 0; @@ -6194,7 +5752,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } Layout.Alignment alignment = getLayoutAlignment(); - boolean shouldEllipsize = mEllipsize != null && mInput == null; + boolean shouldEllipsize = mEllipsize != null && getKeyListener() == null; final boolean switchEllipsize = mEllipsize == TruncateAt.MARQUEE && mMarqueeFadeMode != MARQUEE_FADE_NORMAL; TruncateAt effectiveEllipsize = mEllipsize; @@ -6311,7 +5869,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (mText instanceof Spannable) { result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth, alignment, mTextDir, mSpacingMult, - mSpacingAdd, mIncludePad, mInput == null ? effectiveEllipsize : null, + mSpacingAdd, mIncludePad, getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth); } else { if (boring == UNKNOWN_BORING) { @@ -6772,7 +6330,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - if (changed) mTextDisplayListIsValid = false; + if (changed && mEditor != null) getEditor().mTextDisplayListIsValid = false; } /** @@ -6998,11 +6556,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // This offsets because getInterestingRect() is in terms of viewport coordinates, but // requestRectangleOnScreen() is in terms of content coordinates. - if (mTempRect == null) mTempRect = new Rect(); // The offsets here are to ensure the rectangle we are using is // within our view bounds, in case the cursor is on the far left // or right. If it isn't withing the bounds, then this request // will be ignored. + if (mTempRect == null) mTempRect = new Rect(); mTempRect.set(x - 2, top, x + 2, bottom); getInterestingRect(mTempRect, line); mTempRect.offset(mScrollX, mScrollY); @@ -7222,11 +6780,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * @param singleLine */ private void setInputTypeSingleLine(boolean singleLine) { - if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { + if (mEditor != null && (getEditor().mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { if (singleLine) { - mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; + getEditor().mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } else { - mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; + getEditor().mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; } } } @@ -7305,7 +6863,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @android.view.RemotableViewMethod public void setSelectAllOnFocus(boolean selectAllOnFocus) { - mSelectAllOnFocus = selectAllOnFocus; + createEditorIfNeeded("setSelectAllOnFocus"); + getEditor().mSelectAllOnFocus = selectAllOnFocus; if (selectAllOnFocus && !(mText instanceof Spannable)) { setText(mText, BufferType.SPANNABLE); @@ -7319,8 +6878,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ @android.view.RemotableViewMethod public void setCursorVisible(boolean visible) { - if (mCursorVisible != visible) { - mCursorVisible = visible; + if (visible && mEditor == null) return; // visible is the default value with no edit data + createEditorIfNeeded("setCursorVisible"); + if (getEditor().mCursorVisible != visible) { + getEditor().mCursorVisible = visible; invalidate(); makeBlink(); @@ -7331,7 +6892,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private boolean isCursorVisible() { - return mCursorVisible && isTextEditable(); + // The default value is true, even when there is no associated Editor + return mEditor == null ? true : (getEditor().mCursorVisible && isTextEditable()); } private boolean canMarquee() { @@ -7343,7 +6905,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void startMarquee() { // Do not ellipsize EditText - if (mInput != null) return; + if (getKeyListener() != null) return; if (compressText(getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight())) { return; @@ -7393,142 +6955,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private static final class Marquee extends Handler { - // TODO: Add an option to configure this - private static final float MARQUEE_DELTA_MAX = 0.07f; - private static final int MARQUEE_DELAY = 1200; - private static final int MARQUEE_RESTART_DELAY = 1200; - private static final int MARQUEE_RESOLUTION = 1000 / 30; - private static final int MARQUEE_PIXELS_PER_SECOND = 30; - - private static final byte MARQUEE_STOPPED = 0x0; - private static final byte MARQUEE_STARTING = 0x1; - private static final byte MARQUEE_RUNNING = 0x2; - - private static final int MESSAGE_START = 0x1; - private static final int MESSAGE_TICK = 0x2; - private static final int MESSAGE_RESTART = 0x3; - - private final WeakReference<TextView> mView; - - private byte mStatus = MARQUEE_STOPPED; - private final float mScrollUnit; - private float mMaxScroll; - float mMaxFadeScroll; - private float mGhostStart; - private float mGhostOffset; - private float mFadeStop; - private int mRepeatLimit; - - float mScroll; - - Marquee(TextView v) { - final float density = v.getContext().getResources().getDisplayMetrics().density; - mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION; - mView = new WeakReference<TextView>(v); - } - - @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MESSAGE_START: - mStatus = MARQUEE_RUNNING; - tick(); - break; - case MESSAGE_TICK: - tick(); - break; - case MESSAGE_RESTART: - if (mStatus == MARQUEE_RUNNING) { - if (mRepeatLimit >= 0) { - mRepeatLimit--; - } - start(mRepeatLimit); - } - break; - } - } - - void tick() { - if (mStatus != MARQUEE_RUNNING) { - return; - } - - removeMessages(MESSAGE_TICK); - - final TextView textView = mView.get(); - if (textView != null && (textView.isFocused() || textView.isSelected())) { - mScroll += mScrollUnit; - if (mScroll > mMaxScroll) { - mScroll = mMaxScroll; - sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY); - } else { - sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION); - } - textView.invalidate(); - } - } - - void stop() { - mStatus = MARQUEE_STOPPED; - removeMessages(MESSAGE_START); - removeMessages(MESSAGE_RESTART); - removeMessages(MESSAGE_TICK); - resetScroll(); - } - - private void resetScroll() { - mScroll = 0.0f; - final TextView textView = mView.get(); - if (textView != null) textView.invalidate(); - } - - void start(int repeatLimit) { - if (repeatLimit == 0) { - stop(); - return; - } - mRepeatLimit = repeatLimit; - final TextView textView = mView.get(); - if (textView != null && textView.mLayout != null) { - mStatus = MARQUEE_STARTING; - mScroll = 0.0f; - final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() - - textView.getCompoundPaddingRight(); - final float lineWidth = textView.mLayout.getLineWidth(0); - final float gap = textWidth / 3.0f; - mGhostStart = lineWidth - textWidth + gap; - mMaxScroll = mGhostStart + textWidth; - mGhostOffset = lineWidth + gap; - mFadeStop = lineWidth + textWidth / 6.0f; - mMaxFadeScroll = mGhostStart + lineWidth + lineWidth; - - textView.invalidate(); - sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY); - } - } - - float getGhostOffset() { - return mGhostOffset; - } - - boolean shouldDrawLeftFade() { - return mScroll <= mFadeStop; - } - - boolean shouldDrawGhost() { - return mStatus == MARQUEE_RUNNING && mScroll > mGhostStart; - } - - boolean isRunning() { - return mStatus == MARQUEE_RUNNING; - } - - boolean isStopped() { - return mStatus == MARQUEE_STOPPED; - } - } - /** * This method is called when the text is changed, in case any subclasses * would like to know. @@ -7557,7 +6983,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ protected void onSelectionChanged(int selStart, int selEnd) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); - mTextDisplayListIsValid = false; + getEditor().mTextDisplayListIsValid = false; } /** @@ -7636,13 +7062,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - updateSpellCheckSpans(start, start + after, false); - mTextDisplayListIsValid = false; - - // Hide the controllers as soon as text is modified (typing, procedural...) - // We do not hide the span controllers, since they can be added when a new text is - // inserted into the text view (voice IME). - hideCursorControllers(); + if (mEditor != null) getEditor().sendOnTextChanged(start, after); } /** @@ -7664,7 +7084,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * through a thunk. */ void handleTextChanged(CharSequence buffer, int start, int before, int after) { - final InputMethodState ims = mInputMethodState; + final InputMethodState ims = mEditor == null ? null : getEditor().mInputMethodState; if (ims == null || ims.mBatchEditNesting == 0) { updateAfterEdit(); } @@ -7683,7 +7103,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener sendOnTextChanged(buffer, start, before, after); onTextChanged(buffer, start, before, after); } - + /** * Not private so it can be called from an inner class without going * through a thunk. @@ -7694,18 +7114,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean selChanged = false; int newSelStart=-1, newSelEnd=-1; - - final InputMethodState ims = mInputMethodState; - + + final InputMethodState ims = mEditor == null ? null : getEditor().mInputMethodState; + if (what == Selection.SELECTION_END) { - mHighlightPathBogus = true; selChanged = true; newSelEnd = newStart; - if (!isFocused()) { - mSelectionMoved = true; - } - if (oldStart >= 0 || newStart >= 0) { invalidateCursor(Selection.getSelectionStart(buf), oldStart, newStart); registerForPreDraw(); @@ -7714,14 +7129,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (what == Selection.SELECTION_START) { - mHighlightPathBogus = true; selChanged = true; newSelStart = newStart; - if (!isFocused()) { - mSelectionMoved = true; - } - if (oldStart >= 0 || newStart >= 0) { int end = Selection.getSelectionEnd(buf); invalidateCursor(end, oldStart, newStart); @@ -7729,6 +7139,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (selChanged) { + if (mEditor != null) { + getEditor().mHighlightPathBogus = true; + if (!isFocused()) getEditor().mSelectionMoved = true; + } + if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) { if (newSelStart < 0) { newSelStart = Selection.getSelectionStart(buf); @@ -7744,16 +7159,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener what instanceof CharacterStyle) { if (ims == null || ims.mBatchEditNesting == 0) { invalidate(); - mHighlightPathBogus = true; + if (mEditor != null) getEditor().mHighlightPathBogus = true; checkForResize(); } else { ims.mContentChanged = true; } - mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; } if (MetaKeyKeyListener.isMetaTracker(buf, what)) { - mHighlightPathBogus = true; + if (mEditor != null) getEditor().mHighlightPathBogus = true; if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) { ims.mSelectionModeChanged = true; } @@ -7797,8 +7212,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) { - mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what); + if (mEditor != null && getEditor().mSpellChecker != null && newStart < 0 && what instanceof SpellCheckSpan) { + getEditor().mSpellChecker.removeSpellCheckSpan((SpellCheckSpan) what); } } @@ -7807,289 +7222,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ private void updateSpellCheckSpans(int start, int end, boolean createSpellChecker) { if (isTextEditable() && isSuggestionsEnabled() && !(this instanceof ExtractEditText)) { - if (mSpellChecker == null && createSpellChecker) { - mSpellChecker = new SpellChecker(this); - } - if (mSpellChecker != null) { - mSpellChecker.spellCheck(start, end); - } - } - } - - /** - * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related - * pop-up should be displayed. - */ - private class EasyEditSpanController { - - private static final int DISPLAY_TIMEOUT_MS = 3000; // 3 secs - - private EasyEditPopupWindow mPopupWindow; - - private EasyEditSpan mEasyEditSpan; - - private Runnable mHidePopup; - - private void hide() { - if (mPopupWindow != null) { - mPopupWindow.hide(); - TextView.this.removeCallbacks(mHidePopup); - } - removeSpans(mText); - mEasyEditSpan = null; - } - - /** - * Monitors the changes in the text. - * - * <p>{@link ChangeWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used, - * as the notifications are not sent when a spannable (with spans) is inserted. - */ - public void onTextChange(CharSequence buffer) { - adjustSpans(mText); - - if (getWindowVisibility() != View.VISIBLE) { - // The window is not visible yet, ignore the text change. - return; - } - - if (mLayout == null) { - // The view has not been layout yet, ignore the text change - return; - } - - InputMethodManager imm = InputMethodManager.peekInstance(); - if (!(TextView.this instanceof ExtractEditText) - && imm != null && imm.isFullscreenMode()) { - // The input is in extract mode. We do not have to handle the easy edit in the - // original TextView, as the ExtractEditText will do - return; - } - - // Remove the current easy edit span, as the text changed, and remove the pop-up - // (if any) - if (mEasyEditSpan != null) { - if (mText instanceof Spannable) { - ((Spannable) mText).removeSpan(mEasyEditSpan); - } - mEasyEditSpan = null; - } - if (mPopupWindow != null && mPopupWindow.isShowing()) { - mPopupWindow.hide(); - } - - // Display the new easy edit span (if any). - if (buffer instanceof Spanned) { - mEasyEditSpan = getSpan((Spanned) buffer); - if (mEasyEditSpan != null) { - if (mPopupWindow == null) { - mPopupWindow = new EasyEditPopupWindow(); - mHidePopup = new Runnable() { - @Override - public void run() { - hide(); - } - }; - } - mPopupWindow.show(mEasyEditSpan); - TextView.this.removeCallbacks(mHidePopup); - TextView.this.postDelayed(mHidePopup, DISPLAY_TIMEOUT_MS); - } + if (getEditor().mSpellChecker == null && createSpellChecker) { + getEditor().mSpellChecker = new SpellChecker(this); } - } - - /** - * Adjusts the spans by removing all of them except the last one. - */ - private void adjustSpans(CharSequence buffer) { - // This method enforces that only one easy edit span is attached to the text. - // A better way to enforce this would be to listen for onSpanAdded, but this method - // cannot be used in this scenario as no notification is triggered when a text with - // spans is inserted into a text. - if (buffer instanceof Spannable) { - Spannable spannable = (Spannable) buffer; - EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(), - EasyEditSpan.class); - for (int i = 0; i < spans.length - 1; i++) { - spannable.removeSpan(spans[i]); - } - } - } - - /** - * Removes all the {@link EasyEditSpan} currently attached. - */ - private void removeSpans(CharSequence buffer) { - if (buffer instanceof Spannable) { - Spannable spannable = (Spannable) buffer; - EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(), - EasyEditSpan.class); - for (int i = 0; i < spans.length; i++) { - spannable.removeSpan(spans[i]); - } - } - } - - private EasyEditSpan getSpan(Spanned spanned) { - EasyEditSpan[] easyEditSpans = spanned.getSpans(0, spanned.length(), - EasyEditSpan.class); - if (easyEditSpans.length == 0) { - return null; - } else { - return easyEditSpans[0]; + if (getEditor().mSpellChecker != null) { + getEditor().mSpellChecker.spellCheck(start, end); } } } /** - * Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled - * by {@link EasyEditSpanController}. - */ - private class EasyEditPopupWindow extends PinnedPopupWindow - implements OnClickListener { - private static final int POPUP_TEXT_LAYOUT = - com.android.internal.R.layout.text_edit_action_popup_text; - private TextView mDeleteTextView; - private EasyEditSpan mEasyEditSpan; - - @Override - protected void createPopupWindow() { - mPopupWindow = new PopupWindow(TextView.this.mContext, null, - com.android.internal.R.attr.textSelectHandleWindowStyle); - mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); - mPopupWindow.setClippingEnabled(true); - } - - @Override - protected void initContentView() { - LinearLayout linearLayout = new LinearLayout(TextView.this.getContext()); - linearLayout.setOrientation(LinearLayout.HORIZONTAL); - mContentView = linearLayout; - mContentView.setBackgroundResource( - com.android.internal.R.drawable.text_edit_side_paste_window); - - LayoutInflater inflater = (LayoutInflater)TextView.this.mContext. - getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - LayoutParams wrapContent = new LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); - - mDeleteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null); - mDeleteTextView.setLayoutParams(wrapContent); - mDeleteTextView.setText(com.android.internal.R.string.delete); - mDeleteTextView.setOnClickListener(this); - mContentView.addView(mDeleteTextView); - } - - public void show(EasyEditSpan easyEditSpan) { - mEasyEditSpan = easyEditSpan; - super.show(); - } - - @Override - public void onClick(View view) { - if (view == mDeleteTextView) { - Editable editable = (Editable) mText; - int start = editable.getSpanStart(mEasyEditSpan); - int end = editable.getSpanEnd(mEasyEditSpan); - if (start >= 0 && end >= 0) { - deleteText_internal(start, end); - } - } - } - - @Override - protected int getTextOffset() { - // Place the pop-up at the end of the span - Editable editable = (Editable) mText; - return editable.getSpanEnd(mEasyEditSpan); - } - - @Override - protected int getVerticalLocalPosition(int line) { - return mLayout.getLineBottom(line); - } - - @Override - protected int clipVertically(int positionY) { - // As we display the pop-up below the span, no vertical clipping is required. - return positionY; - } - } - - private class ChangeWatcher implements TextWatcher, SpanWatcher { - - private CharSequence mBeforeText; - - private EasyEditSpanController mEasyEditSpanController; - - private ChangeWatcher() { - mEasyEditSpanController = new EasyEditSpanController(); - } - - public void beforeTextChanged(CharSequence buffer, int start, - int before, int after) { - if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start - + " before=" + before + " after=" + after + ": " + buffer); - - if (AccessibilityManager.getInstance(mContext).isEnabled() - && !isPasswordInputType(mInputType) - && !hasPasswordTransformationMethod()) { - mBeforeText = buffer.toString(); - } - - TextView.this.sendBeforeTextChanged(buffer, start, before, after); - } - - public void onTextChanged(CharSequence buffer, int start, - int before, int after) { - if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onTextChanged start=" + start - + " before=" + before + " after=" + after + ": " + buffer); - TextView.this.handleTextChanged(buffer, start, before, after); - - mEasyEditSpanController.onTextChange(buffer); - - if (AccessibilityManager.getInstance(mContext).isEnabled() && - (isFocused() || isSelected() && isShown())) { - sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); - mBeforeText = null; - } - } - - public void afterTextChanged(Editable buffer) { - if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer); - TextView.this.sendAfterTextChanged(buffer); - - if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { - MetaKeyKeyListener.stopSelecting(TextView.this, buffer); - } - } - - public void onSpanChanged(Spannable buf, - Object what, int s, int e, int st, int en) { - if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanChanged s=" + s + " e=" + e - + " st=" + st + " en=" + en + " what=" + what + ": " + buf); - TextView.this.spanChange(buf, what, s, st, e, en); - } - - public void onSpanAdded(Spannable buf, Object what, int s, int e) { - if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e - + " what=" + what + ": " + buf); - TextView.this.spanChange(buf, what, -1, s, -1, e); - } - - public void onSpanRemoved(Spannable buf, Object what, int s, int e) { - if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanRemoved s=" + s + " e=" + e - + " what=" + what + ": " + buf); - TextView.this.spanChange(buf, what, s, -1, e, -1); - } - - private void hideControllers() { - mEasyEditSpanController.hide(); - } - } - - /** * @hide */ @Override @@ -8109,7 +7251,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // Because of View recycling in ListView, there is no easy way to know when a TextView with // selection becomes visible again. Until a better solution is found, stop text selection // mode (if any) as soon as this TextView is recycled. - hideControllers(); + if (mEditor != null) hideControllers(); } @Override @@ -8127,95 +7269,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.onFocusChanged(focused, direction, previouslyFocusedRect); return; } - - mShowCursor = SystemClock.uptimeMillis(); - ensureEndedBatchEdit(); + if (mEditor != null) getEditor().onFocusChanged(focused, direction); if (focused) { - int selStart = getSelectionStart(); - int selEnd = getSelectionEnd(); - - // SelectAllOnFocus fields are highlighted and not selected. Do not start text selection - // mode for these, unless there was a specific selection already started. - final boolean isFocusHighlighted = mSelectAllOnFocus && selStart == 0 && - selEnd == mText.length(); - mCreatedWithASelection = mFrozenWithFocus && hasSelection() && !isFocusHighlighted; - - if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) { - // If a tap was used to give focus to that view, move cursor at tap position. - // Has to be done before onTakeFocus, which can be overloaded. - final int lastTapPosition = getLastTapPosition(); - if (lastTapPosition >= 0) { - Selection.setSelection((Spannable) mText, lastTapPosition); - } - - if (mMovement != null) { - mMovement.onTakeFocus(this, (Spannable) mText, direction); - } - - // The DecorView does not have focus when the 'Done' ExtractEditText button is - // pressed. Since it is the ViewAncestor's mView, it requests focus before - // ExtractEditText clears focus, which gives focus to the ExtractEditText. - // This special case ensure that we keep current selection in that case. - // It would be better to know why the DecorView does not have focus at that time. - if (((this instanceof ExtractEditText) || mSelectionMoved) && - selStart >= 0 && selEnd >= 0) { - /* - * Someone intentionally set the selection, so let them - * do whatever it is that they wanted to do instead of - * the default on-focus behavior. We reset the selection - * here instead of just skipping the onTakeFocus() call - * because some movement methods do something other than - * just setting the selection in theirs and we still - * need to go through that path. - */ - Selection.setSelection((Spannable) mText, selStart, selEnd); - } - - if (mSelectAllOnFocus) { - selectAll(); - } - - mTouchFocusSelected = true; - } - - mFrozenWithFocus = false; - mSelectionMoved = false; - if (mText instanceof Spannable) { Spannable sp = (Spannable) mText; MetaKeyKeyListener.resetMetaState(sp); } - - makeBlink(); - - if (mError != null) { - showError(); - } - } else { - if (mError != null) { - hideError(); - } - // Don't leave us in the middle of a batch edit. - onEndBatchEdit(); - - if (this instanceof ExtractEditText) { - // terminateTextSelectionMode removes selection, which we want to keep when - // ExtractEditText goes out of focus. - final int selStart = getSelectionStart(); - final int selEnd = getSelectionEnd(); - hideControllers(); - Selection.setSelection((Spannable) mText, selStart, selEnd); - } else { - hideControllers(); - downgradeEasyCorrectionSpans(); - } - - // No need to create the controller - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.resetTouchOffsets(); - } } startStopMarquee(focused); @@ -8227,48 +7288,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener super.onFocusChanged(focused, direction, previouslyFocusedRect); } - private int getLastTapPosition() { - // No need to create the controller at that point, no last tap position saved - if (mSelectionModifierCursorController != null) { - int lastTapPosition = mSelectionModifierCursorController.getMinTouchOffset(); - if (lastTapPosition >= 0) { - // Safety check, should not be possible. - if (lastTapPosition > mText.length()) { - Log.e(LOG_TAG, "Invalid tap focus position (" + lastTapPosition + " vs " - + mText.length() + ")"); - lastTapPosition = mText.length(); - } - return lastTapPosition; - } - } - - return -1; - } - @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); - if (hasWindowFocus) { - if (mBlink != null) { - mBlink.uncancel(); - makeBlink(); - } - } else { - if (mBlink != null) { - mBlink.cancel(); - } - // Don't leave us in the middle of a batch edit. - onEndBatchEdit(); - if (mInputContentType != null) { - mInputContentType.enterDown = false; - } - - hideControllers(); - if (mSuggestionsPopupWindow != null) { - mSuggestionsPopupWindow.onParentLostFocus(); - } - } + if (mEditor != null) getEditor().onWindowFocusChanged(hasWindowFocus); startStopMarquee(hasWindowFocus); } @@ -8276,7 +7300,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override protected void onVisibilityChanged(View changedView, int visibility) { super.onVisibilityChanged(changedView, visibility); - if (visibility != VISIBLE) { + if (mEditor != null && visibility != VISIBLE) { hideControllers(); } } @@ -8311,23 +7335,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public boolean onTouchEvent(MotionEvent event) { final int action = event.getActionMasked(); - if (hasSelectionController()) { - getSelectionController().onTouchEvent(event); - } - - if (mShowSuggestionRunnable != null) { - removeCallbacks(mShowSuggestionRunnable); - } - - if (action == MotionEvent.ACTION_DOWN) { - mLastDownPositionX = event.getX(); - mLastDownPositionY = event.getY(); - - // Reset this state; it will be re-set if super.onTouchEvent - // causes focus to move to the view. - mTouchFocusSelected = false; - mIgnoreActionUpEvent = false; - } + if (mEditor != null) getEditor().onTouchEvent(event); final boolean superResult = super.onTouchEvent(event); @@ -8336,13 +7344,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * move the selection away from whatever the menu action was * trying to affect. */ - if (mDiscardNextActionUp && action == MotionEvent.ACTION_UP) { - mDiscardNextActionUp = false; + if (mEditor != null && getEditor().mDiscardNextActionUp && action == MotionEvent.ACTION_UP) { + getEditor().mDiscardNextActionUp = false; return superResult; } final boolean touchIsFinished = (action == MotionEvent.ACTION_UP) && - !mIgnoreActionUpEvent && isFocused(); + (mEditor == null || !getEditor().mIgnoreActionUpEvent) && isFocused(); if ((mMovement != null || onCheckIsTextEditor()) && isEnabled() && mText instanceof Spannable && mLayout != null) { @@ -8352,7 +7360,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener handled |= mMovement.onTouchEvent(this, (Spannable) mText, event); } - if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && mTextIsSelectable) { + final boolean textIsSelectable = isTextSelectable(); + if (touchIsFinished && mLinksClickable && mAutoLinkMask != 0 && textIsSelectable) { // The LinkMovementMethod which should handle taps on links has not been installed // on non editable text that support text selection. // We reproduce its behavior here to open links for these. @@ -8365,34 +7374,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - if (touchIsFinished && (isTextEditable() || mTextIsSelectable)) { + if (touchIsFinished && (isTextEditable() || textIsSelectable)) { // Show the IME, except when selecting in read-only text. final InputMethodManager imm = InputMethodManager.peekInstance(); viewClicked(imm); - if (!mTextIsSelectable) { + if (!textIsSelectable) { handled |= imm != null && imm.showSoftInput(this, 0); } - boolean selectAllGotFocus = mSelectAllOnFocus && didTouchFocusSelect(); + boolean selectAllGotFocus = getEditor().mSelectAllOnFocus && didTouchFocusSelect(); hideControllers(); if (!selectAllGotFocus && mText.length() > 0) { // Move cursor final int offset = getOffsetForPosition(event.getX(), event.getY()); Selection.setSelection((Spannable) mText, offset); - if (mSpellChecker != null) { + if (getEditor().mSpellChecker != null) { // When the cursor moves, the word that was typed may need spell check - mSpellChecker.onSelectionChanged(); + getEditor().mSpellChecker.onSelectionChanged(); } if (!extractedTextModeWillBeStarted()) { if (isCursorInsideEasyCorrectionSpan()) { - if (mShowSuggestionRunnable == null) { - mShowSuggestionRunnable = new Runnable() { - public void run() { - showSuggestions(); - } - }; - } - postDelayed(mShowSuggestionRunnable, + getEditor().mShowSuggestionRunnable = new Runnable() { + public void run() { + showSuggestions(); + } + }; + // removeCallbacks is performed on every touch + postDelayed(getEditor().mShowSuggestionRunnable, ViewConfiguration.getDoubleTapTimeout()); } else if (hasInsertionController()) { getInsertionController().show(); @@ -8475,6 +7483,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void prepareCursorControllers() { + if (mEditor == null) return; + boolean windowSupportsHandles = false; ViewGroup.LayoutParams params = getRootView().getLayoutParams(); @@ -8484,23 +7494,23 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener || windowParams.type > WindowManager.LayoutParams.LAST_SUB_WINDOW; } - mInsertionControllerEnabled = windowSupportsHandles && isCursorVisible() && mLayout != null; - mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() && + getEditor().mInsertionControllerEnabled = windowSupportsHandles && isCursorVisible() && mLayout != null; + getEditor().mSelectionControllerEnabled = windowSupportsHandles && textCanBeSelected() && mLayout != null; - if (!mInsertionControllerEnabled) { + if (!getEditor().mInsertionControllerEnabled) { hideInsertionPointCursorController(); - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.onDetached(); - mInsertionPointCursorController = null; + if (getEditor().mInsertionPointCursorController != null) { + getEditor().mInsertionPointCursorController.onDetached(); + getEditor().mInsertionPointCursorController = null; } } - if (!mSelectionControllerEnabled) { + if (!getEditor().mSelectionControllerEnabled) { stopSelectionActionMode(); - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.onDetached(); - mSelectionModifierCursorController = null; + if (getEditor().mSelectionModifierCursorController != null) { + getEditor().mSelectionModifierCursorController.onDetached(); + getEditor().mSelectionModifierCursorController = null; } } } @@ -8520,19 +7530,18 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener * of interest. */ public boolean didTouchFocusSelect() { - return mTouchFocusSelected; + return mEditor != null && getEditor().mTouchFocusSelected; } @Override public void cancelLongPress() { super.cancelLongPress(); - mIgnoreActionUpEvent = true; + if (mEditor != null) getEditor().mIgnoreActionUpEvent = true; } @Override public boolean onTrackballEvent(MotionEvent event) { - if (mMovement != null && mText instanceof Spannable && - mLayout != null) { + if (mMovement != null && mText instanceof Spannable && mLayout != null) { if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) { return true; } @@ -8545,49 +7554,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mScroller = s; } - private static class Blink extends Handler implements Runnable { - private final WeakReference<TextView> mView; - private boolean mCancelled; - - public Blink(TextView v) { - mView = new WeakReference<TextView>(v); - } - - public void run() { - if (mCancelled) { - return; - } - - removeCallbacks(Blink.this); - - TextView tv = mView.get(); - - if (tv != null && tv.shouldBlink()) { - if (tv.mLayout != null) { - tv.invalidateCursorPath(); - } - - postAtTime(this, SystemClock.uptimeMillis() + BLINK); - } - } - - void cancel() { - if (!mCancelled) { - removeCallbacks(Blink.this); - mCancelled = true; - } - } - - void uncancel() { - mCancelled = false; - } - } - /** * @return True when the TextView isFocused and has a valid zero-length selection (cursor). */ private boolean shouldBlink() { - if (!isCursorVisible() || !isFocused()) return false; + if (mEditor == null || !isCursorVisible() || !isFocused()) return false; final int start = getSelectionStart(); if (start < 0) return false; @@ -8600,12 +7571,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void makeBlink() { if (shouldBlink()) { - mShowCursor = SystemClock.uptimeMillis(); - if (mBlink == null) mBlink = new Blink(this); - mBlink.removeCallbacks(mBlink); - mBlink.postAtTime(mBlink, mShowCursor + BLINK); + getEditor().mShowCursor = SystemClock.uptimeMillis(); + if (getEditor().mBlink == null) getEditor().mBlink = new Blink(this); + getEditor().mBlink.removeCallbacks(getEditor().mBlink); + getEditor().mBlink.postAtTime(getEditor().mBlink, getEditor().mShowCursor + BLINK); } else { - if (mBlink != null) mBlink.removeCallbacks(mBlink); + if (mEditor != null && getEditor().mBlink != null) getEditor().mBlink.removeCallbacks(getEditor().mBlink); } } @@ -8804,7 +7775,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // If you change this condition, make sure prepareCursorController is called anywhere // the value of this condition might be changed. if (mMovement == null || !mMovement.canSelectArbitrarily()) return false; - return isTextEditable() || (mTextIsSelectable && mText instanceof Spannable && isEnabled()); + return isTextEditable() || (isTextSelectable() && mText instanceof Spannable && isEnabled()); } private boolean canCut() { @@ -8812,7 +7783,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return false; } - if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mInput != null) { + if (mText.length() > 0 && hasSelection() && mText instanceof Editable && mEditor != null && getEditor().mKeyListener != null) { return true; } @@ -8833,7 +7804,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private boolean canPaste() { return (mText instanceof Editable && - mInput != null && + mEditor != null && getEditor().mKeyListener != null && getSelectionStart() >= 0 && getSelectionEnd() >= 0 && ((ClipboardManager)getContext().getSystemService(Context.CLIPBOARD_SERVICE)). @@ -8874,8 +7845,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return selectAll(); } - int klass = mInputType & InputType.TYPE_MASK_CLASS; - int variation = mInputType & InputType.TYPE_MASK_VARIATION; + int inputType = getInputType(); + int klass = inputType & InputType.TYPE_MASK_CLASS; + int variation = inputType & InputType.TYPE_MASK_VARIATION; // Specific text field types: select the entire text for these if (klass == InputType.TYPE_CLASS_NUMBER || @@ -8945,17 +7917,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener void onLocaleChanged() { // Will be re-created on demand in getWordIterator with the proper new locale - mWordIterator = null; + getEditor().mWordIterator = null; } /** * @hide */ public WordIterator getWordIterator() { - if (mWordIterator == null) { - mWordIterator = new WordIterator(getTextServicesLocale()); + if (getEditor().mWordIterator == null) { + getEditor().mWordIterator = new WordIterator(getTextServicesLocale()); } - return mWordIterator; + return getEditor().mWordIterator; } private long getCharRange(int offset) { @@ -9209,17 +8181,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return new DragShadowBuilder(shadowView); } - private static class DragLocalState { - public TextView sourceTextView; - public int start, end; - - public DragLocalState(TextView sourceTextView, int start, int end) { - this.sourceTextView = sourceTextView; - this.start = start; - this.end = end; - } - } - @Override public boolean performLongClick() { boolean handled = false; @@ -9230,9 +8191,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } // Long press in empty space moves cursor and shows the Paste affordance if available. - if (!handled && !isPositionOnText(mLastDownPositionX, mLastDownPositionY) && - mInsertionControllerEnabled) { - final int offset = getOffsetForPosition(mLastDownPositionX, mLastDownPositionY); + if (!handled && mEditor != null && !isPositionOnText(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY) && + getEditor().mInsertionControllerEnabled) { + final int offset = getOffsetForPosition(getEditor().mLastDownPositionX, getEditor().mLastDownPositionY); stopSelectionActionMode(); Selection.setSelection((Spannable) mText, offset); getInsertionController().showWithActionPopup(); @@ -9240,7 +8201,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener vibrate = false; } - if (!handled && mSelectionActionMode != null) { + if (!handled && (mEditor == null || getEditor().mSelectionActionMode != null)) { if (touchPositionIsInSelection()) { // Start a drag final int start = getSelectionStart(); @@ -9267,8 +8228,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); } - if (handled) { - mDiscardNextActionUp = true; + if (handled && mEditor != null) { + getEditor().mDiscardNextActionUp = true; } return handled; @@ -9297,10 +8258,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private PositionListener getPositionListener() { - if (mPositionListener == null) { - mPositionListener = new PositionListener(); + if (getEditor().mPositionListener == null) { + getEditor().mPositionListener = new PositionListener(); } - return mPositionListener; + return getEditor().mPositionListener; } private interface TextViewPositionListener { @@ -9308,6 +8269,1420 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener boolean parentPositionChanged, boolean parentScrolled); } + private boolean isPositionVisible(int positionX, int positionY) { + synchronized (TEMP_POSITION) { + final float[] position = TEMP_POSITION; + position[0] = positionX; + position[1] = positionY; + View view = this; + + while (view != null) { + if (view != this) { + // Local scroll is already taken into account in positionX/Y + position[0] -= view.getScrollX(); + position[1] -= view.getScrollY(); + } + + if (position[0] < 0 || position[1] < 0 || + position[0] > view.getWidth() || position[1] > view.getHeight()) { + return false; + } + + if (!view.getMatrix().isIdentity()) { + view.getMatrix().mapPoints(position); + } + + position[0] += view.getLeft(); + position[1] += view.getTop(); + + final ViewParent parent = view.getParent(); + if (parent instanceof View) { + view = (View) parent; + } else { + // We've reached the ViewRoot, stop iterating + view = null; + } + } + } + + // We've been able to walk up the view hierarchy and the position was never clipped + return true; + } + + private boolean isOffsetVisible(int offset) { + final int line = mLayout.getLineForOffset(offset); + final int lineBottom = mLayout.getLineBottom(line); + final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset); + return isPositionVisible(primaryHorizontal + viewportToContentHorizontalOffset(), + lineBottom + viewportToContentVerticalOffset()); + } + + @Override + protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) { + super.onScrollChanged(horiz, vert, oldHoriz, oldVert); + if (mEditor != null && getEditor().mPositionListener != null) { + getEditor().mPositionListener.onScrollChanged(); + } + } + + /** + * Removes the suggestion spans. + */ + CharSequence removeSuggestionSpans(CharSequence text) { + if (text instanceof Spanned) { + Spannable spannable; + if (text instanceof Spannable) { + spannable = (Spannable) text; + } else { + spannable = new SpannableString(text); + text = spannable; + } + + SuggestionSpan[] spans = spannable.getSpans(0, text.length(), SuggestionSpan.class); + for (int i = 0; i < spans.length; i++) { + spannable.removeSpan(spans[i]); + } + } + return text; + } + + void showSuggestions() { + if (getEditor().mSuggestionsPopupWindow == null) { + getEditor().mSuggestionsPopupWindow = new SuggestionsPopupWindow(); + } + hideControllers(); + getEditor().mSuggestionsPopupWindow.show(); + } + + boolean areSuggestionsShown() { + return getEditor().mSuggestionsPopupWindow != null && getEditor().mSuggestionsPopupWindow.isShowing(); + } + + /** + * Return whether or not suggestions are enabled on this TextView. The suggestions are generated + * by the IME or by the spell checker as the user types. This is done by adding + * {@link SuggestionSpan}s to the text. + * + * When suggestions are enabled (default), this list of suggestions will be displayed when the + * user asks for them on these parts of the text. This value depends on the inputType of this + * TextView. + * + * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}. + * + * In addition, the type variation must be one of + * {@link InputType#TYPE_TEXT_VARIATION_NORMAL}, + * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT}, + * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE}, + * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or + * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}. + * + * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set. + * + * @return true if the suggestions popup window is enabled, based on the inputType. + */ + public boolean isSuggestionsEnabled() { + if (mEditor == null) return false; + if ((getEditor().mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false; + if ((getEditor().mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false; + + final int variation = getEditor().mInputType & EditorInfo.TYPE_MASK_VARIATION; + return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL || + variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT || + variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE || + variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE || + variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT); + } + + /** + * If provided, this ActionMode.Callback will be used to create the ActionMode when text + * selection is initiated in this View. + * + * The standard implementation populates the menu with a subset of Select All, Cut, Copy and + * Paste actions, depending on what this View supports. + * + * A custom implementation can add new entries in the default menu in its + * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The + * default actions can also be removed from the menu using {@link Menu#removeItem(int)} and + * passing {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy} + * or {@link android.R.id#paste} ids as parameters. + * + * Returning false from + * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent + * the action mode from being started. + * + * Action click events should be handled by the custom implementation of + * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}. + * + * Note that text selection mode is not started when a TextView receives focus and the + * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in + * that case, to allow for quick replacement. + */ + public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) { + createEditorIfNeeded("custom selection action mode set"); + getEditor().mCustomSelectionActionModeCallback = actionModeCallback; + } + + /** + * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null. + * + * @return The current custom selection callback. + */ + public ActionMode.Callback getCustomSelectionActionModeCallback() { + return mEditor == null ? null : getEditor().mCustomSelectionActionModeCallback; + } + + /** + * + * @return true if the selection mode was actually started. + */ + private boolean startSelectionActionMode() { + if (getEditor().mSelectionActionMode != null) { + // Selection action mode is already started + return false; + } + + if (!canSelectText() || !requestFocus()) { + Log.w(LOG_TAG, "TextView does not support text selection. Action mode cancelled."); + return false; + } + + if (!hasSelection()) { + // There may already be a selection on device rotation + if (!selectCurrentWord()) { + // No word found under cursor or text selection not permitted. + return false; + } + } + + boolean willExtract = extractedTextModeWillBeStarted(); + + // Do not start the action mode when extracted text will show up full screen, which would + // immediately hide the newly created action bar and would be visually distracting. + if (!willExtract) { + ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); + getEditor().mSelectionActionMode = startActionMode(actionModeCallback); + } + + final boolean selectionStarted = getEditor().mSelectionActionMode != null || willExtract; + if (selectionStarted && !isTextSelectable()) { + // Show the IME to be able to replace text, except when selecting non editable text. + final InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + imm.showSoftInput(this, 0, null); + } + } + + return selectionStarted; + } + + private boolean extractedTextModeWillBeStarted() { + if (!(this instanceof ExtractEditText)) { + final InputMethodManager imm = InputMethodManager.peekInstance(); + return imm != null && imm.isFullscreenMode(); + } + return false; + } + + /** + * @hide + */ + protected void stopSelectionActionMode() { + if (getEditor().mSelectionActionMode != null) { + // This will hide the mSelectionModifierCursorController + getEditor().mSelectionActionMode.finish(); + } + } + + /** + * Paste clipboard content between min and max positions. + */ + private void paste(int min, int max) { + ClipboardManager clipboard = + (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = clipboard.getPrimaryClip(); + if (clip != null) { + boolean didFirst = false; + for (int i=0; i<clip.getItemCount(); i++) { + CharSequence paste = clip.getItemAt(i).coerceToText(getContext()); + if (paste != null) { + if (!didFirst) { + long minMax = prepareSpacesAroundPaste(min, max, paste); + min = extractRangeStartFromLong(minMax); + max = extractRangeEndFromLong(minMax); + Selection.setSelection((Spannable) mText, max); + ((Editable) mText).replace(min, max, paste); + didFirst = true; + } else { + ((Editable) mText).insert(getSelectionEnd(), "\n"); + ((Editable) mText).insert(getSelectionEnd(), paste); + } + } + } + stopSelectionActionMode(); + LAST_CUT_OR_COPY_TIME = 0; + } + } + + private void setPrimaryClip(ClipData clip) { + ClipboardManager clipboard = (ClipboardManager) getContext(). + getSystemService(Context.CLIPBOARD_SERVICE); + clipboard.setPrimaryClip(clip); + LAST_CUT_OR_COPY_TIME = SystemClock.uptimeMillis(); + } + + private void hideInsertionPointCursorController() { + // No need to create the controller to hide it. + if (getEditor().mInsertionPointCursorController != null) { + getEditor().mInsertionPointCursorController.hide(); + } + } + + /** + * Hides the insertion controller and stops text selection mode, hiding the selection controller + */ + private void hideControllers() { + hideCursorControllers(); + hideSpanControllers(); + } + + private void hideSpanControllers() { + if (mChangeWatcher != null) { + mChangeWatcher.hideControllers(); + } + } + + private void hideCursorControllers() { + if (getEditor().mSuggestionsPopupWindow != null && !getEditor().mSuggestionsPopupWindow.isShowingUp()) { + // Should be done before hide insertion point controller since it triggers a show of it + getEditor().mSuggestionsPopupWindow.hide(); + } + hideInsertionPointCursorController(); + stopSelectionActionMode(); + } + + /** + * Get the character offset closest to the specified absolute position. A typical use case is to + * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method. + * + * @param x The horizontal absolute position of a point on screen + * @param y The vertical absolute position of a point on screen + * @return the character offset for the character whose position is closest to the specified + * position. Returns -1 if there is no layout. + */ + public int getOffsetForPosition(float x, float y) { + if (getLayout() == null) return -1; + final int line = getLineAtCoordinate(y); + final int offset = getOffsetAtCoordinate(line, x); + return offset; + } + + private float convertToLocalHorizontalCoordinate(float x) { + x -= getTotalPaddingLeft(); + // Clamp the position to inside of the view. + x = Math.max(0.0f, x); + x = Math.min(getWidth() - getTotalPaddingRight() - 1, x); + x += getScrollX(); + return x; + } + + private int getLineAtCoordinate(float y) { + y -= getTotalPaddingTop(); + // Clamp the position to inside of the view. + y = Math.max(0.0f, y); + y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y); + y += getScrollY(); + return getLayout().getLineForVertical((int) y); + } + + private int getOffsetAtCoordinate(int line, float x) { + x = convertToLocalHorizontalCoordinate(x); + return getLayout().getOffsetForHorizontal(line, x); + } + + /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed + * in the view. Returns false when the position is in the empty space of left/right of text. + */ + private boolean isPositionOnText(float x, float y) { + if (getLayout() == null) return false; + + final int line = getLineAtCoordinate(y); + x = convertToLocalHorizontalCoordinate(x); + + if (x < getLayout().getLineLeft(line)) return false; + if (x > getLayout().getLineRight(line)) return false; + return true; + } + + @Override + public boolean onDragEvent(DragEvent event) { + switch (event.getAction()) { + case DragEvent.ACTION_DRAG_STARTED: + return hasInsertionController(); + + case DragEvent.ACTION_DRAG_ENTERED: + TextView.this.requestFocus(); + return true; + + case DragEvent.ACTION_DRAG_LOCATION: + final int offset = getOffsetForPosition(event.getX(), event.getY()); + Selection.setSelection((Spannable)mText, offset); + return true; + + case DragEvent.ACTION_DROP: + onDrop(event); + return true; + + case DragEvent.ACTION_DRAG_ENDED: + case DragEvent.ACTION_DRAG_EXITED: + default: + return true; + } + } + + private void onDrop(DragEvent event) { + StringBuilder content = new StringBuilder(""); + ClipData clipData = event.getClipData(); + final int itemCount = clipData.getItemCount(); + for (int i=0; i < itemCount; i++) { + Item item = clipData.getItemAt(i); + content.append(item.coerceToText(TextView.this.mContext)); + } + + final int offset = getOffsetForPosition(event.getX(), event.getY()); + + Object localState = event.getLocalState(); + DragLocalState dragLocalState = null; + if (localState instanceof DragLocalState) { + dragLocalState = (DragLocalState) localState; + } + boolean dragDropIntoItself = dragLocalState != null && + dragLocalState.sourceTextView == this; + + if (dragDropIntoItself) { + if (offset >= dragLocalState.start && offset < dragLocalState.end) { + // A drop inside the original selection discards the drop. + return; + } + } + + final int originalLength = mText.length(); + long minMax = prepareSpacesAroundPaste(offset, offset, content); + int min = extractRangeStartFromLong(minMax); + int max = extractRangeEndFromLong(minMax); + + Selection.setSelection((Spannable) mText, max); + replaceText_internal(min, max, content); + + if (dragDropIntoItself) { + int dragSourceStart = dragLocalState.start; + int dragSourceEnd = dragLocalState.end; + if (max <= dragSourceStart) { + // Inserting text before selection has shifted positions + final int shift = mText.length() - originalLength; + dragSourceStart += shift; + dragSourceEnd += shift; + } + + // Delete original selection + deleteText_internal(dragSourceStart, dragSourceEnd); + + // Make sure we do not leave two adjacent spaces. + if ((dragSourceStart == 0 || + Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) && + (dragSourceStart == mText.length() || + Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) { + final int pos = dragSourceStart == mText.length() ? + dragSourceStart - 1 : dragSourceStart; + deleteText_internal(pos, pos + 1); + } + } + } + + /** + * @return True if this view supports insertion handles. + */ + boolean hasInsertionController() { + return getEditor().mInsertionControllerEnabled; + } + + /** + * @return True if this view supports selection handles. + */ + boolean hasSelectionController() { + return getEditor().mSelectionControllerEnabled; + } + + InsertionPointCursorController getInsertionController() { + if (!getEditor().mInsertionControllerEnabled) { + return null; + } + + if (getEditor().mInsertionPointCursorController == null) { + getEditor().mInsertionPointCursorController = new InsertionPointCursorController(); + + final ViewTreeObserver observer = getViewTreeObserver(); + observer.addOnTouchModeChangeListener(getEditor().mInsertionPointCursorController); + } + + return getEditor().mInsertionPointCursorController; + } + + SelectionModifierCursorController getSelectionController() { + if (!getEditor().mSelectionControllerEnabled) { + return null; + } + + if (getEditor().mSelectionModifierCursorController == null) { + getEditor().mSelectionModifierCursorController = new SelectionModifierCursorController(); + + final ViewTreeObserver observer = getViewTreeObserver(); + observer.addOnTouchModeChangeListener(getEditor().mSelectionModifierCursorController); + } + + return getEditor().mSelectionModifierCursorController; + } + + boolean isInBatchEditMode() { + if (mEditor == null) return false; + final InputMethodState ims = getEditor().mInputMethodState; + if (ims != null) { + return ims.mBatchEditNesting > 0; + } + return getEditor().mInBatchEditControllers; + } + + @Override + public void onResolveTextDirection() { + if (hasPasswordTransformationMethod()) { + mTextDir = TextDirectionHeuristics.LOCALE; + return; + } + + // Always need to resolve layout direction first + final boolean defaultIsRtl = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL); + + // Now, we can select the heuristic + int textDir = getResolvedTextDirection(); + switch (textDir) { + default: + case TEXT_DIRECTION_FIRST_STRONG: + mTextDir = (defaultIsRtl ? TextDirectionHeuristics.FIRSTSTRONG_RTL : + TextDirectionHeuristics.FIRSTSTRONG_LTR); + break; + case TEXT_DIRECTION_ANY_RTL: + mTextDir = TextDirectionHeuristics.ANYRTL_LTR; + break; + case TEXT_DIRECTION_LTR: + mTextDir = TextDirectionHeuristics.LTR; + break; + case TEXT_DIRECTION_RTL: + mTextDir = TextDirectionHeuristics.RTL; + break; + case TEXT_DIRECTION_LOCALE: + mTextDir = TextDirectionHeuristics.LOCALE; + break; + } + } + + /** + * Subclasses will need to override this method to implement their own way of resolving + * drawables depending on the layout direction. + * + * A call to the super method will be required from the subclasses implementation. + */ + protected void resolveDrawables() { + // No need to resolve twice + if (mResolvedDrawables) { + return; + } + // No drawable to resolve + if (mDrawables == null) { + return; + } + // No relative drawable to resolve + if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) { + mResolvedDrawables = true; + return; + } + + Drawables dr = mDrawables; + switch(getResolvedLayoutDirection()) { + case LAYOUT_DIRECTION_RTL: + if (dr.mDrawableStart != null) { + dr.mDrawableRight = dr.mDrawableStart; + + dr.mDrawableSizeRight = dr.mDrawableSizeStart; + dr.mDrawableHeightRight = dr.mDrawableHeightStart; + } + if (dr.mDrawableEnd != null) { + dr.mDrawableLeft = dr.mDrawableEnd; + + dr.mDrawableSizeLeft = dr.mDrawableSizeEnd; + dr.mDrawableHeightLeft = dr.mDrawableHeightEnd; + } + break; + + case LAYOUT_DIRECTION_LTR: + default: + if (dr.mDrawableStart != null) { + dr.mDrawableLeft = dr.mDrawableStart; + + dr.mDrawableSizeLeft = dr.mDrawableSizeStart; + dr.mDrawableHeightLeft = dr.mDrawableHeightStart; + } + if (dr.mDrawableEnd != null) { + dr.mDrawableRight = dr.mDrawableEnd; + + dr.mDrawableSizeRight = dr.mDrawableSizeEnd; + dr.mDrawableHeightRight = dr.mDrawableHeightEnd; + } + break; + } + mResolvedDrawables = true; + } + + protected void resetResolvedDrawables() { + mResolvedDrawables = false; + } + + /** + * @hide + */ + protected void viewClicked(InputMethodManager imm) { + if (imm != null) { + imm.viewClicked(this); + } + } + + /** + * Deletes the range of text [start, end[. + * @hide + */ + protected void deleteText_internal(int start, int end) { + ((Editable) mText).delete(start, end); + } + + /** + * Replaces the range of text [start, end[ by replacement text + * @hide + */ + protected void replaceText_internal(int start, int end, CharSequence text) { + ((Editable) mText).replace(start, end, text); + } + + /** + * Sets a span on the specified range of text + * @hide + */ + protected void setSpan_internal(Object span, int start, int end, int flags) { + ((Editable) mText).setSpan(span, start, end, flags); + } + + /** + * Moves the cursor to the specified offset position in text + * @hide + */ + protected void setCursorPosition_internal(int start, int end) { + Selection.setSelection(((Editable) mText), start, end); + } + + /** + * An Editor should be created as soon as any of the editable-specific fields (grouped + * inside the Editor object) is assigned to a non-default value. + * This method will create the Editor if needed. + * + * A standard TextView (as well as buttons, checkboxes...) should not qualify and hence will + * have a null Editor, unlike an EditText. Inconsistent in-between states will have an + * Editor for backward compatibility, as soon as one of these fields is assigned. + * + * Also note that for performance reasons, the mEditor is created when needed, but not + * reset when no more edit-specific fields are needed. + */ + private void createEditorIfNeeded(String reason) { + if (mEditor == null) { + if (!(this instanceof EditText)) { + Log.e(LOG_TAG + " EDITOR", "Creating Editor on TextView. " + reason); + } + mEditor = new Editor(); + } else { + if (!(this instanceof EditText)) { + Log.d(LOG_TAG + " EDITOR", "Redundant Editor creation. " + reason); + } + } + } + + private Editor getEditor() { + if (mEditor == null) { + //createEditorIfNeeded("Problem: mEditor is not initialized!"); + Log.e(LOG_TAG, "mEditor not initialized. Please send a bug report to debunne@"); + } + return mEditor; + } + + /** + * User interface state that is stored by TextView for implementing + * {@link View#onSaveInstanceState}. + */ + public static class SavedState extends BaseSavedState { + int selStart; + int selEnd; + CharSequence text; + boolean frozenWithFocus; + CharSequence error; + + SavedState(Parcelable superState) { + super(superState); + } + + @Override + public void writeToParcel(Parcel out, int flags) { + super.writeToParcel(out, flags); + out.writeInt(selStart); + out.writeInt(selEnd); + out.writeInt(frozenWithFocus ? 1 : 0); + TextUtils.writeToParcel(text, out, flags); + + if (error == null) { + out.writeInt(0); + } else { + out.writeInt(1); + TextUtils.writeToParcel(error, out, flags); + } + } + + @Override + public String toString() { + String str = "TextView.SavedState{" + + Integer.toHexString(System.identityHashCode(this)) + + " start=" + selStart + " end=" + selEnd; + if (text != null) { + str += " text=" + text; + } + return str + "}"; + } + + @SuppressWarnings("hiding") + public static final Parcelable.Creator<SavedState> CREATOR + = new Parcelable.Creator<SavedState>() { + public SavedState createFromParcel(Parcel in) { + return new SavedState(in); + } + + public SavedState[] newArray(int size) { + return new SavedState[size]; + } + }; + + private SavedState(Parcel in) { + super(in); + selStart = in.readInt(); + selEnd = in.readInt(); + frozenWithFocus = (in.readInt() != 0); + text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + + if (in.readInt() != 0) { + error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); + } + } + } + + private static class CharWrapper implements CharSequence, GetChars, GraphicsOperations { + private char[] mChars; + private int mStart, mLength; + + public CharWrapper(char[] chars, int start, int len) { + mChars = chars; + mStart = start; + mLength = len; + } + + /* package */ void set(char[] chars, int start, int len) { + mChars = chars; + mStart = start; + mLength = len; + } + + public int length() { + return mLength; + } + + public char charAt(int off) { + return mChars[off + mStart]; + } + + @Override + public String toString() { + return new String(mChars, mStart, mLength); + } + + public CharSequence subSequence(int start, int end) { + if (start < 0 || end < 0 || start > mLength || end > mLength) { + throw new IndexOutOfBoundsException(start + ", " + end); + } + + return new String(mChars, start + mStart, end - start); + } + + public void getChars(int start, int end, char[] buf, int off) { + if (start < 0 || end < 0 || start > mLength || end > mLength) { + throw new IndexOutOfBoundsException(start + ", " + end); + } + + System.arraycopy(mChars, start + mStart, buf, off, end - start); + } + + public void drawText(Canvas c, int start, int end, + float x, float y, Paint p) { + c.drawText(mChars, start + mStart, end - start, x, y, p); + } + + public void drawTextRun(Canvas c, int start, int end, + int contextStart, int contextEnd, float x, float y, int flags, Paint p) { + int count = end - start; + int contextCount = contextEnd - contextStart; + c.drawTextRun(mChars, start + mStart, count, contextStart + mStart, + contextCount, x, y, flags, p); + } + + public float measureText(int start, int end, Paint p) { + return p.measureText(mChars, start + mStart, end - start); + } + + public int getTextWidths(int start, int end, float[] widths, Paint p) { + return p.getTextWidths(mChars, start + mStart, end - start, widths); + } + + public float getTextRunAdvances(int start, int end, int contextStart, + int contextEnd, int flags, float[] advances, int advancesIndex, + Paint p) { + int count = end - start; + int contextCount = contextEnd - contextStart; + return p.getTextRunAdvances(mChars, start + mStart, count, + contextStart + mStart, contextCount, flags, advances, + advancesIndex); + } + + public float getTextRunAdvances(int start, int end, int contextStart, + int contextEnd, int flags, float[] advances, int advancesIndex, + Paint p, int reserved) { + int count = end - start; + int contextCount = contextEnd - contextStart; + return p.getTextRunAdvances(mChars, start + mStart, count, + contextStart + mStart, contextCount, flags, advances, + advancesIndex, reserved); + } + + public int getTextRunCursor(int contextStart, int contextEnd, int flags, + int offset, int cursorOpt, Paint p) { + int contextCount = contextEnd - contextStart; + return p.getTextRunCursor(mChars, contextStart + mStart, + contextCount, flags, offset + mStart, cursorOpt); + } + } + + private static class ErrorPopup extends PopupWindow { + private boolean mAbove = false; + private final TextView mView; + private int mPopupInlineErrorBackgroundId = 0; + private int mPopupInlineErrorAboveBackgroundId = 0; + + ErrorPopup(TextView v, int width, int height) { + super(v, width, height); + mView = v; + // Make sure the TextView has a background set as it will be used the first time it is + // shown and positionned. Initialized with below background, which should have + // dimensions identical to the above version for this to work (and is more likely). + mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId, + com.android.internal.R.styleable.Theme_errorMessageBackground); + mView.setBackgroundResource(mPopupInlineErrorBackgroundId); + } + + void fixDirection(boolean above) { + mAbove = above; + + if (above) { + mPopupInlineErrorAboveBackgroundId = + getResourceId(mPopupInlineErrorAboveBackgroundId, + com.android.internal.R.styleable.Theme_errorMessageAboveBackground); + } else { + mPopupInlineErrorBackgroundId = getResourceId(mPopupInlineErrorBackgroundId, + com.android.internal.R.styleable.Theme_errorMessageBackground); + } + + mView.setBackgroundResource(above ? mPopupInlineErrorAboveBackgroundId : + mPopupInlineErrorBackgroundId); + } + + private int getResourceId(int currentId, int index) { + if (currentId == 0) { + TypedArray styledAttributes = mView.getContext().obtainStyledAttributes( + R.styleable.Theme); + currentId = styledAttributes.getResourceId(index, 0); + styledAttributes.recycle(); + } + return currentId; + } + + @Override + public void update(int x, int y, int w, int h, boolean force) { + super.update(x, y, w, h, force); + + boolean above = isAboveAnchor(); + if (above != mAbove) { + fixDirection(above); + } + } + } + + private class CorrectionHighlighter { + private final Path mPath = new Path(); + private final Paint mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + private int mStart, mEnd; + private long mFadingStartTime; + private final static int FADE_OUT_DURATION = 400; + + public CorrectionHighlighter() { + mPaint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); + mPaint.setStyle(Paint.Style.FILL); + } + + public void highlight(CorrectionInfo info) { + mStart = info.getOffset(); + mEnd = mStart + info.getNewText().length(); + mFadingStartTime = SystemClock.uptimeMillis(); + + if (mStart < 0 || mEnd < 0) { + stopAnimation(); + } + } + + public void draw(Canvas canvas, int cursorOffsetVertical) { + if (updatePath() && updatePaint()) { + if (cursorOffsetVertical != 0) { + canvas.translate(0, cursorOffsetVertical); + } + + canvas.drawPath(mPath, mPaint); + + if (cursorOffsetVertical != 0) { + canvas.translate(0, -cursorOffsetVertical); + } + invalidate(true); // TODO invalidate cursor region only + } else { + stopAnimation(); + invalidate(false); // TODO invalidate cursor region only + } + } + + private boolean updatePaint() { + final long duration = SystemClock.uptimeMillis() - mFadingStartTime; + if (duration > FADE_OUT_DURATION) return false; + + final float coef = 1.0f - (float) duration / FADE_OUT_DURATION; + final int highlightColorAlpha = Color.alpha(mHighlightColor); + final int color = (mHighlightColor & 0x00FFFFFF) + + ((int) (highlightColorAlpha * coef) << 24); + mPaint.setColor(color); + return true; + } + + private boolean updatePath() { + final Layout layout = TextView.this.mLayout; + if (layout == null) return false; + + // Update in case text is edited while the animation is run + final int length = mText.length(); + int start = Math.min(length, mStart); + int end = Math.min(length, mEnd); + + mPath.reset(); + TextView.this.mLayout.getSelectionPath(start, end, mPath); + return true; + } + + private void invalidate(boolean delayed) { + if (TextView.this.mLayout == null) return; + + synchronized (TEMP_RECTF) { + mPath.computeBounds(TEMP_RECTF, false); + + int left = getCompoundPaddingLeft(); + int top = getExtendedPaddingTop() + getVerticalOffset(true); + + if (delayed) { + TextView.this.postInvalidateDelayed(16, // 60 Hz update + left + (int) TEMP_RECTF.left, top + (int) TEMP_RECTF.top, + left + (int) TEMP_RECTF.right, top + (int) TEMP_RECTF.bottom); + } else { + TextView.this.postInvalidate((int) TEMP_RECTF.left, (int) TEMP_RECTF.top, + (int) TEMP_RECTF.right, (int) TEMP_RECTF.bottom); + } + } + } + + private void stopAnimation() { + TextView.this.getEditor().mCorrectionHighlighter = null; + } + } + + private static final class Marquee extends Handler { + // TODO: Add an option to configure this + private static final float MARQUEE_DELTA_MAX = 0.07f; + private static final int MARQUEE_DELAY = 1200; + private static final int MARQUEE_RESTART_DELAY = 1200; + private static final int MARQUEE_RESOLUTION = 1000 / 30; + private static final int MARQUEE_PIXELS_PER_SECOND = 30; + + private static final byte MARQUEE_STOPPED = 0x0; + private static final byte MARQUEE_STARTING = 0x1; + private static final byte MARQUEE_RUNNING = 0x2; + + private static final int MESSAGE_START = 0x1; + private static final int MESSAGE_TICK = 0x2; + private static final int MESSAGE_RESTART = 0x3; + + private final WeakReference<TextView> mView; + + private byte mStatus = MARQUEE_STOPPED; + private final float mScrollUnit; + private float mMaxScroll; + float mMaxFadeScroll; + private float mGhostStart; + private float mGhostOffset; + private float mFadeStop; + private int mRepeatLimit; + + float mScroll; + + Marquee(TextView v) { + final float density = v.getContext().getResources().getDisplayMetrics().density; + mScrollUnit = (MARQUEE_PIXELS_PER_SECOND * density) / MARQUEE_RESOLUTION; + mView = new WeakReference<TextView>(v); + } + + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case MESSAGE_START: + mStatus = MARQUEE_RUNNING; + tick(); + break; + case MESSAGE_TICK: + tick(); + break; + case MESSAGE_RESTART: + if (mStatus == MARQUEE_RUNNING) { + if (mRepeatLimit >= 0) { + mRepeatLimit--; + } + start(mRepeatLimit); + } + break; + } + } + + void tick() { + if (mStatus != MARQUEE_RUNNING) { + return; + } + + removeMessages(MESSAGE_TICK); + + final TextView textView = mView.get(); + if (textView != null && (textView.isFocused() || textView.isSelected())) { + mScroll += mScrollUnit; + if (mScroll > mMaxScroll) { + mScroll = mMaxScroll; + sendEmptyMessageDelayed(MESSAGE_RESTART, MARQUEE_RESTART_DELAY); + } else { + sendEmptyMessageDelayed(MESSAGE_TICK, MARQUEE_RESOLUTION); + } + textView.invalidate(); + } + } + + void stop() { + mStatus = MARQUEE_STOPPED; + removeMessages(MESSAGE_START); + removeMessages(MESSAGE_RESTART); + removeMessages(MESSAGE_TICK); + resetScroll(); + } + + private void resetScroll() { + mScroll = 0.0f; + final TextView textView = mView.get(); + if (textView != null) textView.invalidate(); + } + + void start(int repeatLimit) { + if (repeatLimit == 0) { + stop(); + return; + } + mRepeatLimit = repeatLimit; + final TextView textView = mView.get(); + if (textView != null && textView.mLayout != null) { + mStatus = MARQUEE_STARTING; + mScroll = 0.0f; + final int textWidth = textView.getWidth() - textView.getCompoundPaddingLeft() - + textView.getCompoundPaddingRight(); + final float lineWidth = textView.mLayout.getLineWidth(0); + final float gap = textWidth / 3.0f; + mGhostStart = lineWidth - textWidth + gap; + mMaxScroll = mGhostStart + textWidth; + mGhostOffset = lineWidth + gap; + mFadeStop = lineWidth + textWidth / 6.0f; + mMaxFadeScroll = mGhostStart + lineWidth + lineWidth; + + textView.invalidate(); + sendEmptyMessageDelayed(MESSAGE_START, MARQUEE_DELAY); + } + } + + float getGhostOffset() { + return mGhostOffset; + } + + boolean shouldDrawLeftFade() { + return mScroll <= mFadeStop; + } + + boolean shouldDrawGhost() { + return mStatus == MARQUEE_RUNNING && mScroll > mGhostStart; + } + + boolean isRunning() { + return mStatus == MARQUEE_RUNNING; + } + + boolean isStopped() { + return mStatus == MARQUEE_STOPPED; + } + } + + /** + * Controls the {@link EasyEditSpan} monitoring when it is added, and when the related + * pop-up should be displayed. + */ + private class EasyEditSpanController { + + private static final int DISPLAY_TIMEOUT_MS = 3000; // 3 secs + + private EasyEditPopupWindow mPopupWindow; + + private EasyEditSpan mEasyEditSpan; + + private Runnable mHidePopup; + + private void hide() { + if (mPopupWindow != null) { + mPopupWindow.hide(); + TextView.this.removeCallbacks(mHidePopup); + } + removeSpans(mText); + mEasyEditSpan = null; + } + + /** + * Monitors the changes in the text. + * + * <p>{@link ChangeWatcher#onSpanAdded(Spannable, Object, int, int)} cannot be used, + * as the notifications are not sent when a spannable (with spans) is inserted. + */ + public void onTextChange(CharSequence buffer) { + adjustSpans(mText); + + if (getWindowVisibility() != View.VISIBLE) { + // The window is not visible yet, ignore the text change. + return; + } + + if (mLayout == null) { + // The view has not been layout yet, ignore the text change + return; + } + + InputMethodManager imm = InputMethodManager.peekInstance(); + if (!(TextView.this instanceof ExtractEditText) + && imm != null && imm.isFullscreenMode()) { + // The input is in extract mode. We do not have to handle the easy edit in the + // original TextView, as the ExtractEditText will do + return; + } + + // Remove the current easy edit span, as the text changed, and remove the pop-up + // (if any) + if (mEasyEditSpan != null) { + if (mText instanceof Spannable) { + ((Spannable) mText).removeSpan(mEasyEditSpan); + } + mEasyEditSpan = null; + } + if (mPopupWindow != null && mPopupWindow.isShowing()) { + mPopupWindow.hide(); + } + + // Display the new easy edit span (if any). + if (buffer instanceof Spanned) { + mEasyEditSpan = getSpan((Spanned) buffer); + if (mEasyEditSpan != null) { + if (mPopupWindow == null) { + mPopupWindow = new EasyEditPopupWindow(); + mHidePopup = new Runnable() { + @Override + public void run() { + hide(); + } + }; + } + mPopupWindow.show(mEasyEditSpan); + TextView.this.removeCallbacks(mHidePopup); + TextView.this.postDelayed(mHidePopup, DISPLAY_TIMEOUT_MS); + } + } + } + + /** + * Adjusts the spans by removing all of them except the last one. + */ + private void adjustSpans(CharSequence buffer) { + // This method enforces that only one easy edit span is attached to the text. + // A better way to enforce this would be to listen for onSpanAdded, but this method + // cannot be used in this scenario as no notification is triggered when a text with + // spans is inserted into a text. + if (buffer instanceof Spannable) { + Spannable spannable = (Spannable) buffer; + EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(), + EasyEditSpan.class); + for (int i = 0; i < spans.length - 1; i++) { + spannable.removeSpan(spans[i]); + } + } + } + + /** + * Removes all the {@link EasyEditSpan} currently attached. + */ + private void removeSpans(CharSequence buffer) { + if (buffer instanceof Spannable) { + Spannable spannable = (Spannable) buffer; + EasyEditSpan[] spans = spannable.getSpans(0, spannable.length(), + EasyEditSpan.class); + for (int i = 0; i < spans.length; i++) { + spannable.removeSpan(spans[i]); + } + } + } + + private EasyEditSpan getSpan(Spanned spanned) { + EasyEditSpan[] easyEditSpans = spanned.getSpans(0, spanned.length(), + EasyEditSpan.class); + if (easyEditSpans.length == 0) { + return null; + } else { + return easyEditSpans[0]; + } + } + } + + /** + * Displays the actions associated to an {@link EasyEditSpan}. The pop-up is controlled + * by {@link EasyEditSpanController}. + */ + private class EasyEditPopupWindow extends PinnedPopupWindow + implements OnClickListener { + private static final int POPUP_TEXT_LAYOUT = + com.android.internal.R.layout.text_edit_action_popup_text; + private TextView mDeleteTextView; + private EasyEditSpan mEasyEditSpan; + + @Override + protected void createPopupWindow() { + mPopupWindow = new PopupWindow(TextView.this.mContext, null, + com.android.internal.R.attr.textSelectHandleWindowStyle); + mPopupWindow.setInputMethodMode(PopupWindow.INPUT_METHOD_NOT_NEEDED); + mPopupWindow.setClippingEnabled(true); + } + + @Override + protected void initContentView() { + LinearLayout linearLayout = new LinearLayout(TextView.this.getContext()); + linearLayout.setOrientation(LinearLayout.HORIZONTAL); + mContentView = linearLayout; + mContentView.setBackgroundResource( + com.android.internal.R.drawable.text_edit_side_paste_window); + + LayoutInflater inflater = (LayoutInflater)TextView.this.mContext. + getSystemService(Context.LAYOUT_INFLATER_SERVICE); + + LayoutParams wrapContent = new LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); + + mDeleteTextView = (TextView) inflater.inflate(POPUP_TEXT_LAYOUT, null); + mDeleteTextView.setLayoutParams(wrapContent); + mDeleteTextView.setText(com.android.internal.R.string.delete); + mDeleteTextView.setOnClickListener(this); + mContentView.addView(mDeleteTextView); + } + + public void show(EasyEditSpan easyEditSpan) { + mEasyEditSpan = easyEditSpan; + super.show(); + } + + @Override + public void onClick(View view) { + if (view == mDeleteTextView) { + Editable editable = (Editable) mText; + int start = editable.getSpanStart(mEasyEditSpan); + int end = editable.getSpanEnd(mEasyEditSpan); + if (start >= 0 && end >= 0) { + deleteText_internal(start, end); + } + } + } + + @Override + protected int getTextOffset() { + // Place the pop-up at the end of the span + Editable editable = (Editable) mText; + return editable.getSpanEnd(mEasyEditSpan); + } + + @Override + protected int getVerticalLocalPosition(int line) { + return mLayout.getLineBottom(line); + } + + @Override + protected int clipVertically(int positionY) { + // As we display the pop-up below the span, no vertical clipping is required. + return positionY; + } + } + + private class ChangeWatcher implements TextWatcher, SpanWatcher { + + private CharSequence mBeforeText; + + private EasyEditSpanController mEasyEditSpanController; + + private ChangeWatcher() { + mEasyEditSpanController = new EasyEditSpanController(); + } + + public void beforeTextChanged(CharSequence buffer, int start, + int before, int after) { + if (DEBUG_EXTRACT) Log.v(LOG_TAG, "beforeTextChanged start=" + start + + " before=" + before + " after=" + after + ": " + buffer); + + if (AccessibilityManager.getInstance(mContext).isEnabled() + && !isPasswordInputType(getInputType()) + && !hasPasswordTransformationMethod()) { + mBeforeText = buffer.toString(); + } + + TextView.this.sendBeforeTextChanged(buffer, start, before, after); + } + + public void onTextChanged(CharSequence buffer, int start, + int before, int after) { + if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onTextChanged start=" + start + + " before=" + before + " after=" + after + ": " + buffer); + TextView.this.handleTextChanged(buffer, start, before, after); + + mEasyEditSpanController.onTextChange(buffer); + + if (AccessibilityManager.getInstance(mContext).isEnabled() && + (isFocused() || isSelected() && isShown())) { + sendAccessibilityEventTypeViewTextChanged(mBeforeText, start, before, after); + mBeforeText = null; + } + } + + public void afterTextChanged(Editable buffer) { + if (DEBUG_EXTRACT) Log.v(LOG_TAG, "afterTextChanged: " + buffer); + TextView.this.sendAfterTextChanged(buffer); + + if (MetaKeyKeyListener.getMetaState(buffer, MetaKeyKeyListener.META_SELECTING) != 0) { + MetaKeyKeyListener.stopSelecting(TextView.this, buffer); + } + } + + public void onSpanChanged(Spannable buf, + Object what, int s, int e, int st, int en) { + if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanChanged s=" + s + " e=" + e + + " st=" + st + " en=" + en + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, s, st, e, en); + } + + public void onSpanAdded(Spannable buf, Object what, int s, int e) { + if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanAdded s=" + s + " e=" + e + + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, -1, s, -1, e); + } + + public void onSpanRemoved(Spannable buf, Object what, int s, int e) { + if (DEBUG_EXTRACT) Log.v(LOG_TAG, "onSpanRemoved s=" + s + " e=" + e + + " what=" + what + ": " + buf); + TextView.this.spanChange(buf, what, s, -1, e, -1); + } + + private void hideControllers() { + mEasyEditSpanController.hide(); + } + } + + private static class Blink extends Handler implements Runnable { + private final WeakReference<TextView> mView; + private boolean mCancelled; + + public Blink(TextView v) { + mView = new WeakReference<TextView>(v); + } + + public void run() { + if (mCancelled) { + return; + } + + removeCallbacks(Blink.this); + + TextView tv = mView.get(); + + if (tv != null && tv.shouldBlink()) { + if (tv.mLayout != null) { + tv.invalidateCursorPath(); + } + + postAtTime(this, SystemClock.uptimeMillis() + BLINK); + } + } + + void cancel() { + if (!mCancelled) { + removeCallbacks(Blink.this); + mCancelled = true; + } + } + + void uncancel() { + mCancelled = false; + } + } + + private static class DragLocalState { + public TextView sourceTextView; + public int start, end; + + public DragLocalState(TextView sourceTextView, int start, int end) { + this.sourceTextView = sourceTextView; + this.start = start; + this.end = end; + } + } + private class PositionListener implements ViewTreeObserver.OnPreDrawListener { // 3 handles // 3 ActionPopup [replace, suggestion, easyedit] (suggestionsPopup first hides the others) @@ -9320,6 +9695,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private int mPositionX, mPositionY; private int mNumberOfListeners; private boolean mScrollHasChanged; + final int[] mTempCoords = new int[2]; public void addSubscriber(TextViewPositionListener positionListener, boolean canMove) { if (mNumberOfListeners == 0) { @@ -9398,62 +9774,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private boolean isPositionVisible(int positionX, int positionY) { - synchronized (sTmpPosition) { - final float[] position = sTmpPosition; - position[0] = positionX; - position[1] = positionY; - View view = this; - - while (view != null) { - if (view != this) { - // Local scroll is already taken into account in positionX/Y - position[0] -= view.getScrollX(); - position[1] -= view.getScrollY(); - } - - if (position[0] < 0 || position[1] < 0 || - position[0] > view.getWidth() || position[1] > view.getHeight()) { - return false; - } - - if (!view.getMatrix().isIdentity()) { - view.getMatrix().mapPoints(position); - } - - position[0] += view.getLeft(); - position[1] += view.getTop(); - - final ViewParent parent = view.getParent(); - if (parent instanceof View) { - view = (View) parent; - } else { - // We've reached the ViewRoot, stop iterating - view = null; - } - } - } - - // We've been able to walk up the view hierarchy and the position was never clipped - return true; - } - - private boolean isOffsetVisible(int offset) { - final int line = mLayout.getLineForOffset(offset); - final int lineBottom = mLayout.getLineBottom(line); - final int primaryHorizontal = (int) mLayout.getPrimaryHorizontal(offset); - return isPositionVisible(primaryHorizontal + viewportToContentHorizontalOffset(), - lineBottom + viewportToContentVerticalOffset()); - } - - @Override - protected void onScrollChanged(int horiz, int vert, int oldHoriz, int oldVert) { - super.onScrollChanged(horiz, vert, oldHoriz, oldVert); - if (mPositionListener != null) { - mPositionListener.onScrollChanged(); - } - } - private abstract class PinnedPopupWindow implements TextViewPositionListener { protected PopupWindow mPopupWindow; protected ViewGroup mContentView; @@ -9581,7 +9901,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener TextView.this.getPositionListener().removeSubscriber(SuggestionsPopupWindow.this); // Safe cast since show() checks that mText is an Editable - ((Spannable) mText).removeSpan(mSuggestionRangeSpan); + ((Spannable) mText).removeSpan(getEditor().mSuggestionRangeSpan); setCursorVisible(mCursorWasVisibleBeforeSuggestions); if (hasInsertionController()) { @@ -9591,7 +9911,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } public SuggestionsPopupWindow() { - mCursorWasVisibleBeforeSuggestions = mCursorVisible; + mCursorWasVisibleBeforeSuggestions = getEditor().mCursorVisible; mSuggestionSpanComparator = new SuggestionSpanComparator(); mSpansLengths = new HashMap<SuggestionSpan, Integer>(); } @@ -9729,7 +10049,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (!(mText instanceof Editable)) return; if (updateSuggestions()) { - mCursorWasVisibleBeforeSuggestions = mCursorVisible; + mCursorWasVisibleBeforeSuggestions = getEditor().mCursorVisible; setCursorVisible(false); mIsShowingUp = true; super.show(); @@ -9884,17 +10204,17 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mNumberOfSuggestions++; - if (mSuggestionRangeSpan == null) mSuggestionRangeSpan = new SuggestionRangeSpan(); + if (getEditor().mSuggestionRangeSpan == null) getEditor().mSuggestionRangeSpan = new SuggestionRangeSpan(); if (underlineColor == 0) { // Fallback on the default highlight color when the first span does not provide one - mSuggestionRangeSpan.setBackgroundColor(mHighlightColor); + getEditor().mSuggestionRangeSpan.setBackgroundColor(mHighlightColor); } else { final float BACKGROUND_TRANSPARENCY = 0.4f; final int newAlpha = (int) (Color.alpha(underlineColor) * BACKGROUND_TRANSPARENCY); - mSuggestionRangeSpan.setBackgroundColor( + getEditor().mSuggestionRangeSpan.setBackgroundColor( (underlineColor & 0x00FFFFFF) + (newAlpha << 24)); } - spannable.setSpan(mSuggestionRangeSpan, spanUnionStart, spanUnionEnd, + spannable.setSpan(getEditor().mSuggestionRangeSpan, spanUnionStart, spanUnionEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); mSuggestionsAdapter.notifyDataSetChanged(); @@ -9927,8 +10247,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener SuggestionInfo suggestionInfo = mSuggestionInfos[position]; if (suggestionInfo.suggestionIndex == DELETE_TEXT) { - final int spanUnionStart = editable.getSpanStart(mSuggestionRangeSpan); - int spanUnionEnd = editable.getSpanEnd(mSuggestionRangeSpan); + final int spanUnionStart = editable.getSpanStart(getEditor().mSuggestionRangeSpan); + int spanUnionEnd = editable.getSpanEnd(getEditor().mSuggestionRangeSpan); if (spanUnionStart >= 0 && spanUnionEnd > spanUnionStart) { // Do not leave two adjacent spaces after deletion, or one at beginning of text if (spanUnionEnd < editable.length() && @@ -10028,209 +10348,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } /** - * Removes the suggestion spans. - */ - CharSequence removeSuggestionSpans(CharSequence text) { - if (text instanceof Spanned) { - Spannable spannable; - if (text instanceof Spannable) { - spannable = (Spannable) text; - } else { - spannable = new SpannableString(text); - text = spannable; - } - - SuggestionSpan[] spans = spannable.getSpans(0, text.length(), SuggestionSpan.class); - for (int i = 0; i < spans.length; i++) { - spannable.removeSpan(spans[i]); - } - } - return text; - } - - void showSuggestions() { - if (mSuggestionsPopupWindow == null) { - mSuggestionsPopupWindow = new SuggestionsPopupWindow(); - } - hideControllers(); - mSuggestionsPopupWindow.show(); - } - - boolean areSuggestionsShown() { - return mSuggestionsPopupWindow != null && mSuggestionsPopupWindow.isShowing(); - } - - /** - * Return whether or not suggestions are enabled on this TextView. The suggestions are generated - * by the IME or by the spell checker as the user types. This is done by adding - * {@link SuggestionSpan}s to the text. - * - * When suggestions are enabled (default), this list of suggestions will be displayed when the - * user asks for them on these parts of the text. This value depends on the inputType of this - * TextView. - * - * The class of the input type must be {@link InputType#TYPE_CLASS_TEXT}. - * - * In addition, the type variation must be one of - * {@link InputType#TYPE_TEXT_VARIATION_NORMAL}, - * {@link InputType#TYPE_TEXT_VARIATION_EMAIL_SUBJECT}, - * {@link InputType#TYPE_TEXT_VARIATION_LONG_MESSAGE}, - * {@link InputType#TYPE_TEXT_VARIATION_SHORT_MESSAGE} or - * {@link InputType#TYPE_TEXT_VARIATION_WEB_EDIT_TEXT}. - * - * And finally, the {@link InputType#TYPE_TEXT_FLAG_NO_SUGGESTIONS} flag must <i>not</i> be set. - * - * @return true if the suggestions popup window is enabled, based on the inputType. - */ - public boolean isSuggestionsEnabled() { - if ((mInputType & InputType.TYPE_MASK_CLASS) != InputType.TYPE_CLASS_TEXT) return false; - if ((mInputType & InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS) > 0) return false; - - final int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; - return (variation == EditorInfo.TYPE_TEXT_VARIATION_NORMAL || - variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT || - variation == EditorInfo.TYPE_TEXT_VARIATION_LONG_MESSAGE || - variation == EditorInfo.TYPE_TEXT_VARIATION_SHORT_MESSAGE || - variation == EditorInfo.TYPE_TEXT_VARIATION_WEB_EDIT_TEXT); - } - - /** - * If provided, this ActionMode.Callback will be used to create the ActionMode when text - * selection is initiated in this View. - * - * The standard implementation populates the menu with a subset of Select All, Cut, Copy and - * Paste actions, depending on what this View supports. - * - * A custom implementation can add new entries in the default menu in its - * {@link android.view.ActionMode.Callback#onPrepareActionMode(ActionMode, Menu)} method. The - * default actions can also be removed from the menu using {@link Menu#removeItem(int)} and - * passing {@link android.R.id#selectAll}, {@link android.R.id#cut}, {@link android.R.id#copy} - * or {@link android.R.id#paste} ids as parameters. - * - * Returning false from - * {@link android.view.ActionMode.Callback#onCreateActionMode(ActionMode, Menu)} will prevent - * the action mode from being started. - * - * Action click events should be handled by the custom implementation of - * {@link android.view.ActionMode.Callback#onActionItemClicked(ActionMode, MenuItem)}. - * - * Note that text selection mode is not started when a TextView receives focus and the - * {@link android.R.attr#selectAllOnFocus} flag has been set. The content is highlighted in - * that case, to allow for quick replacement. - */ - public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) { - mCustomSelectionActionModeCallback = actionModeCallback; - } - - /** - * Retrieves the value set in {@link #setCustomSelectionActionModeCallback}. Default is null. - * - * @return The current custom selection callback. - */ - public ActionMode.Callback getCustomSelectionActionModeCallback() { - return mCustomSelectionActionModeCallback; - } - - /** - * - * @return true if the selection mode was actually started. - */ - private boolean startSelectionActionMode() { - if (mSelectionActionMode != null) { - // Selection action mode is already started - return false; - } - - if (!canSelectText() || !requestFocus()) { - Log.w(LOG_TAG, "TextView does not support text selection. Action mode cancelled."); - return false; - } - - if (!hasSelection()) { - // There may already be a selection on device rotation - if (!selectCurrentWord()) { - // No word found under cursor or text selection not permitted. - return false; - } - } - - boolean willExtract = extractedTextModeWillBeStarted(); - - // Do not start the action mode when extracted text will show up full screen, which would - // immediately hide the newly created action bar and would be visually distracting. - if (!willExtract) { - ActionMode.Callback actionModeCallback = new SelectionActionModeCallback(); - mSelectionActionMode = startActionMode(actionModeCallback); - } - - final boolean selectionStarted = mSelectionActionMode != null || willExtract; - if (selectionStarted && !mTextIsSelectable) { - // Show the IME to be able to replace text, except when selecting non editable text. - final InputMethodManager imm = InputMethodManager.peekInstance(); - if (imm != null) { - imm.showSoftInput(this, 0, null); - } - } - - return selectionStarted; - } - - private boolean extractedTextModeWillBeStarted() { - if (!(this instanceof ExtractEditText)) { - final InputMethodManager imm = InputMethodManager.peekInstance(); - return imm != null && imm.isFullscreenMode(); - } - return false; - } - - /** - * @hide - */ - protected void stopSelectionActionMode() { - if (mSelectionActionMode != null) { - // This will hide the mSelectionModifierCursorController - mSelectionActionMode.finish(); - } - } - - /** - * Paste clipboard content between min and max positions. - */ - private void paste(int min, int max) { - ClipboardManager clipboard = - (ClipboardManager) getContext().getSystemService(Context.CLIPBOARD_SERVICE); - ClipData clip = clipboard.getPrimaryClip(); - if (clip != null) { - boolean didFirst = false; - for (int i=0; i<clip.getItemCount(); i++) { - CharSequence paste = clip.getItemAt(i).coerceToText(getContext()); - if (paste != null) { - if (!didFirst) { - long minMax = prepareSpacesAroundPaste(min, max, paste); - min = extractRangeStartFromLong(minMax); - max = extractRangeEndFromLong(minMax); - Selection.setSelection((Spannable) mText, max); - ((Editable) mText).replace(min, max, paste); - didFirst = true; - } else { - ((Editable) mText).insert(getSelectionEnd(), "\n"); - ((Editable) mText).insert(getSelectionEnd(), paste); - } - } - } - stopSelectionActionMode(); - sLastCutOrCopyTime = 0; - } - } - - private void setPrimaryClip(ClipData clip) { - ClipboardManager clipboard = (ClipboardManager) getContext(). - getSystemService(Context.CLIPBOARD_SERVICE); - clipboard.setPrimaryClip(clip); - sLastCutOrCopyTime = SystemClock.uptimeMillis(); - } - - /** * An ActionMode Callback class that is used to provide actions while in text selection mode. * * The default callback provides a subset of Select All, Cut, Copy and Paste actions, depending @@ -10292,8 +10409,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener styledAttributes.recycle(); - if (mCustomSelectionActionModeCallback != null) { - if (!mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) { + if (getEditor().mCustomSelectionActionModeCallback != null) { + if (!getEditor().mCustomSelectionActionModeCallback.onCreateActionMode(mode, menu)) { // The custom mode can choose to cancel the action mode return false; } @@ -10309,16 +10426,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public boolean onPrepareActionMode(ActionMode mode, Menu menu) { - if (mCustomSelectionActionModeCallback != null) { - return mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu); + if (getEditor().mCustomSelectionActionModeCallback != null) { + return getEditor().mCustomSelectionActionModeCallback.onPrepareActionMode(mode, menu); } return true; } @Override public boolean onActionItemClicked(ActionMode mode, MenuItem item) { - if (mCustomSelectionActionModeCallback != null && - mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { + if (getEditor().mCustomSelectionActionModeCallback != null && + getEditor().mCustomSelectionActionModeCallback.onActionItemClicked(mode, item)) { return true; } return onTextContextMenuItem(item.getItemId()); @@ -10326,16 +10443,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener @Override public void onDestroyActionMode(ActionMode mode) { - if (mCustomSelectionActionModeCallback != null) { - mCustomSelectionActionModeCallback.onDestroyActionMode(mode); + if (getEditor().mCustomSelectionActionModeCallback != null) { + getEditor().mCustomSelectionActionModeCallback.onDestroyActionMode(mode); } Selection.setSelection((Spannable) mText, getSelectionEnd()); - if (mSelectionModifierCursorController != null) { - mSelectionModifierCursorController.hide(); + if (getEditor().mSelectionModifierCursorController != null) { + getEditor().mSelectionModifierCursorController.hide(); } - mSelectionActionMode = null; + getEditor().mSelectionActionMode = null; } } @@ -10349,7 +10466,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener protected void createPopupWindow() { mPopupWindow = new PopupWindow(TextView.this.mContext, null, com.android.internal.R.attr.textSelectHandleWindowStyle); - mPopupWindow.setClippingEnabled(true); + mPopupWindow.setClippingEnabled(true); } @Override @@ -10749,7 +10866,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener public void show() { super.show(); - final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - sLastCutOrCopyTime; + final long durationSinceCutOrCopy = SystemClock.uptimeMillis() - LAST_CUT_OR_COPY_TIME; if (durationSinceCutOrCopy < RECENT_CUT_COPY_DURATION) { showActionPopupWindow(0); } @@ -10763,13 +10880,14 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void hideAfterDelay() { - removeHiderCallback(); if (mHider == null) { mHider = new Runnable() { public void run() { hide(); } }; + } else { + removeHiderCallback(); } TextView.this.postDelayed(mHider, DELAY_BEFORE_HANDLE_FADES_OUT); } @@ -10990,12 +11108,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private InsertionHandleView getHandle() { - if (mSelectHandleCenter == null) { - mSelectHandleCenter = mContext.getResources().getDrawable( + if (getEditor().mSelectHandleCenter == null) { + getEditor().mSelectHandleCenter = mContext.getResources().getDrawable( mTextSelectHandleRes); } if (mHandle == null) { - mHandle = new InsertionHandleView(mSelectHandleCenter); + mHandle = new InsertionHandleView(getEditor().mSelectHandleCenter); } return mHandle; } @@ -11036,12 +11154,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void initDrawables() { - if (mSelectHandleLeft == null) { - mSelectHandleLeft = mContext.getResources().getDrawable( + if (getEditor().mSelectHandleLeft == null) { + getEditor().mSelectHandleLeft = mContext.getResources().getDrawable( mTextSelectHandleLeftRes); } - if (mSelectHandleRight == null) { - mSelectHandleRight = mContext.getResources().getDrawable( + if (getEditor().mSelectHandleRight == null) { + getEditor().mSelectHandleRight = mContext.getResources().getDrawable( mTextSelectHandleRightRes); } } @@ -11049,10 +11167,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private void initHandles() { // Lazy object creation has to be done before updatePosition() is called. if (mStartHandle == null) { - mStartHandle = new SelectionStartHandleView(mSelectHandleLeft, mSelectHandleRight); + mStartHandle = new SelectionStartHandleView(getEditor().mSelectHandleLeft, getEditor().mSelectHandleRight); } if (mEndHandle == null) { - mEndHandle = new SelectionEndHandleView(mSelectHandleRight, mSelectHandleLeft); + mEndHandle = new SelectionEndHandleView(getEditor().mSelectHandleRight, getEditor().mSelectHandleLeft); } mStartHandle.show(); @@ -11097,7 +11215,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener if (stayedInArea && isPositionOnText(x, y)) { startSelectionActionMode(); - mDiscardNextActionUp = true; + getEditor().mDiscardNextActionUp = true; } } } @@ -11186,465 +11304,501 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - private void hideInsertionPointCursorController() { - // No need to create the controller to hide it. - if (mInsertionPointCursorController != null) { - mInsertionPointCursorController.hide(); - } + static class InputContentType { + int imeOptions = EditorInfo.IME_NULL; + String privateImeOptions; + CharSequence imeActionLabel; + int imeActionId; + Bundle extras; + OnEditorActionListener onEditorActionListener; + boolean enterDown; } - /** - * Hides the insertion controller and stops text selection mode, hiding the selection controller - */ - private void hideControllers() { - hideCursorControllers(); - hideSpanControllers(); + static class InputMethodState { + Rect mCursorRectInWindow = new Rect(); + RectF mTmpRectF = new RectF(); + float[] mTmpOffset = new float[2]; + ExtractedTextRequest mExtracting; + final ExtractedText mTmpExtracted = new ExtractedText(); + int mBatchEditNesting; + boolean mCursorChanged; + boolean mSelectionModeChanged; + boolean mContentChanged; + int mChangedStart, mChangedEnd, mChangedDelta; } - private void hideSpanControllers() { - if (mChangeWatcher != null) { - mChangeWatcher.hideControllers(); + private class Editor { + Editor() { + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + final CompatibilityInfo compat = TextView.this.getResources().getCompatibilityInfo(); + mHighlightPaint.setCompatibilityScaling(compat.applicationScale); } - } - private void hideCursorControllers() { - if (mSuggestionsPopupWindow != null && !mSuggestionsPopupWindow.isShowingUp()) { - // Should be done before hide insertion point controller since it triggers a show of it - mSuggestionsPopupWindow.hide(); - } - hideInsertionPointCursorController(); - stopSelectionActionMode(); - } + // Cursor Controllers. + InsertionPointCursorController mInsertionPointCursorController; + SelectionModifierCursorController mSelectionModifierCursorController; + ActionMode mSelectionActionMode; + boolean mInsertionControllerEnabled; + boolean mSelectionControllerEnabled; - /** - * Get the character offset closest to the specified absolute position. A typical use case is to - * pass the result of {@link MotionEvent#getX()} and {@link MotionEvent#getY()} to this method. - * - * @param x The horizontal absolute position of a point on screen - * @param y The vertical absolute position of a point on screen - * @return the character offset for the character whose position is closest to the specified - * position. Returns -1 if there is no layout. - */ - public int getOffsetForPosition(float x, float y) { - if (getLayout() == null) return -1; - final int line = getLineAtCoordinate(y); - final int offset = getOffsetAtCoordinate(line, x); - return offset; - } + // Used to highlight a word when it is corrected by the IME + CorrectionHighlighter mCorrectionHighlighter; - private float convertToLocalHorizontalCoordinate(float x) { - x -= getTotalPaddingLeft(); - // Clamp the position to inside of the view. - x = Math.max(0.0f, x); - x = Math.min(getWidth() - getTotalPaddingRight() - 1, x); - x += getScrollX(); - return x; - } + InputContentType mInputContentType; + InputMethodState mInputMethodState; - private int getLineAtCoordinate(float y) { - y -= getTotalPaddingTop(); - // Clamp the position to inside of the view. - y = Math.max(0.0f, y); - y = Math.min(getHeight() - getTotalPaddingBottom() - 1, y); - y += getScrollY(); - return getLayout().getLineForVertical((int) y); - } + Path mHighlightPath; + boolean mHighlightPathBogus = true; + final Paint mHighlightPaint; - private int getOffsetAtCoordinate(int line, float x) { - x = convertToLocalHorizontalCoordinate(x); - return getLayout().getOffsetForHorizontal(line, x); - } + DisplayList mTextDisplayList; + boolean mTextDisplayListIsValid; - /** Returns true if the screen coordinates position (x,y) corresponds to a character displayed - * in the view. Returns false when the position is in the empty space of left/right of text. - */ - private boolean isPositionOnText(float x, float y) { - if (getLayout() == null) return false; + boolean mFrozenWithFocus; + boolean mSelectionMoved; + boolean mTouchFocusSelected; - final int line = getLineAtCoordinate(y); - x = convertToLocalHorizontalCoordinate(x); + KeyListener mKeyListener; + int mInputType = EditorInfo.TYPE_NULL; - if (x < getLayout().getLineLeft(line)) return false; - if (x > getLayout().getLineRight(line)) return false; - return true; - } + boolean mDiscardNextActionUp; + boolean mIgnoreActionUpEvent; - @Override - public boolean onDragEvent(DragEvent event) { - switch (event.getAction()) { - case DragEvent.ACTION_DRAG_STARTED: - return hasInsertionController(); + long mShowCursor; + Blink mBlink; - case DragEvent.ACTION_DRAG_ENTERED: - TextView.this.requestFocus(); - return true; + boolean mCursorVisible = true; + boolean mSelectAllOnFocus; + boolean mTextIsSelectable; - case DragEvent.ACTION_DRAG_LOCATION: - final int offset = getOffsetForPosition(event.getX(), event.getY()); - Selection.setSelection((Spannable)mText, offset); - return true; + CharSequence mError; + boolean mErrorWasChanged; + ErrorPopup mErrorPopup; + /** + * This flag is set if the TextView tries to display an error before it + * is attached to the window (so its position is still unknown). + * It causes the error to be shown later, when onAttachedToWindow() + * is called. + */ + boolean mShowErrorAfterAttach; - case DragEvent.ACTION_DROP: - onDrop(event); - return true; + boolean mInBatchEditControllers; - case DragEvent.ACTION_DRAG_ENDED: - case DragEvent.ACTION_DRAG_EXITED: - default: - return true; - } - } + SuggestionsPopupWindow mSuggestionsPopupWindow; + SuggestionRangeSpan mSuggestionRangeSpan; + Runnable mShowSuggestionRunnable; - private void onDrop(DragEvent event) { - StringBuilder content = new StringBuilder(""); - ClipData clipData = event.getClipData(); - final int itemCount = clipData.getItemCount(); - for (int i=0; i < itemCount; i++) { - Item item = clipData.getItemAt(i); - content.append(item.coerceToText(TextView.this.mContext)); - } + final Drawable[] mCursorDrawable = new Drawable[2]; + int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split) - final int offset = getOffsetForPosition(event.getX(), event.getY()); + Drawable mSelectHandleLeft; + Drawable mSelectHandleRight; + Drawable mSelectHandleCenter; - Object localState = event.getLocalState(); - DragLocalState dragLocalState = null; - if (localState instanceof DragLocalState) { - dragLocalState = (DragLocalState) localState; - } - boolean dragDropIntoItself = dragLocalState != null && - dragLocalState.sourceTextView == this; + // Global listener that detects changes in the global position of the TextView + PositionListener mPositionListener; - if (dragDropIntoItself) { - if (offset >= dragLocalState.start && offset < dragLocalState.end) { - // A drop inside the original selection discards the drop. - return; - } - } + float mLastDownPositionX, mLastDownPositionY; + Callback mCustomSelectionActionModeCallback; - final int originalLength = mText.length(); - long minMax = prepareSpacesAroundPaste(offset, offset, content); - int min = extractRangeStartFromLong(minMax); - int max = extractRangeEndFromLong(minMax); + // Set when this TextView gained focus with some text selected. Will start selection mode. + boolean mCreatedWithASelection; - Selection.setSelection((Spannable) mText, max); - replaceText_internal(min, max, content); + WordIterator mWordIterator; + SpellChecker mSpellChecker; - if (dragDropIntoItself) { - int dragSourceStart = dragLocalState.start; - int dragSourceEnd = dragLocalState.end; - if (max <= dragSourceStart) { - // Inserting text before selection has shifted positions - final int shift = mText.length() - originalLength; - dragSourceStart += shift; - dragSourceEnd += shift; + void onAttachedToWindow() { + final ViewTreeObserver observer = getViewTreeObserver(); + // No need to create the controller. + // The get method will add the listener on controller creation. + if (mInsertionPointCursorController != null) { + observer.addOnTouchModeChangeListener(mInsertionPointCursorController); } - - // Delete original selection - deleteText_internal(dragSourceStart, dragSourceEnd); - - // Make sure we do not leave two adjacent spaces. - if ((dragSourceStart == 0 || - Character.isSpaceChar(mTransformed.charAt(dragSourceStart - 1))) && - (dragSourceStart == mText.length() || - Character.isSpaceChar(mTransformed.charAt(dragSourceStart)))) { - final int pos = dragSourceStart == mText.length() ? - dragSourceStart - 1 : dragSourceStart; - deleteText_internal(pos, pos + 1); + if (mSelectionModifierCursorController != null) { + observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); } + updateSpellCheckSpans(0, mText.length(), true /* create the spell checker if needed */); } - } - /** - * @return True if this view supports insertion handles. - */ - boolean hasInsertionController() { - return mInsertionControllerEnabled; - } + void onDetachedFromWindow() { + if (mError != null) { + hideError(); + } - /** - * @return True if this view supports selection handles. - */ - boolean hasSelectionController() { - return mSelectionControllerEnabled; - } + if (mBlink != null) { + mBlink.removeCallbacks(mBlink); + } - InsertionPointCursorController getInsertionController() { - if (!mInsertionControllerEnabled) { - return null; - } + if (mInsertionPointCursorController != null) { + mInsertionPointCursorController.onDetached(); + } - if (mInsertionPointCursorController == null) { - mInsertionPointCursorController = new InsertionPointCursorController(); + if (mSelectionModifierCursorController != null) { + mSelectionModifierCursorController.onDetached(); + } - final ViewTreeObserver observer = getViewTreeObserver(); - observer.addOnTouchModeChangeListener(mInsertionPointCursorController); - } + if (mShowSuggestionRunnable != null) { + removeCallbacks(mShowSuggestionRunnable); + } - return mInsertionPointCursorController; - } + if (mTextDisplayList != null) { + mTextDisplayList.invalidate(); + } - SelectionModifierCursorController getSelectionController() { - if (!mSelectionControllerEnabled) { - return null; + if (mSpellChecker != null) { + mSpellChecker.closeSession(); + // Forces the creation of a new SpellChecker next time this window is created. + // Will handle the cases where the settings has been changed in the meantime. + mSpellChecker = null; + } + + hideControllers(); } - if (mSelectionModifierCursorController == null) { - mSelectionModifierCursorController = new SelectionModifierCursorController(); + void adjustInputType(boolean password, boolean passwordInputType, + boolean webPasswordInputType, boolean numberPasswordInputType) { + // mInputType has been set from inputType, possibly modified by mInputMethod. + // Specialize mInputType to [web]password if we have a text class and the original input + // type was a password. + if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { + if (password || passwordInputType) { + mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) + | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD; + } + if (webPasswordInputType) { + mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) + | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD; + } + } else if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_NUMBER) { + if (numberPasswordInputType) { + mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) + | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD; + } + } + } - final ViewTreeObserver observer = getViewTreeObserver(); - observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); + void setFrame() { + if (mErrorPopup != null) { + TextView tv = (TextView) mErrorPopup.getContentView(); + chooseSize(mErrorPopup, mError, tv); + mErrorPopup.update(TextView.this, getErrorX(), getErrorY(), + mErrorPopup.getWidth(), mErrorPopup.getHeight()); + } } - return mSelectionModifierCursorController; - } + void onFocusChanged(boolean focused, int direction) { + mShowCursor = SystemClock.uptimeMillis(); + ensureEndedBatchEdit(); - boolean isInBatchEditMode() { - final InputMethodState ims = mInputMethodState; - if (ims != null) { - return ims.mBatchEditNesting > 0; - } - return mInBatchEditControllers; - } + if (focused) { + int selStart = getSelectionStart(); + int selEnd = getSelectionEnd(); - @Override - protected void resolveTextDirection() { - if (hasPasswordTransformationMethod()) { - mTextDir = TextDirectionHeuristics.LOCALE; - return; - } + // SelectAllOnFocus fields are highlighted and not selected. Do not start text selection + // mode for these, unless there was a specific selection already started. + final boolean isFocusHighlighted = mSelectAllOnFocus && selStart == 0 && + selEnd == mText.length(); - // Always need to resolve layout direction first - final boolean defaultIsRtl = (getResolvedLayoutDirection() == LAYOUT_DIRECTION_RTL); + mCreatedWithASelection = mFrozenWithFocus && hasSelection() && !isFocusHighlighted; - // Then resolve text direction on the parent - super.resolveTextDirection(); + if (!mFrozenWithFocus || (selStart < 0 || selEnd < 0)) { + // If a tap was used to give focus to that view, move cursor at tap position. + // Has to be done before onTakeFocus, which can be overloaded. + final int lastTapPosition = getLastTapPosition(); + if (lastTapPosition >= 0) { + Selection.setSelection((Spannable) mText, lastTapPosition); + } - // Now, we can select the heuristic - int textDir = getResolvedTextDirection(); - switch (textDir) { - default: - case TEXT_DIRECTION_FIRST_STRONG: - mTextDir = (defaultIsRtl ? TextDirectionHeuristics.FIRSTSTRONG_RTL : - TextDirectionHeuristics.FIRSTSTRONG_LTR); - break; - case TEXT_DIRECTION_ANY_RTL: - mTextDir = TextDirectionHeuristics.ANYRTL_LTR; - break; - case TEXT_DIRECTION_LTR: - mTextDir = TextDirectionHeuristics.LTR; - break; - case TEXT_DIRECTION_RTL: - mTextDir = TextDirectionHeuristics.RTL; - break; - case TEXT_DIRECTION_LOCALE: - mTextDir = TextDirectionHeuristics.LOCALE; - break; - } - } + // Note this may have to be moved out of the Editor class + if (mMovement != null) { + mMovement.onTakeFocus(TextView.this, (Spannable) mText, direction); + } - /** - * Subclasses will need to override this method to implement their own way of resolving - * drawables depending on the layout direction. - * - * A call to the super method will be required from the subclasses implementation. - * - */ - protected void resolveDrawables() { - // No need to resolve twice - if (mResolvedDrawables) { - return; - } - // No drawable to resolve - if (mDrawables == null) { - return; - } - // No relative drawable to resolve - if (mDrawables.mDrawableStart == null && mDrawables.mDrawableEnd == null) { - mResolvedDrawables = true; - return; - } + // The DecorView does not have focus when the 'Done' ExtractEditText button is + // pressed. Since it is the ViewAncestor's mView, it requests focus before + // ExtractEditText clears focus, which gives focus to the ExtractEditText. + // This special case ensure that we keep current selection in that case. + // It would be better to know why the DecorView does not have focus at that time. + if (((TextView.this instanceof ExtractEditText) || mSelectionMoved) && + selStart >= 0 && selEnd >= 0) { + /* + * Someone intentionally set the selection, so let them + * do whatever it is that they wanted to do instead of + * the default on-focus behavior. We reset the selection + * here instead of just skipping the onTakeFocus() call + * because some movement methods do something other than + * just setting the selection in theirs and we still + * need to go through that path. + */ + Selection.setSelection((Spannable) mText, selStart, selEnd); + } - Drawables dr = mDrawables; - switch(getResolvedLayoutDirection()) { - case LAYOUT_DIRECTION_RTL: - if (dr.mDrawableStart != null) { - dr.mDrawableRight = dr.mDrawableStart; + if (mSelectAllOnFocus) { + selectAll(); + } - dr.mDrawableSizeRight = dr.mDrawableSizeStart; - dr.mDrawableHeightRight = dr.mDrawableHeightStart; + mTouchFocusSelected = true; } - if (dr.mDrawableEnd != null) { - dr.mDrawableLeft = dr.mDrawableEnd; - dr.mDrawableSizeLeft = dr.mDrawableSizeEnd; - dr.mDrawableHeightLeft = dr.mDrawableHeightEnd; - } - break; + mFrozenWithFocus = false; + mSelectionMoved = false; - case LAYOUT_DIRECTION_LTR: - default: - if (dr.mDrawableStart != null) { - dr.mDrawableLeft = dr.mDrawableStart; + if (mError != null) { + showError(); + } - dr.mDrawableSizeLeft = dr.mDrawableSizeStart; - dr.mDrawableHeightLeft = dr.mDrawableHeightStart; + makeBlink(); + } else { + if (mError != null) { + hideError(); + } + // Don't leave us in the middle of a batch edit. + onEndBatchEdit(); + + if (TextView.this instanceof ExtractEditText) { + // terminateTextSelectionMode removes selection, which we want to keep when + // ExtractEditText goes out of focus. + final int selStart = getSelectionStart(); + final int selEnd = getSelectionEnd(); + hideControllers(); + Selection.setSelection((Spannable) mText, selStart, selEnd); + } else { + hideControllers(); + downgradeEasyCorrectionSpans(); } - if (dr.mDrawableEnd != null) { - dr.mDrawableRight = dr.mDrawableEnd; - dr.mDrawableSizeRight = dr.mDrawableSizeEnd; - dr.mDrawableHeightRight = dr.mDrawableHeightEnd; + // No need to create the controller + if (mSelectionModifierCursorController != null) { + mSelectionModifierCursorController.resetTouchOffsets(); } - break; + } } - mResolvedDrawables = true; - } - protected void resetResolvedDrawables() { - mResolvedDrawables = false; - } + void sendOnTextChanged(int start, int after) { + updateSpellCheckSpans(start, start + after, false); + mTextDisplayListIsValid = false; - /** - * @hide - */ - protected void viewClicked(InputMethodManager imm) { - if (imm != null) { - imm.viewClicked(this); + // Hide the controllers as soon as text is modified (typing, procedural...) + // We do not hide the span controllers, since they can be added when a new text is + // inserted into the text view (voice IME). + hideCursorControllers(); } - } - - /** - * Deletes the range of text [start, end[. - * @hide - */ - protected void deleteText_internal(int start, int end) { - ((Editable) mText).delete(start, end); - } - /** - * Replaces the range of text [start, end[ by replacement text - * @hide - */ - protected void replaceText_internal(int start, int end, CharSequence text) { - ((Editable) mText).replace(start, end, text); - } + private int getLastTapPosition() { + // No need to create the controller at that point, no last tap position saved + if (mSelectionModifierCursorController != null) { + int lastTapPosition = mSelectionModifierCursorController.getMinTouchOffset(); + if (lastTapPosition >= 0) { + // Safety check, should not be possible. + if (lastTapPosition > mText.length()) { + Log.e(LOG_TAG, "Invalid tap focus position (" + lastTapPosition + " vs " + + mText.length() + ")"); + lastTapPosition = mText.length(); + } + return lastTapPosition; + } + } - /** - * Sets a span on the specified range of text - * @hide - */ - protected void setSpan_internal(Object span, int start, int end, int flags) { - ((Editable) mText).setSpan(span, start, end, flags); - } + return -1; + } - /** - * Moves the cursor to the specified offset position in text - * @hide - */ - protected void setCursorPosition_internal(int start, int end) { - Selection.setSelection(((Editable) mText), start, end); - } + void onWindowFocusChanged(boolean hasWindowFocus) { + if (hasWindowFocus) { + if (mBlink != null) { + mBlink.uncancel(); + makeBlink(); + } + } else { + if (mBlink != null) { + mBlink.cancel(); + } + if (mInputContentType != null) { + mInputContentType.enterDown = false; + } + // Order matters! Must be done before onParentLostFocus to rely on isShowingUp + hideControllers(); + if (mSuggestionsPopupWindow != null) { + mSuggestionsPopupWindow.onParentLostFocus(); + } - @ViewDebug.ExportedProperty(category = "text") - private CharSequence mText; - private CharSequence mTransformed; - private BufferType mBufferType = BufferType.NORMAL; + // Don't leave us in the middle of a batch edit. + onEndBatchEdit(); + } + } - private int mInputType = EditorInfo.TYPE_NULL; - private CharSequence mHint; - private Layout mHintLayout; + void onTouchEvent(MotionEvent event) { + if (hasSelectionController()) { + getSelectionController().onTouchEvent(event); + } - private KeyListener mInput; + if (mShowSuggestionRunnable != null) { + removeCallbacks(mShowSuggestionRunnable); + mShowSuggestionRunnable = null; + } - private MovementMethod mMovement; - private TransformationMethod mTransformation; - private boolean mAllowTransformationLengthChange; - private ChangeWatcher mChangeWatcher; + if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { + mLastDownPositionX = event.getX(); + mLastDownPositionY = event.getY(); - private ArrayList<TextWatcher> mListeners = null; + // Reset this state; it will be re-set if super.onTouchEvent + // causes focus to move to the view. + mTouchFocusSelected = false; + mIgnoreActionUpEvent = false; + } + } - // display attributes - private final TextPaint mTextPaint; - private boolean mUserSetTextScaleX; - private final Paint mHighlightPaint; - private int mHighlightColor = 0x6633B5E5; - private Layout mLayout; + void onDraw(Canvas canvas, Layout layout, int cursorOffsetVertical) { + Path highlight = null; + Paint highlightPaint = null; - private long mShowCursor; - private Blink mBlink; - private boolean mCursorVisible = true; + int selStart = -1, selEnd = -1; + boolean drawCursor = false; - // Cursor Controllers. - private InsertionPointCursorController mInsertionPointCursorController; - private SelectionModifierCursorController mSelectionModifierCursorController; - private ActionMode mSelectionActionMode; - private boolean mInsertionControllerEnabled; - private boolean mSelectionControllerEnabled; - private boolean mInBatchEditControllers; + highlightPaint = mHighlightPaint; + // If there is no movement method, then there can be no selection. + // Check that first and attempt to skip everything having to do with + // the cursor. + // XXX This is not strictly true -- a program could set the + // selection manually if it really wanted to. + if (mMovement != null && (isFocused() || isPressed())) { + selStart = getSelectionStart(); + selEnd = getSelectionEnd(); - private boolean mSelectAllOnFocus = false; + if (selStart >= 0) { + if (mHighlightPath == null) mHighlightPath = new Path(); - private int mGravity = Gravity.TOP | Gravity.START; - private boolean mHorizontallyScrolling; + if (selStart == selEnd) { + if (isCursorVisible() && + (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) { + if (mHighlightPathBogus) { + mHighlightPath.reset(); + mLayout.getCursorPath(selStart, mHighlightPath, mText); + updateCursorsPositions(); + mHighlightPathBogus = false; + } - private int mAutoLinkMask; - private boolean mLinksClickable = true; + // XXX should pass to skin instead of drawing directly + highlightPaint.setColor(mCurTextColor); + if (mCurrentAlpha != 255) { + highlightPaint.setAlpha( + (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255); + } + highlightPaint.setStyle(Paint.Style.STROKE); + highlight = mHighlightPath; + drawCursor = mCursorCount > 0; + } + } else if (textCanBeSelected()) { + if (mHighlightPathBogus) { + mHighlightPath.reset(); + mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); + mHighlightPathBogus = false; + } - private float mSpacingMult = 1.0f; - private float mSpacingAdd = 0.0f; - private boolean mTextIsSelectable = false; + // XXX should pass to skin instead of drawing directly + highlightPaint.setColor(mHighlightColor); + if (mCurrentAlpha != 255) { + highlightPaint.setAlpha( + (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255); + } + highlightPaint.setStyle(Paint.Style.FILL); - private static final int LINES = 1; - private static final int EMS = LINES; - private static final int PIXELS = 2; + highlight = mHighlightPath; + } + } + } - private int mMaximum = Integer.MAX_VALUE; - private int mMaxMode = LINES; - private int mMinimum = 0; - private int mMinMode = LINES; + final InputMethodState ims = mInputMethodState; + if (ims != null && ims.mBatchEditNesting == 0) { + InputMethodManager imm = InputMethodManager.peekInstance(); + if (imm != null) { + if (imm.isActive(TextView.this)) { + boolean reported = false; + if (ims.mContentChanged || ims.mSelectionModeChanged) { + // We are in extract mode and the content has changed + // in some way... just report complete new text to the + // input method. + reported = reportExtractedText(); + } + if (!reported && highlight != null) { + int candStart = -1; + int candEnd = -1; + if (mText instanceof Spannable) { + Spannable sp = (Spannable)mText; + candStart = EditableInputConnection.getComposingSpanStart(sp); + candEnd = EditableInputConnection.getComposingSpanEnd(sp); + } + imm.updateSelection(TextView.this, selStart, selEnd, candStart, candEnd); + } + } - private int mOldMaximum = mMaximum; - private int mOldMaxMode = mMaxMode; + if (imm.isWatchingCursor(TextView.this) && highlight != null) { + highlight.computeBounds(ims.mTmpRectF, true); + ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; - private int mMaxWidth = Integer.MAX_VALUE; - private int mMaxWidthMode = PIXELS; - private int mMinWidth = 0; - private int mMinWidthMode = PIXELS; + canvas.getMatrix().mapPoints(ims.mTmpOffset); + ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); - private boolean mSingleLine; - private int mDesiredHeightAtMeasure = -1; - private boolean mIncludePad = true; + ims.mTmpRectF.offset(0, cursorOffsetVertical); - // tmp primitives, so we don't alloc them on each draw - private Path mHighlightPath; - private boolean mHighlightPathBogus = true; - private static final RectF sTempRect = new RectF(); - private static final float[] sTmpPosition = new float[2]; + ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), + (int)(ims.mTmpRectF.top + 0.5), + (int)(ims.mTmpRectF.right + 0.5), + (int)(ims.mTmpRectF.bottom + 0.5)); - // XXX should be much larger - private static final int VERY_WIDE = 1024*1024; + imm.updateCursor(TextView.this, + ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, + ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); + } + } + } - private static final int BLINK = 500; + if (mCorrectionHighlighter != null) { + mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); + } - private static final int ANIMATED_SCROLL_GAP = 250; - private long mLastScroll; - private Scroller mScroller = null; + if (drawCursor) { + drawCursor(canvas, cursorOffsetVertical); + // Rely on the drawable entirely, do not draw the cursor line. + // Has to be done after the IMM related code above which relies on the highlight. + highlight = null; + } - private BoringLayout.Metrics mBoring; - private BoringLayout.Metrics mHintBoring; + if (canHaveDisplayList() && canvas.isHardwareAccelerated()) { + final int width = mRight - mLeft; + final int height = mBottom - mTop; - private BoringLayout mSavedLayout, mSavedHintLayout; + if (mTextDisplayList == null || !mTextDisplayList.isValid() || + !mTextDisplayListIsValid) { + if (mTextDisplayList == null) { + mTextDisplayList = getHardwareRenderer().createDisplayList("Text"); + } - private TextDirectionHeuristic mTextDir = null; + final HardwareCanvas hardwareCanvas = mTextDisplayList.start(); + try { + hardwareCanvas.setViewport(width, height); + // The dirty rect should always be null for a display list + hardwareCanvas.onPreDraw(null); + hardwareCanvas.translate(-mScrollX, -mScrollY); + layout.draw(hardwareCanvas, highlight, highlightPaint, cursorOffsetVertical); + hardwareCanvas.translate(mScrollX, mScrollY); + } finally { + hardwareCanvas.onPostDraw(); + mTextDisplayList.end(); + mTextDisplayListIsValid = true; + } + } + canvas.translate(mScrollX, mScrollY); + ((HardwareCanvas) canvas).drawDisplayList(mTextDisplayList, width, height, null, + DisplayList.FLAG_CLIP_CHILDREN); + canvas.translate(-mScrollX, -mScrollY); + } else { + layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical); + } - private static final InputFilter[] NO_FILTERS = new InputFilter[0]; - private InputFilter[] mFilters = NO_FILTERS; - private static final Spanned EMPTY_SPANNED = new SpannedString(""); - private static int DRAG_SHADOW_MAX_TEXT_LENGTH = 20; - // System wide time for last cut or copy action. - private static long sLastCutOrCopyTime; - // Used to highlight a word when it is corrected by the IME - private CorrectionHighlighter mCorrectionHighlighter; - // New state used to change background based on whether this TextView is multiline. - private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline }; + if (mMarquee != null && mMarquee.shouldDrawGhost()) { + canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); + layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical); + } + } + } } diff --git a/core/java/android/widget/TimePicker.java b/core/java/android/widget/TimePicker.java index 8f10fff919bd..ef1d7d01bf36 100644 --- a/core/java/android/widget/TimePicker.java +++ b/core/java/android/widget/TimePicker.java @@ -213,6 +213,7 @@ public class TimePicker extends FrameLayout { button.requestFocus(); mIsAm = !mIsAm; updateAmPmControl(); + onTimeChanged(); } }); } else { @@ -227,6 +228,7 @@ public class TimePicker extends FrameLayout { picker.requestFocus(); mIsAm = !mIsAm; updateAmPmControl(); + onTimeChanged(); } }); mAmPmSpinnerInput = (EditText) mAmPmSpinner.findViewById(R.id.numberpicker_input); diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java index f3d891d11989..02dc27bef3e9 100644 --- a/core/java/android/widget/ZoomButtonsController.java +++ b/core/java/android/widget/ZoomButtonsController.java @@ -501,7 +501,7 @@ public class ZoomButtonsController implements View.OnTouchListener { } else { - ViewRootImpl viewRoot = getOwnerViewRootImpl(); + ViewRootImpl viewRoot = mOwnerView.getViewRootImpl(); if (viewRoot != null) { viewRoot.dispatchKey(event); } @@ -526,20 +526,6 @@ public class ZoomButtonsController implements View.OnTouchListener { } } - private ViewRootImpl getOwnerViewRootImpl() { - View rootViewOfOwner = mOwnerView.getRootView(); - if (rootViewOfOwner == null) { - return null; - } - - ViewParent parentOfRootView = rootViewOfOwner.getParent(); - if (parentOfRootView instanceof ViewRootImpl) { - return (ViewRootImpl) parentOfRootView; - } else { - return null; - } - } - /** * @hide The ZoomButtonsController implements the OnTouchListener, but this * does not need to be shown in its public API. diff --git a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl index ba0aa1a44fb3..4553f9f48dbc 100644 --- a/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl +++ b/core/java/com/android/internal/textservice/ISpellCheckerSession.aidl @@ -24,7 +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 onGetSentenceSuggestionsMultiple(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 b44dbc896b00..641ed8ce288e 100644 --- a/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl +++ b/core/java/com/android/internal/textservice/ISpellCheckerSessionListener.aidl @@ -16,6 +16,7 @@ package com.android.internal.textservice; +import android.view.textservice.SentenceSuggestionsInfo; import android.view.textservice.SuggestionsInfo; /** @@ -23,5 +24,5 @@ import android.view.textservice.SuggestionsInfo; */ oneway interface ISpellCheckerSessionListener { void onGetSuggestions(in SuggestionsInfo[] results); - void onGetSuggestionsForSentence(in SuggestionsInfo[] results); + void onGetSentenceSuggestions(in SentenceSuggestionsInfo[] result); } diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index ed02636f1c6a..fa165272de7c 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -405,8 +405,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi View child = mMenuView.getChildAt(i); child.setScaleY(0); ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0, 1); - a.setDuration(100); - a.setStartDelay(j * 70); + a.setDuration(300); b.with(a); } } @@ -432,8 +431,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi View child = mMenuView.getChildAt(i); child.setScaleY(0); ObjectAnimator a = ObjectAnimator.ofFloat(child, "scaleY", 0); - a.setDuration(100); - a.setStartDelay(i * 70); + a.setDuration(300); b.with(a); } } diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 517ce4e6c832..2f325bf74e52 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -606,6 +606,9 @@ public class ActionBarView extends AbsActionBarView { ((mDisplayOptions & ActionBar.DISPLAY_USE_LOGO) == 0 || mLogo == null)) { mHomeLayout.setIcon(icon); } + if (mExpandedActionView != null) { + mExpandedHomeLayout.setIcon(mIcon.getConstantState().newDrawable(getResources())); + } } public void setIcon(int resId) { diff --git a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java index 2e7810fa1139..26518ebdd2a7 100644 --- a/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java +++ b/core/java/com/android/internal/widget/PasswordEntryKeyboardHelper.java @@ -147,7 +147,7 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { } private void sendKeyEventsToTarget(int character) { - Handler handler = mTargetView.getHandler(); + ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl(); KeyEvent[] events = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD).getEvents( new char[] { (char) character }); if (events != null) { @@ -156,22 +156,22 @@ public class PasswordEntryKeyboardHelper implements OnKeyboardActionListener { KeyEvent event = events[i]; event = KeyEvent.changeFlags(event, event.getFlags() | KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE); - handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY, event)); + viewRootImpl.dispatchKey(event); } } } public void sendDownUpKeyEvents(int keyEventCode) { long eventTime = SystemClock.uptimeMillis(); - Handler handler = mTargetView.getHandler(); - handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, + ViewRootImpl viewRootImpl = mTargetView.getViewRootImpl(); + viewRootImpl.dispatchKeyFromIme( new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_DOWN, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, - KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); - handler.sendMessage(handler.obtainMessage(ViewRootImpl.DISPATCH_KEY_FROM_IME, + KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); + viewRootImpl.dispatchKeyFromIme( new KeyEvent(eventTime, eventTime, KeyEvent.ACTION_UP, keyEventCode, 0, 0, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, - KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE))); + KeyEvent.FLAG_SOFT_KEYBOARD|KeyEvent.FLAG_KEEP_TOUCH_MODE)); } public void onKey(int primaryCode, int[] keyCodes) { diff --git a/core/java/com/google/android/mms/ContentType.java b/core/java/com/google/android/mms/ContentType.java index b066fadae2f1..12a134399c33 100644 --- a/core/java/com/google/android/mms/ContentType.java +++ b/core/java/com/google/android/mms/ContentType.java @@ -39,6 +39,7 @@ public class ContentType { public static final String IMAGE_GIF = "image/gif"; public static final String IMAGE_WBMP = "image/vnd.wap.wbmp"; public static final String IMAGE_PNG = "image/png"; + public static final String IMAGE_X_MS_BMP = "image/x-ms-bmp"; public static final String AUDIO_UNSPECIFIED = "audio/*"; public static final String AUDIO_AAC = "audio/aac"; @@ -58,6 +59,7 @@ public class ContentType { public static final String AUDIO_X_MPEG = "audio/x-mpeg"; public static final String AUDIO_X_MPG = "audio/x-mpg"; public static final String AUDIO_3GPP = "audio/3gpp"; + public static final String AUDIO_X_WAV = "audio/x-wav"; public static final String AUDIO_OGG = "application/ogg"; public static final String VIDEO_UNSPECIFIED = "video/*"; @@ -89,6 +91,7 @@ public class ContentType { sSupportedContentTypes.add(IMAGE_WBMP); sSupportedContentTypes.add(IMAGE_PNG); sSupportedContentTypes.add(IMAGE_JPG); + sSupportedContentTypes.add(IMAGE_X_MS_BMP); //supportedContentTypes.add(IMAGE_SVG); not yet supported. sSupportedContentTypes.add(AUDIO_AAC); @@ -106,6 +109,7 @@ public class ContentType { sSupportedContentTypes.add(AUDIO_X_MPEG3); sSupportedContentTypes.add(AUDIO_X_MPEG); sSupportedContentTypes.add(AUDIO_X_MPG); + sSupportedContentTypes.add(AUDIO_X_WAV); sSupportedContentTypes.add(AUDIO_3GPP); sSupportedContentTypes.add(AUDIO_OGG); @@ -127,6 +131,7 @@ public class ContentType { sSupportedImageTypes.add(IMAGE_WBMP); sSupportedImageTypes.add(IMAGE_PNG); sSupportedImageTypes.add(IMAGE_JPG); + sSupportedImageTypes.add(IMAGE_X_MS_BMP); // add supported audio types sSupportedAudioTypes.add(AUDIO_AAC); @@ -145,6 +150,7 @@ public class ContentType { sSupportedAudioTypes.add(AUDIO_X_MPEG3); sSupportedAudioTypes.add(AUDIO_X_MPEG); sSupportedAudioTypes.add(AUDIO_X_MPG); + sSupportedAudioTypes.add(AUDIO_X_WAV); sSupportedAudioTypes.add(AUDIO_3GPP); sSupportedAudioTypes.add(AUDIO_OGG); diff --git a/core/java/com/google/android/mms/pdu/PduComposer.java b/core/java/com/google/android/mms/pdu/PduComposer.java index 8940945a605a..d426f89bcb6c 100644 --- a/core/java/com/google/android/mms/pdu/PduComposer.java +++ b/core/java/com/google/android/mms/pdu/PduComposer.java @@ -835,9 +835,7 @@ public class PduComposer { appendOctet(PduHeaders.CONTENT_TYPE); // Message body - makeMessageBody(); - - return PDU_COMPOSE_SUCCESS; // Composing the message is OK + return makeMessageBody(); } /** diff --git a/core/java/com/google/android/mms/pdu/PduPersister.java b/core/java/com/google/android/mms/pdu/PduPersister.java index 8d57e5df5b0c..7037b6128c29 100644 --- a/core/java/com/google/android/mms/pdu/PduPersister.java +++ b/core/java/com/google/android/mms/pdu/PduPersister.java @@ -783,7 +783,7 @@ public class PduPersister { Log.v(TAG, "Saving data to: " + uri); } - byte[] buffer = new byte[256]; + byte[] buffer = new byte[8192]; for (int len = 0; (len = is.read(buffer)) != -1; ) { os.write(buffer, 0, len); } diff --git a/core/jni/Android.mk b/core/jni/Android.mk index 78e8df3a0428..c6d3ceeef6e7 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -185,6 +185,7 @@ LOCAL_C_INCLUDES += \ libcore/include LOCAL_SHARED_LIBRARIES := \ + libandroidfw \ libexpat \ libnativehelper \ libcutils \ diff --git a/core/jni/android/graphics/BitmapFactory.cpp b/core/jni/android/graphics/BitmapFactory.cpp index ea35006f65a3..4b64bf3b38d6 100644 --- a/core/jni/android/graphics/BitmapFactory.cpp +++ b/core/jni/android/graphics/BitmapFactory.cpp @@ -15,8 +15,8 @@ #include "JNIHelp.h" #include <android_runtime/AndroidRuntime.h> -#include <utils/Asset.h> -#include <utils/ResourceTypes.h> +#include <androidfw/Asset.h> +#include <androidfw/ResourceTypes.h> #include <netinet/in.h> #include <sys/mman.h> #include <sys/stat.h> diff --git a/core/jni/android/graphics/BitmapRegionDecoder.cpp b/core/jni/android/graphics/BitmapRegionDecoder.cpp index d43792939e72..682877a97b0e 100644 --- a/core/jni/android/graphics/BitmapRegionDecoder.cpp +++ b/core/jni/android/graphics/BitmapRegionDecoder.cpp @@ -37,7 +37,7 @@ #include <binder/Parcel.h> #include <jni.h> -#include <utils/Asset.h> +#include <androidfw/Asset.h> #include <sys/stat.h> #if 0 diff --git a/core/jni/android/graphics/Movie.cpp b/core/jni/android/graphics/Movie.cpp index c1acaa3e4683..4f64ff8f47b0 100644 --- a/core/jni/android/graphics/Movie.cpp +++ b/core/jni/android/graphics/Movie.cpp @@ -5,8 +5,8 @@ #include "SkUtils.h" #include "CreateJavaOutputStreamAdaptor.h" -#include <utils/Asset.h> -#include <utils/ResourceTypes.h> +#include <androidfw/Asset.h> +#include <androidfw/ResourceTypes.h> #include <netinet/in.h> #if 0 diff --git a/core/jni/android/graphics/NinePatch.cpp b/core/jni/android/graphics/NinePatch.cpp index f3b28a9e2e9c..684b1c1d3579 100644 --- a/core/jni/android/graphics/NinePatch.cpp +++ b/core/jni/android/graphics/NinePatch.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "9patch" #define LOG_NDEBUG 1 -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <utils/Log.h> #include "SkCanvas.h" diff --git a/core/jni/android/graphics/NinePatchImpl.cpp b/core/jni/android/graphics/NinePatchImpl.cpp index 1d0bb50613d1..ff0eb45b986d 100644 --- a/core/jni/android/graphics/NinePatchImpl.cpp +++ b/core/jni/android/graphics/NinePatchImpl.cpp @@ -18,7 +18,7 @@ #define LOG_TAG "NinePatch" #define LOG_NDEBUG 1 -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <utils/Log.h> #include "SkBitmap.h" diff --git a/core/jni/android/graphics/NinePatchPeeker.h b/core/jni/android/graphics/NinePatchPeeker.h index 8567e23f07fa..207536c2b324 100644 --- a/core/jni/android/graphics/NinePatchPeeker.h +++ b/core/jni/android/graphics/NinePatchPeeker.h @@ -18,7 +18,7 @@ #define NinePatchPeeker_h #include "SkImageDecoder.h" -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> using namespace android; diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp index b25598aecf2a..7f4c37bff34d 100644 --- a/core/jni/android/graphics/Typeface.cpp +++ b/core/jni/android/graphics/Typeface.cpp @@ -2,10 +2,10 @@ #include <android_runtime/AndroidRuntime.h> #include "GraphicsJNI.h" -#include <android_runtime/android_util_AssetManager.h> #include "SkStream.h" #include "SkTypeface.h" -#include <utils/AssetManager.h> +#include <android_runtime/android_util_AssetManager.h> +#include <androidfw/AssetManager.h> using namespace android; diff --git a/core/jni/android/graphics/Utils.h b/core/jni/android/graphics/Utils.h index 9a7a69703c2d..75ceaa25e4be 100644 --- a/core/jni/android/graphics/Utils.h +++ b/core/jni/android/graphics/Utils.h @@ -22,7 +22,7 @@ #include "android_util_Binder.h" #include <jni.h> -#include <utils/Asset.h> +#include <androidfw/Asset.h> namespace android { diff --git a/core/jni/android_app_NativeActivity.cpp b/core/jni/android_app_NativeActivity.cpp index 536681b0452f..4b3324b7f16f 100644 --- a/core/jni/android_app_NativeActivity.cpp +++ b/core/jni/android_app_NativeActivity.cpp @@ -27,7 +27,7 @@ #include <android_runtime/android_util_AssetManager.h> #include <surfaceflinger/Surface.h> #include <ui/egl/android_natives.h> -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include <utils/Looper.h> #include "JNIHelp.h" diff --git a/core/jni/android_app_backup_FullBackup.cpp b/core/jni/android_app_backup_FullBackup.cpp index 066a23e38e03..2ca645a6ee03 100644 --- a/core/jni/android_app_backup_FullBackup.cpp +++ b/core/jni/android_app_backup_FullBackup.cpp @@ -21,7 +21,7 @@ #include "JNIHelp.h" #include <android_runtime/AndroidRuntime.h> -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> #include <string.h> diff --git a/core/jni/android_backup_BackupDataInput.cpp b/core/jni/android_backup_BackupDataInput.cpp index 2fb0076ff1e1..25b0007fa15f 100644 --- a/core/jni/android_backup_BackupDataInput.cpp +++ b/core/jni/android_backup_BackupDataInput.cpp @@ -20,7 +20,7 @@ #include "JNIHelp.h" #include <android_runtime/AndroidRuntime.h> -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> namespace android { diff --git a/core/jni/android_backup_BackupDataOutput.cpp b/core/jni/android_backup_BackupDataOutput.cpp index abe0104cfcf5..e8f0fb85e29b 100644 --- a/core/jni/android_backup_BackupDataOutput.cpp +++ b/core/jni/android_backup_BackupDataOutput.cpp @@ -20,7 +20,7 @@ #include "JNIHelp.h" #include <android_runtime/AndroidRuntime.h> -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> namespace android { diff --git a/core/jni/android_backup_FileBackupHelperBase.cpp b/core/jni/android_backup_FileBackupHelperBase.cpp index 0dfd8dbfb29d..bb3a751ae8e6 100644 --- a/core/jni/android_backup_FileBackupHelperBase.cpp +++ b/core/jni/android_backup_FileBackupHelperBase.cpp @@ -20,7 +20,7 @@ #include "JNIHelp.h" #include <android_runtime/AndroidRuntime.h> -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> namespace android { diff --git a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp index 3b8fb7974140..3b8fb7974140 100755..100644 --- a/core/jni/android_bluetooth_BluetoothAudioGateway.cpp +++ b/core/jni/android_bluetooth_BluetoothAudioGateway.cpp diff --git a/core/jni/android_bluetooth_c.c b/core/jni/android_bluetooth_c.c index b4c672711594..b4c672711594 100755..100644 --- a/core/jni/android_bluetooth_c.c +++ b/core/jni/android_bluetooth_c.c diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp index 883753843233..5d51ee2d82f5 100644 --- a/core/jni/android_content_res_ObbScanner.cpp +++ b/core/jni/android_content_res_ObbScanner.cpp @@ -18,7 +18,7 @@ #include <utils/Log.h> #include <utils/String8.h> -#include <utils/ObbFile.h> +#include <androidfw/ObbFile.h> #include "jni.h" #include "JNIHelp.h" diff --git a/core/jni/android_os_Power.cpp b/core/jni/android_os_Power.cpp index dc1699089e38..48845f6f402e 100644 --- a/core/jni/android_os_Power.cpp +++ b/core/jni/android_os_Power.cpp @@ -15,13 +15,18 @@ ** limitations under the License. */ +#define LOG_TAG "Power-JNI" + #include "JNIHelp.h" #include "jni.h" #include "android_runtime/AndroidRuntime.h" #include <utils/misc.h> +#include <hardware/power.h> #include <hardware_legacy/power.h> #include <cutils/android_reboot.h> +static struct power_module *sPowerModule; + namespace android { @@ -65,7 +70,9 @@ setLastUserActivityTimeout(JNIEnv *env, jobject clazz, jlong timeMS) static int setScreenState(JNIEnv *env, jobject clazz, jboolean on) { - return set_screen_state(on); + if (sPowerModule) + sPowerModule->setInteractive(sPowerModule, on); + return 0; } static void android_os_Power_shutdown(JNIEnv *env, jobject clazz) @@ -85,12 +92,26 @@ static void android_os_Power_reboot(JNIEnv *env, jobject clazz, jstring reason) jniThrowIOException(env, errno); } +static int android_os_Power_init(JNIEnv *env, jobject clazz) +{ + status_t err = hw_get_module(POWER_HARDWARE_MODULE_ID, + (hw_module_t const**)&sPowerModule); + ALOGE_IF(err, "couldn't load %s module (%s)", + POWER_HARDWARE_MODULE_ID, strerror(-err)); + + if (!err) + sPowerModule->init(sPowerModule); + + return err; +} + static JNINativeMethod method_table[] = { { "acquireWakeLock", "(ILjava/lang/String;)V", (void*)acquireWakeLock }, { "releaseWakeLock", "(Ljava/lang/String;)V", (void*)releaseWakeLock }, { "setLastUserActivityTimeout", "(J)I", (void*)setLastUserActivityTimeout }, { "setScreenState", "(Z)I", (void*)setScreenState }, { "shutdown", "()V", (void*)android_os_Power_shutdown }, + { "powerInitNative", "()I", (void*)android_os_Power_init }, { "rebootNative", "(Ljava/lang/String;)V", (void*)android_os_Power_reboot }, }; diff --git a/core/jni/android_server_BluetoothEventLoop.cpp b/core/jni/android_server_BluetoothEventLoop.cpp index 9292fc02113c..8a69ba438460 100644 --- a/core/jni/android_server_BluetoothEventLoop.cpp +++ b/core/jni/android_server_BluetoothEventLoop.cpp @@ -164,7 +164,6 @@ static void initializeNativeDataNative(JNIEnv* env, jobject object) { ALOGE("%s: out of memory!", __FUNCTION__); return; } - memset(nat, 0, sizeof(native_data_t)); pthread_mutex_init(&(nat->thread_mutex), NULL); @@ -722,24 +721,20 @@ static jboolean startEventLoopNative(JNIEnv *env, jobject object) { return JNI_FALSE; } - nat->pollData = (struct pollfd *)malloc(sizeof(struct pollfd) * - DEFAULT_INITIAL_POLLFD_COUNT); + nat->pollData = (struct pollfd *)calloc( + DEFAULT_INITIAL_POLLFD_COUNT, sizeof(struct pollfd)); if (!nat->pollData) { ALOGE("out of memory error starting EventLoop!"); goto done; } - nat->watchData = (DBusWatch **)malloc(sizeof(DBusWatch *) * - DEFAULT_INITIAL_POLLFD_COUNT); + nat->watchData = (DBusWatch **)calloc( + DEFAULT_INITIAL_POLLFD_COUNT, sizeof(DBusWatch *)); if (!nat->watchData) { ALOGE("out of memory error starting EventLoop!"); goto done; } - memset(nat->pollData, 0, sizeof(struct pollfd) * - DEFAULT_INITIAL_POLLFD_COUNT); - memset(nat->watchData, 0, sizeof(DBusWatch *) * - DEFAULT_INITIAL_POLLFD_COUNT); nat->pollDataSize = DEFAULT_INITIAL_POLLFD_COUNT; nat->pollMemberCount = 1; diff --git a/core/jni/android_util_AssetManager.cpp b/core/jni/android_util_AssetManager.cpp index 17130af39450..7146667472ab 100644 --- a/core/jni/android_util_AssetManager.cpp +++ b/core/jni/android_util_AssetManager.cpp @@ -31,9 +31,9 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <utils/Asset.h> -#include <utils/AssetManager.h> -#include <utils/ResourceTypes.h> +#include <androidfw/Asset.h> +#include <androidfw/AssetManager.h> +#include <androidfw/ResourceTypes.h> #include <stdio.h> diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp index 958ddb22cbc7..28746ce1eea4 100644 --- a/core/jni/android_util_StringBlock.cpp +++ b/core/jni/android_util_StringBlock.cpp @@ -23,7 +23,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <stdio.h> diff --git a/core/jni/android_util_XmlBlock.cpp b/core/jni/android_util_XmlBlock.cpp index 45728dbb1bc5..ad6033b1c175 100644 --- a/core/jni/android_util_XmlBlock.cpp +++ b/core/jni/android_util_XmlBlock.cpp @@ -19,12 +19,11 @@ #include "jni.h" #include "JNIHelp.h" -#include <utils/misc.h> #include <android_runtime/AndroidRuntime.h> -#include <utils/AssetManager.h> +#include <androidfw/AssetManager.h> +#include <androidfw/ResourceTypes.h> #include <utils/Log.h> - -#include <utils/ResourceTypes.h> +#include <utils/misc.h> #include <stdio.h> diff --git a/core/jni/android_view_GLES20Canvas.cpp b/core/jni/android_view_GLES20Canvas.cpp index e19bb38dc53d..cdce4f9e1984 100644 --- a/core/jni/android_view_GLES20Canvas.cpp +++ b/core/jni/android_view_GLES20Canvas.cpp @@ -24,7 +24,7 @@ #include <android_runtime/AndroidRuntime.h> #include <android_runtime/android_graphics_SurfaceTexture.h> #include <cutils/properties.h> -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <gui/SurfaceTexture.h> @@ -652,9 +652,9 @@ static void android_view_GLES20Canvas_destroyDisplayList(JNIEnv* env, static bool android_view_GLES20Canvas_drawDisplayList(JNIEnv* env, jobject clazz, OpenGLRenderer* renderer, DisplayList* displayList, - jint width, jint height, jobject dirty) { + jint width, jint height, jobject dirty, jint flags) { android::uirenderer::Rect bounds; - bool redraw = renderer->drawDisplayList(displayList, width, height, bounds); + bool redraw = renderer->drawDisplayList(displayList, width, height, bounds, flags); if (redraw && dirty != NULL) { env->CallVoidMethod(dirty, gRectClassInfo.set, int(bounds.left), int(bounds.top), int(bounds.right), int(bounds.bottom)); @@ -901,7 +901,7 @@ static JNINativeMethod gMethods[] = { { "nGetDisplayListSize", "(I)I", (void*) android_view_GLES20Canvas_getDisplayListSize }, { "nSetDisplayListName", "(ILjava/lang/String;)V", (void*) android_view_GLES20Canvas_setDisplayListName }, - { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;)Z", + { "nDrawDisplayList", "(IIIILandroid/graphics/Rect;I)Z", (void*) android_view_GLES20Canvas_drawDisplayList }, { "nCreateDisplayListRenderer", "()I", (void*) android_view_GLES20Canvas_createDisplayListRenderer }, diff --git a/core/jni/android_view_InputChannel.cpp b/core/jni/android_view_InputChannel.cpp index 6d783dead52c..8350e73f91dc 100644 --- a/core/jni/android_view_InputChannel.cpp +++ b/core/jni/android_view_InputChannel.cpp @@ -21,7 +21,7 @@ #include <android_runtime/AndroidRuntime.h> #include <binder/Parcel.h> #include <utils/Log.h> -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include "android_view_InputChannel.h" #include "android_util_Binder.h" diff --git a/core/jni/android_view_InputChannel.h b/core/jni/android_view_InputChannel.h index fa2d28288923..096702140a2a 100644 --- a/core/jni/android_view_InputChannel.h +++ b/core/jni/android_view_InputChannel.h @@ -19,7 +19,7 @@ #include "jni.h" -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> namespace android { diff --git a/core/jni/android_view_InputEventReceiver.cpp b/core/jni/android_view_InputEventReceiver.cpp index 47b3f66beb19..e7d424472831 100644 --- a/core/jni/android_view_InputEventReceiver.cpp +++ b/core/jni/android_view_InputEventReceiver.cpp @@ -28,7 +28,7 @@ #include <utils/Log.h> #include <utils/Looper.h> #include <utils/threads.h> -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include "android_os_MessageQueue.h" #include "android_view_InputChannel.h" #include "android_view_KeyEvent.h" @@ -264,7 +264,7 @@ static void nativeFinishInputEvent(JNIEnv* env, jclass clazz, jint receiverPtr, sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); status_t status = receiver->finishInputEvent(seq, handled); - if (status) { + if (status && status != DEAD_OBJECT) { String8 message; message.appendFormat("Failed to finish input event. status=%d", status); jniThrowRuntimeException(env, message.string()); @@ -275,7 +275,7 @@ static void nativeConsumeBatchedInputEvents(JNIEnv* env, jclass clazz, jint rece sp<NativeInputEventReceiver> receiver = reinterpret_cast<NativeInputEventReceiver*>(receiverPtr); status_t status = receiver->consumeEvents(true /*consumeBatches*/); - if (status) { + if (status && status != DEAD_OBJECT) { String8 message; message.appendFormat("Failed to consume batched input event. status=%d", status); jniThrowRuntimeException(env, message.string()); diff --git a/core/jni/android_view_KeyCharacterMap.cpp b/core/jni/android_view_KeyCharacterMap.cpp index b9f373882440..7245d9de134c 100644 --- a/core/jni/android_view_KeyCharacterMap.cpp +++ b/core/jni/android_view_KeyCharacterMap.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <ui/KeyCharacterMap.h> -#include <ui/Input.h> +#include <androidfw/KeyCharacterMap.h> +#include <androidfw/Input.h> #include <android_runtime/AndroidRuntime.h> #include <nativehelper/jni.h> diff --git a/core/jni/android_view_KeyEvent.cpp b/core/jni/android_view_KeyEvent.cpp index 27469a22f06f..36a98f94e2fd 100644 --- a/core/jni/android_view_KeyEvent.cpp +++ b/core/jni/android_view_KeyEvent.cpp @@ -20,7 +20,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include "android_view_KeyEvent.h" namespace android { diff --git a/core/jni/android_view_MotionEvent.cpp b/core/jni/android_view_MotionEvent.cpp index 2def1d14b7f3..0fb1b17a2b8e 100644 --- a/core/jni/android_view_MotionEvent.cpp +++ b/core/jni/android_view_MotionEvent.cpp @@ -20,7 +20,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include "android_view_MotionEvent.h" #include "android_util_Binder.h" #include "android/graphics/Matrix.h" diff --git a/core/jni/android_view_VelocityTracker.cpp b/core/jni/android_view_VelocityTracker.cpp index 0da90d7c66c7..668d3bb7dfdd 100644 --- a/core/jni/android_view_VelocityTracker.cpp +++ b/core/jni/android_view_VelocityTracker.cpp @@ -20,7 +20,7 @@ #include <android_runtime/AndroidRuntime.h> #include <utils/Log.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include "android_view_MotionEvent.h" diff --git a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp index b68c97a6cd5d..afbcfc259529 100644 --- a/core/jni/com_android_internal_content_NativeLibraryHelper.cpp +++ b/core/jni/com_android_internal_content_NativeLibraryHelper.cpp @@ -21,7 +21,7 @@ #include <utils/Log.h> #include <ScopedUtfChars.h> -#include <utils/ZipFileRO.h> +#include <androidfw/ZipFileRO.h> #include <zlib.h> diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 68c919e92cb3..a2b1117ddf95 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -1521,6 +1521,17 @@ android:description="@string/permdesc_serialPort" android:protectionLevel="normal" /> + <!-- Allows the holder to access content providers from outside an ApplicationThread. + This permission is enforced by the ActivityManagerService on the corresponding APIs, + in particular ActivityManagerService#getContentProviderExternal(String) and + ActivityManagerService#removeContentProviderExternal(String). + @hide + --> + <permission android:name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" + android:label="@string/permlab_accessContentProvidersExternally" + android:description="@string/permdesc_accessContentProvidersExternally" + android:protectionLevel="signature" /> + <!-- The system process is explicitly the only one allowed to launch the confirmation UI for full backup/restore --> <uses-permission android:name="android.permission.CONFIRM_FULL_BACKUP"/> diff --git a/core/res/assets/webkit/youtube.html b/core/res/assets/webkit/youtube.html index d808bcf7789b..8e103c1655e1 100644 --- a/core/res/assets/webkit/youtube.html +++ b/core/res/assets/webkit/youtube.html @@ -13,94 +13,59 @@ height: 100%; padding: 0%; z-index: 10; + background-size: 100%; + background: no-repeat; + background-position: center; + } + #play { + position: absolute; + left: 50%; + top: 50%; + } + #logo { + position: absolute; + bottom: 0; + right: 0; } </style> </head> <body id="body"> <script type="text/javascript"> - // Nominal original size. If the embed is smaller than this, the play and logo - // images get scaled appropriately. These are actually 3/4 of the sizes suggested - // by youtube, so the images don't get too tiny. - defHeight = 258; - defWidth = 318; - function setup() { var width = document.body.clientWidth; var height = document.body.clientHeight; - var canvas = document.getElementById("canvas"); - // Resize the canvas to the right size - canvas.width = width; - canvas.height = height; - var ctx = canvas.getContext('2d'); + var mainElement = document.getElementById("main"); + var playElement = document.getElementById("play"); var loadcount = 0; - function doload() { - if (++loadcount == 3) { - // All images are loaded, so display them. - // (Note that the images are loaded from javascript, so might load - // after document.onload fires) - - playWidth = play.width; - playHeight = play.height; - logoWidth = logo.width; - logoHeight = logo.height; - var ratio = 1; - // If the page is smaller than it 'should' be in either dimension - // we scale the background, play button and logo according to the - // dimension that has been shrunk the most. - if (width / height > defWidth / defHeight && height < defHeight) { - ratio = height / defHeight; - // Stretch the background in this dimension only. - backgroundHeight = background.height / ratio; - ctx.drawImage(background, 0, 0, background.width, background.height, - 0, (height - backgroundHeight) / 2, width, backgroundHeight); - } else if (width / height < defWidth / defHeight && width < defWidth) { - ratio = width / defWidth; - backgroundWidth = background.width / ratio; - ctx.drawImage(background, 0, 0, background.width, background.height, - (width - backgroundWidth) / 2, 0, backgroundWidth, height); - } else { - // In this case stretch the background in both dimensions to fill the space. - ctx.drawImage(background, 0, 0, width, height); - } - playWidth *= ratio; - playHeight *= ratio; - logoWidth *= ratio; - logoHeight *= ratio; - playLeft = (width - playWidth) / 2; - playTop = (height - playHeight) / 2; - ctx.drawImage(play, playLeft, playTop, playWidth, playHeight); - ctx.globalAlpha = 0.7 - ctx.drawImage(logo, width - logoWidth, height - logoHeight, logoWidth, logoHeight); - // To make it slightly easier to hit, the click target is twice the width/height of the unscaled play button - targetLeft = width / 2 - play.width; - targetRight = width / 2 + play.width; - targetTop = height / 2 - play.height; - targetBottom = height / 2 + play.height; + var POSTER = "http://img.youtube.com/vi/VIDEO_ID/0.jpg"; - canvas.addEventListener("click", function(e) { - var posx = e.clientX-canvas.offsetLeft; - var posy = e.clientY-canvas.offsetTop; - if (posx >= targetLeft && posx <= targetRight && - posy >= targetTop && posy <= targetBottom) { - top.location.href = "vnd.youtube:VIDEO_ID"; - } - }, false); + function doload() { + if (++loadcount == 2) { + // Resize the element to the right size + mainElement.width = width; + mainElement.height = height; + mainElement.style.backgroundImage = "url('" + POSTER + "')"; + // Center the play button + playElement.style.marginTop = "-" + play.height/2 + "px"; + playElement.style.marginLeft = "-" + play.width/2 + "px"; + playElement.addEventListener("click", function(e) { + top.location.href = "vnd.youtube:VIDEO_ID"; + }, false); } } var background = new Image(); background.onload = doload; - background.src = "http://img.youtube.com/vi/VIDEO_ID/0.jpg"; + background.src = POSTER; play = new Image(); play.onload = doload; play.src = "play.png"; - logo = new Image(); - logo.onload = doload; - logo.src = "youtube.png"; } + window.onload = setup; </script> <div id="main"> - <canvas id="canvas"></canvas> + <img src="play.png" id="play"></img> + <img src="youtube.png" id="logo"></img> </div> </body> </html> diff --git a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png Binary files differindex f58a19cf8b4a..cb08eed75658 100644 --- a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png +++ b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_3_fully.png diff --git a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png Binary files differindex 744b1fa8e02f..ea065c36e742 100644 --- a/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png +++ b/core/res/res/drawable-hdpi/stat_sys_data_wimax_signal_disconnected.png diff --git a/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_3_fully.png b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_3_fully.png Binary files differnew file mode 100644 index 000000000000..d3ba98c3ebc2 --- /dev/null +++ b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_3_fully.png diff --git a/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_disconnected.png b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_disconnected.png Binary files differnew file mode 100644 index 000000000000..153c6ad1f6cf --- /dev/null +++ b/core/res/res/drawable-mdpi/stat_sys_data_wimax_signal_disconnected.png diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_3_fully.png b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_3_fully.png Binary files differnew file mode 100644 index 000000000000..ec6bc54d77e2 --- /dev/null +++ b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_3_fully.png diff --git a/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_disconnected.png b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_disconnected.png Binary files differnew file mode 100644 index 000000000000..9fd4f33bbf78 --- /dev/null +++ b/core/res/res/drawable-xhdpi/stat_sys_data_wimax_signal_disconnected.png diff --git a/core/res/res/values-af/strings.xml b/core/res/res/values-af/strings.xml index 840323e238ad..abf667693b01 100644 --- a/core/res/res/values-af/strings.xml +++ b/core/res/res/values-af/strings.xml @@ -766,6 +766,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Laat die houer toe om versoeke aan pakketverifieerders te rig. Dit moet nooit vir normale programme nodig wees nie."</string> <string name="permlab_serialPort" msgid="546083327654631076">"kry toegang tot reekspoorte"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Laat die houer toe om toegang te verkry tot reekspoorte wat die SerialManager API gebruik."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"verkry toegang tot inhoud ekstern"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Stel die houer in staat om toegang te verkry tot inhoudverskaffers vanuit die dop. Behoort nooit nodig te wees vir gewone programme nie."</string> <string name="save_password_message" msgid="767344687139195790">"Wil jy hê die blaaier moet hierdie wagwoord onthou?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Nie nou nie"</string> <string name="save_password_remember" msgid="6491879678996749466">"Onthou"</string> @@ -975,6 +977,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kon nie aan Wi-Fikoppel nie"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" het \'n swak internetverbinding."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Begin Wi-Fi Direct. Dit sal die Wi-Fi-kliënt/warmkol afskakel."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kon nie Wi-Fi Direct begin nie."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direk is aan"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Raak vir instellings"</string> <string name="accept" msgid="1645267259272829559">"Aanvaar"</string> <string name="decline" msgid="2112225451706137894">"Weier"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Uitnodiging gestuur"</string> diff --git a/core/res/res/values-am/strings.xml b/core/res/res/values-am/strings.xml index 905b90c44288..18fb40584a08 100644 --- a/core/res/res/values-am/strings.xml +++ b/core/res/res/values-am/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"የፓኬጅ አረጋጋጮችን ጥየቃ ለማድረግ ያዡ ይፈቅዳሉ። ለመደበኛ ትግበራዎች በፍፁም አያስፈልግም።"</string> <string name="permlab_serialPort" msgid="546083327654631076">"ተከታታይ ወደቦችን ድረስ"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API. የተከታታይ አደራጅ APIን በመጠቀም ያዡ የተከታታይ ወደቦችን እንዲደርስ ይፈቅዳል።"</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ይዘት አቅራቢዎች በውጭ በኩል ድረስባቸው"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"ያዢውን ከቀፎው ወደሚመጡ የይዘት አቅራቢዎች እንዲደርስ ይፈቅድለታል። ለመደበኛ መተግበሪያዎች በፍጹም ማስፈለግ የለባቸውም።"</string> <string name="save_password_message" msgid="767344687139195790">"አሳሹ ይህን ይለፍ ቃል እንዲያስታወስ ይፈልጋሉ?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"አሁን አይደለም"</string> <string name="save_password_remember" msgid="6491879678996749466">"አስታውስ"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"ወደ Wi-Fi ለማያያዝ አልተቻለም"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ደካማ የበይነመረብ ግንኙነት ኣለው።"</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ቀጥታ"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"የWi-Fi በቀጥታ ጀምር።ይህ የWi-Fi ደንበኛ /ድረስ ነጥብ ያጠፋል።"</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"በቀጥታ Wi-Fi ማስጀመር አልተቻለም።"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"የWi-Fi ቀጥታ በርቷል"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"ለቅንብሮች ንካ"</string> <string name="accept" msgid="1645267259272829559">"ተቀበል"</string> <string name="decline" msgid="2112225451706137894">"ውድቅ አድርግ"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"ግብዣ ተልኳል"</string> diff --git a/core/res/res/values-ar/strings.xml b/core/res/res/values-ar/strings.xml index a7c37e733cf4..a98a7d4bf6e3 100644 --- a/core/res/res/values-ar/strings.xml +++ b/core/res/res/values-ar/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"السماح للمالك بإجراء طلبات محققي الحزمة. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="permlab_serialPort" msgid="546083327654631076">"الدخول إلى المنافذ التسلسلية"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"يسمح لحامله بالدخول إلى المنافذ التسلسلية باستخدام واجهة برمجة التطبيقات."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"الدخول إلى مزودي المحتوى خارجيًا"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"السماح للمالك بالدخول إلى مزودي المحتوى من الوعاء. لن تكون هناك حاجة إليه مطلقًا مع التطبيقات العادية."</string> <string name="save_password_message" msgid="767344687139195790">"هل تريد من المتصفح تذكر كلمة المرور هذه؟"</string> <string name="save_password_notnow" msgid="6389675316706699758">"ليس الآن"</string> <string name="save_password_remember" msgid="6491879678996749466">"تذكّر"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"تعذر الاتصال بـ Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" لديها اتصال إنترنت رديء."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"اتصال Wi-Fi مباشر"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"ابدأ Wi-Fi Direct. يؤدي هذا إلى إيقاف عميل/نقطة اتصال Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"تعذر بدء Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"تم تشغيل اتصال Wi-Fi المباشر"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"المس للحصول على الإعدادات"</string> <string name="accept" msgid="1645267259272829559">"قبول"</string> <string name="decline" msgid="2112225451706137894">"رفض"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"تم إرسال الدعوة"</string> diff --git a/core/res/res/values-be/strings.xml b/core/res/res/values-be/strings.xml index 4a226a157f2d..233d5a95f02a 100644 --- a/core/res/res/values-be/strings.xml +++ b/core/res/res/values-be/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Дазваляе ўладальніку рабіць запыты верыфікатараў пакету. Не патрабуецца для звычайных прыкладанняў."</string> <string name="permlab_serialPort" msgid="546083327654631076">"атрымаць доступ да паслядоўных партоў"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Дазваляе ўладальніку атрымліваць доступ да паслядоўных партоў з дапамогай API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"знешнi доступ да кантэнт-правайдэраў"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Дае ўладальніку доступ да кантэнт-правайдэраў з абалонкi. Не патрабуецца для звычайных прыкладанняў."</string> <string name="save_password_message" msgid="767344687139195790">"Вы хочаце, каб браўзэр запомніў гэты пароль?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Не цяпер"</string> <string name="save_password_remember" msgid="6491879678996749466">"Запомніць"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Немагчыма падключыцца да Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" дрэннае падключэнне да Інтэрнэту."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Пачаць работу Wi-Fi Direct. Гэта адключыць кліента або кропку доступу Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Немагчыма запусціць Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct уключаны"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Дакраніцеся, каб наладзіць"</string> <string name="accept" msgid="1645267259272829559">"Прыняць"</string> <string name="decline" msgid="2112225451706137894">"Адхіліць"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Запрашэнне адпраўлена"</string> diff --git a/core/res/res/values-bg/strings.xml b/core/res/res/values-bg/strings.xml index f311a28d8682..66516b961900 100644 --- a/core/res/res/values-bg/strings.xml +++ b/core/res/res/values-bg/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Разрешава на притежателя да прави заявки за верификатори на пакета. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> <string name="permlab_serialPort" msgid="546083327654631076">"достъп до серийни портове"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Разрешава на притежателя достъп до серийни портове посредством приложния програмен интерфейс (API) SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"външен достъп до доставчиците на съдърж."</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Разрешава на притежателя достъп до доставчиците на съдържание от командния ред. Нормалните приложения би трябвало никога да не се нуждаят от това."</string> <string name="save_password_message" msgid="767344687139195790">"Искате ли браузърът да запомни тази парола?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Не сега"</string> <string name="save_password_remember" msgid="6491879678996749466">"Запомняне"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не можа да се свърже с Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" има лоша връзка с интернет."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Стартиране на Wi-Fi Direct. Това ще изключи клиентската програма/точката за достъп до Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct не можа да се стартира."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct е включено"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Докоснете за настройки"</string> <string name="accept" msgid="1645267259272829559">"Приемам"</string> <string name="decline" msgid="2112225451706137894">"Отхвърлям"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Поканата е изпратена"</string> diff --git a/core/res/res/values-ca/strings.xml b/core/res/res/values-ca/strings.xml index 95dae9d62b05..b848c2cf3dd9 100644 --- a/core/res/res/values-ca/strings.xml +++ b/core/res/res/values-ca/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet que el titular sol·liciti verificadors de paquets. No s\'hauria de necessitar mai per a les aplicacions normals."</string> <string name="permlab_serialPort" msgid="546083327654631076">"accedeix a ports sèrie"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet que el titular accedeixi a ports sèrie amb l\'API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accedeix als proveïdors de contingut externament"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permet que el titular accedeixi als proveïdors de contingut des de l\'intèrpret d\'ordres. No és necessari per a les aplicacions normals."</string> <string name="save_password_message" msgid="767344687139195790">"Voleu que el navegador recordi aquesta contrasenya?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ara no"</string> <string name="save_password_remember" msgid="6491879678996749466">"Recorda-ho"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"No s\'ha pogut connectar a la Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" té una mala connexió a Internet."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Inicia Wi-Fi Direct. Això desactivarà el client/la zona Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"No s\'ha pogut iniciar Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct està activat"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toca per accedir a la configuració"</string> <string name="accept" msgid="1645267259272829559">"Accepta"</string> <string name="decline" msgid="2112225451706137894">"Rebutja"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"S\'ha enviat la invitació"</string> diff --git a/core/res/res/values-cs/strings.xml b/core/res/res/values-cs/strings.xml index c01526d71b99..544395f7b256 100644 --- a/core/res/res/values-cs/strings.xml +++ b/core/res/res/values-cs/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Umožňuje držiteli podávat žádosti o ověření balíčků. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> <string name="permlab_serialPort" msgid="546083327654631076">"přístup k sériovým portům"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Umožňuje držiteli přístup k sériovým portům pomocí rozhraní SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externí přístup k poskytovatelům obsahu"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Umožňuje držiteli získat z příkazového řádku přístup k poskytovatelům obsahu. Běžné aplikace by toto oprávnění neměly nikdy požadovat."</string> <string name="save_password_message" msgid="767344687139195790">"Chcete, aby si prohlížeč zapamatoval toto heslo?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Nyní ne"</string> <string name="save_password_remember" msgid="6491879678996749466">"Zapamatovat"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Připojení k síti Wi-Fi se nezdařilo"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" má pomalé připojení k internetu."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Přímé připojení sítě Wi-Fi"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Spustit přímé připojení sítě Wi-Fi. Tato možnost vypne provoz sítě Wi-Fi v režimu klient/hotspot."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Přímé připojení sítě Wi-Fi se nepodařilo spustit."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Přímé připojení sítě Wi-Fi je zapnuto"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Nastavení otevřete dotykem"</string> <string name="accept" msgid="1645267259272829559">"Přijmout"</string> <string name="decline" msgid="2112225451706137894">"Odmítnout"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozvánka odeslána."</string> diff --git a/core/res/res/values-da/strings.xml b/core/res/res/values-da/strings.xml index 4904a994cb7a..ceefe1d43d93 100644 --- a/core/res/res/values-da/strings.xml +++ b/core/res/res/values-da/strings.xml @@ -757,13 +757,15 @@ <string name="permlab_addVoicemail" msgid="5525660026090959044">"tilføj telefonsvarer"</string> <string name="permdesc_addVoicemail" msgid="6604508651428252437">"Tillader, at appen kan tilføje beskeder på din telefonsvarer."</string> <string name="permlab_writeGeolocationPermissions" msgid="5962224158955273932">"skifte tilladelser til geografisk placering i Browser"</string> - <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websteder."</string> + <string name="permdesc_writeGeolocationPermissions" msgid="1083743234522638747">"Tillader, at appen kan ændre browserens tilladelser angående geografisk placering. Ondsindede apps kan benytte dette til at sende oplysninger om placering til vilkårlige websites."</string> <string name="permlab_packageVerificationAgent" msgid="5568139100645829117">"bekræft pakker"</string> <string name="permdesc_packageVerificationAgent" msgid="8437590190990843381">"Tillader, at appen kan bekræfte, at en pakke kan installeres."</string> <string name="permlab_bindPackageVerifier" msgid="4187786793360326654">"bind til en bekræftelse af pakker"</string> <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Tillader, at indehaveren kan sende anmodninger om bekræftelser af pakker. Dette bør aldrig være nødvendigt for almindelige apps."</string> <string name="permlab_serialPort" msgid="546083327654631076">"adgang til serielle porte"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Tillader, at indehaveren kan få adgang til serielle porte ved hjælp af SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"adgang til indholdsleverandører eksternt"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Giver indehaveren adgang til indholdsleverandører fra startsiden. Bør aldrig være nødvendigt for normale apps."</string> <string name="save_password_message" msgid="767344687139195790">"Ønsker du, at browseren skal huske denne adgangskode?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ikke nu"</string> <string name="save_password_remember" msgid="6491879678996749466">"Husk"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kunne ikke oprette forbindelse til Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" har en dårlig internetforbindelse."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. Dette slår Wi-Fi-klient/hotspot fra."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct kunne ikke startes."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct er slået til"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tryk for indstillinger"</string> <string name="accept" msgid="1645267259272829559">"Accepter"</string> <string name="decline" msgid="2112225451706137894">"Afvis"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitationen er sendt"</string> diff --git a/core/res/res/values-de/strings.xml b/core/res/res/values-de/strings.xml index 20ef4d7f7aef..3b3d08d30e98 100644 --- a/core/res/res/values-de/strings.xml +++ b/core/res/res/values-de/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ermöglicht dem Halter, Anfragen für die Paketprüfung zu senden. Sollte nie für normale Apps benötigt werden."</string> <string name="permlab_serialPort" msgid="546083327654631076">"Zugriff auf serielle Schnittstellen"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Ermöglicht dem Inhaber den Zugriff auf serielle Schnittstellen über das SerialManager-API"</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Extern auf Content-Anbieter zugreifen"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Berechtigt den Inhaber, extern auf Content-Anbieter zuzugreifen. Bei normalen Apps nicht notwendig"</string> <string name="save_password_message" msgid="767344687139195790">"Möchten Sie, dass der Browser dieses Passwort speichert?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Nicht jetzt"</string> <string name="save_password_remember" msgid="6491879678996749466">"Speichern"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Es konnte keine WLAN-Verbindung hergestellt werden."</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" hat eine schlechte Internetverbindung."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct-Betrieb starten. Hierdurch wird der WLAN-Client-/-Hotspot-Betrieb deaktiviert."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Starten von Wi-Fi Direct nicht möglich"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ist aktiviert."</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Zum Aufrufen der Einstellungen berühren"</string> <string name="accept" msgid="1645267259272829559">"Akzeptieren"</string> <string name="decline" msgid="2112225451706137894">"Ablehnen"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Einladung gesendet"</string> diff --git a/core/res/res/values-el/strings.xml b/core/res/res/values-el/strings.xml index 200f7024ef8e..e2bc244ad4a3 100644 --- a/core/res/res/values-el/strings.xml +++ b/core/res/res/values-el/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Επιτρέπει στον κάτοχο να υποβάλλει ερωτήματα σε προγράμματα επαλήθευσης πακέτου. Δεν απαιτείται για συνήθεις εφαρμογές."</string> <string name="permlab_serialPort" msgid="546083327654631076">"πρόσβαση στις σειριακές θύρες"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Επιτρέπει στον κάτοχο την πρόσβαση στις σειριακές θύρες με τη χρήση του SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"εξωτερική πρόσβαση σε παρόχους περιεχ."</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Επιτρέπει στον κάτοχο να έχει πρόσβαση στους παρόχους περιεχομένου από το κέλυφος. Να μην απαιτείται ποτέ για τις κανονικές εφαρμογές."</string> <string name="save_password_message" msgid="767344687139195790">"Θέλετε το πρόγραμμα περιήγησης να διατηρήσει αυτόν τον κωδικό πρόσβασης;"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Να μην γίνει τώρα"</string> <string name="save_password_remember" msgid="6491879678996749466">"Διατήρηση"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Δεν είναι δυνατή η σύνδεση στο Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" έχει κακή σύνδεση στο Διαδίκτυο."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Ξεκινήστε τη λειτουργία Wi-Fi Direct. Θα απενεργοποιηθεί η λειτουργία πελάτη/φορητού σημείου πρόσβασης Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Δεν ήταν δυνατή η εκκίνηση του Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Το Wi-Fi Direct έχει ενεργοποιηθεί"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Αγγίξτε για ρυθμίσεις"</string> <string name="accept" msgid="1645267259272829559">"Αποδοχή"</string> <string name="decline" msgid="2112225451706137894">"Απόρριψη"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Η πρόσκληση στάλθηκε"</string> diff --git a/core/res/res/values-en-rGB/strings.xml b/core/res/res/values-en-rGB/strings.xml index 285994297419..bed4de28c014 100644 --- a/core/res/res/values-en-rGB/strings.xml +++ b/core/res/res/values-en-rGB/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Allows the holder to make requests of package verifiers. Should never be needed for normal apps."</string> <string name="permlab_serialPort" msgid="546083327654631076">"access serial ports"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Allows the holder to access serial ports using the SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"access content providers externally"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Allows the holder to access content providers from the shell. Should never be needed for normal apps."</string> <string name="save_password_message" msgid="767344687139195790">"Do you want the browser to remember this password?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Not now"</string> <string name="save_password_remember" msgid="6491879678996749466">"Remember"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Couldn\'t connect to Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" has a poor Internet connection."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. This will turn off Wi-Fi client/hotspot."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Couldn\'t start Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct is on"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Touch for settings"</string> <string name="accept" msgid="1645267259272829559">"Accept"</string> <string name="decline" msgid="2112225451706137894">"Decline"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitation sent"</string> diff --git a/core/res/res/values-es-rUS/strings.xml b/core/res/res/values-es-rUS/strings.xml index dbc96e7cc32f..89f51c8a768f 100644 --- a/core/res/res/values-es-rUS/strings.xml +++ b/core/res/res/values-es-rUS/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que el titular solicite verificadores de paquetes. Las aplicaciones normales no deberían necesitar este permiso."</string> <string name="permlab_serialPort" msgid="546083327654631076">"Acceder a los puertos serie"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de la API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores de contenido externamente"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite a su titular a acceder a los proveedores de contenido desde el shell. Las aplicaciones normales nunca deberían necesitarlo."</string> <string name="save_password_message" msgid="767344687139195790">"¿Quieres recordar esta contraseña en el navegador?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no."</string> <string name="save_password_remember" msgid="6491879678996749466">"Recuerda"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"No se pudo conectar a la red Wi-Fi."</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tiene una mala conexión a Internet."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar Wi-Fi Direct. Se desactivará el funcionamiento de la zona o del cliente Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"No se pudo iniciar Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Se activó Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocar para ajustar los parámetros de configuración"</string> <string name="accept" msgid="1645267259272829559">"Aceptar"</string> <string name="decline" msgid="2112225451706137894">"Rechazar"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Se envió la invitación."</string> diff --git a/core/res/res/values-es/strings.xml b/core/res/res/values-es/strings.xml index d3c55a61bb7f..9499214f6bcd 100644 --- a/core/res/res/values-es/strings.xml +++ b/core/res/res/values-es/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que se envíen solicitudes de detectores de paquetes. Las aplicaciones normales no deberían necesitar este permiso."</string> <string name="permlab_serialPort" msgid="546083327654631076">"acceder a puertos serie"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite acceder a puertos serie a través de SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acceder a proveedores de contenido externamente"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite acceder a los proveedores de contenido desde el shell. Las aplicaciones normales nunca deberían necesitar este permiso."</string> <string name="save_password_message" msgid="767344687139195790">"¿Quieres que el navegador recuerde esta contraseña?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ahora no"</string> <string name="save_password_remember" msgid="6491879678996749466">"Recordar"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"No se ha podido establecer conexión con la red Wi-Fi."</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tiene una conexión inestable a Internet."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar Wi-Fi Direct. Se desactivará el funcionamiento de la zona o del cliente Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"No se ha podido iniciar Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct activado"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toca para acceder a Ajustes"</string> <string name="accept" msgid="1645267259272829559">"Aceptar"</string> <string name="decline" msgid="2112225451706137894">"Rechazar"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitación enviada"</string> diff --git a/core/res/res/values-et/strings.xml b/core/res/res/values-et/strings.xml index c3a61693826d..da06538a88ae 100644 --- a/core/res/res/values-et/strings.xml +++ b/core/res/res/values-et/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lubab omanikul teha taotlusi paketi kinnitajate kohta. Tavarakenduste puhul ei peaks seda kunagi vaja minema."</string> <string name="permlab_serialPort" msgid="546083327654631076">"juurdepääs jadaportidele"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Võimaldab omanikul SerialManageri API-liidese abil jadaportidele juurde pääseda."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"väline juurdepääs sisupakkujatele"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Võimaldab valdajal hankida juurdepääsu sisupakkujatele kesta kaudu. Pole kunagi vajalik tavaliste rakenduste puhul."</string> <string name="save_password_message" msgid="767344687139195790">"Kas soovite, et brauser jätaks selle parooli meelde?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Mitte praegu"</string> <string name="save_password_remember" msgid="6491879678996749466">"Pidage meeles"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ei saanud WiFi-ga ühendust"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" on halb Interneti-ühendus."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"WiFi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käivitage WiFi otseühendus. See lülitab välja WiFi kliendi/leviala."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"WiFi otseühenduse käivitamine ebaõnnestus."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"WiFi Direct on sees"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Puuted seadete jaoks"</string> <string name="accept" msgid="1645267259272829559">"Nõustu"</string> <string name="decline" msgid="2112225451706137894">"Keeldu"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Kutse on saadetud"</string> diff --git a/core/res/res/values-fa/strings.xml b/core/res/res/values-fa/strings.xml index 87eee68d0385..9a6a293a023a 100644 --- a/core/res/res/values-fa/strings.xml +++ b/core/res/res/values-fa/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"به دارنده اجازه میدهد تا تاییدکنندگان بسته را درخواست کند. برای برنامههای عادی نیاز نیست."</string> <string name="permlab_serialPort" msgid="546083327654631076">"دسترسی به درگاههای سریال"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"به دارنده اجازه میدهد با استفاده از SerialManager API به درگاههای سریال دسترسی داشته باشد."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"دسترسی خارجی به ارائهدهندگان محتوا"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"به دارنده اجازه میدهد تا از خارج برنامه به ارائه دهندگان محتوا دسترسی داشته باشد. هرگز برای برنامههای معمولی به آن نیازی نیست."</string> <string name="save_password_message" msgid="767344687139195790">"می خواهید مرورگر این رمز ورود را به خاطر داشته باشد؟"</string> <string name="save_password_notnow" msgid="6389675316706699758">"اکنون خیر"</string> <string name="save_password_remember" msgid="6491879678996749466">"به خاطر سپردن"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"اتصال به Wi-Fi ممکن نیست"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" اتصال اینترنتی ضعیفی دارد."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct را شروع کنید. این کار نقطه اتصال/سرویس گیرنده Wi-Fi را غیرفعال خواهد کرد."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct شروع نشد."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct روشن است"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"لمس کردن برای تنظیمات"</string> <string name="accept" msgid="1645267259272829559">"پذیرش"</string> <string name="decline" msgid="2112225451706137894">"عدم پذیرش"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"دعوتنامه ارسال شد"</string> diff --git a/core/res/res/values-fi/strings.xml b/core/res/res/values-fi/strings.xml index 5ab71583e236..2fe9b4eaa9fa 100644 --- a/core/res/res/values-fi/strings.xml +++ b/core/res/res/values-fi/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Antaa sovelluksen tehdä pakettien vahvistuspyyntöjä. Ei tavallisten sovellusten käyttöön."</string> <string name="permlab_serialPort" msgid="546083327654631076">"käytä sarjaportteja"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Luvan haltija voi käyttää sarjaportteja SerialManager-sovellusliittymän avulla."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"käytä ulkoisia sisällöntarjoajia"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Antaa luvan haltijan käyttää liittymän sisällöntarjoajia. Ei normaalien sovelluksien käyttöön."</string> <string name="save_password_message" msgid="767344687139195790">"Haluatko selaimen muistavan tämän salasanan?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ei nyt"</string> <string name="save_password_remember" msgid="6491879678996749466">"Muista"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wifi-yhteyden muodostaminen epäonnistui"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" : huono internetyhteys."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Suora wifi-yhteys"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Käynnistä suora wifi-yhteys. Wifi-asiakas/-hotspot poistetaan käytöstä."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Suoran wifi-yhteyden käynnistäminen epäonnistui."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct on käytössä"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tarkastele asetuksia koskettamalla"</string> <string name="accept" msgid="1645267259272829559">"Hyväksy"</string> <string name="decline" msgid="2112225451706137894">"Hylkää"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Kutsu lähetetty."</string> diff --git a/core/res/res/values-fr/strings.xml b/core/res/res/values-fr/strings.xml index 606735043a20..d6e8f301a0b9 100644 --- a/core/res/res/values-fr/strings.xml +++ b/core/res/res/values-fr/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permet à l\'application autorisée d\'effectuer des requêtes de vérificateurs de package. Les applications standards ne doivent jamais avoir recours à cette fonctionnalité."</string> <string name="permlab_serialPort" msgid="546083327654631076">"accéder aux ports série"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permet à l\'application autorisée d\'accéder aux ports série avec l\'API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accès externe fournisseurs de contenu"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permettre à l\'application titulaire d\'accéder à des fournisseurs de contenu depuis l\'interface. Les applications standards ne devraient jamais avoir recours à cette autorisation."</string> <string name="save_password_message" msgid="767344687139195790">"Voulez-vous que le navigateur se souvienne de ce mot de passe ?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Pas maintenant"</string> <string name="save_password_remember" msgid="6491879678996749466">"Mémoriser"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Impossible de se connecter au Wi-Fi."</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" dispose d\'une mauvaise connexion Internet."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Lancer le Wi-Fi Direct. Cela désactive le fonctionnement du Wi-Fi client ou via un point d\'accès."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Impossible d\'activer le Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct activé"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Appuyez pour accéder aux paramètres."</string> <string name="accept" msgid="1645267259272829559">"Accepter"</string> <string name="decline" msgid="2112225451706137894">"Refuser"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitation envoyée"</string> diff --git a/core/res/res/values-hi/strings.xml b/core/res/res/values-hi/strings.xml index 1aaedbd3d0ab..f5a3cb76a6b7 100644 --- a/core/res/res/values-hi/strings.xml +++ b/core/res/res/values-hi/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"धारक को पैकेज प्रमाणक के अनुरोध की अनुमति देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यक नहीं होना चाहिए."</string> <string name="permlab_serialPort" msgid="546083327654631076">"सीरियल पोर्ट पर पहुंचें"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API का उपयोग करके धारक को सीरियल पोर्ट पर पहुंच प्रदान करता है."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"बाह्य रूप से सामग्री प्रदाताओं पर पहुंच"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"धारक को शेल से सामग्री प्रदाताओं तक पहुंचने देता है. सामान्य एप्लिकेशन के लिए कभी भी आवश्यकता नहीं होनी चाहिए."</string> <string name="save_password_message" msgid="767344687139195790">"क्या आप चाहते हैं कि ब्राउज़र पासवर्ड को याद रखे?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"अभी नहीं"</string> <string name="save_password_remember" msgid="6491879678996749466">"याद रखें"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi से कनेक्ट नहीं हो सका"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" के पास एक कमज़ोर इंटरनेट कनेक्शन है."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi प्रत्यक्ष"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi डायरेक्ट प्रारंभ करें. इससे Wi-Fi क्लाइंट/हॉटस्पॉट कार्यवाही बंद हो जाएगी."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi डायरेक्ट प्रारंभ नहीं किया जा सका."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi प्रत्यक्ष चालू है"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"सेटिंग के लिए स्पर्श करें"</string> <string name="accept" msgid="1645267259272829559">"स्वीकार करें"</string> <string name="decline" msgid="2112225451706137894">"अस्वीकार करें"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"आमंत्रण भेजा गया"</string> diff --git a/core/res/res/values-hr/strings.xml b/core/res/res/values-hr/strings.xml index 07759844531a..01378666557e 100644 --- a/core/res/res/values-hr/strings.xml +++ b/core/res/res/values-hr/strings.xml @@ -215,8 +215,8 @@ <string name="permdesc_reorderTasks" msgid="4175137612205663399">"Omogućuje aplikaciji da premjesti zadatke u prednji plan ili pozadinu. Zlonamjerne aplikacije mogu na silu doći u prednji plan bez vašeg nadzora."</string> <string name="permlab_removeTasks" msgid="6821513401870377403">"zaustavljanje pokrenutih aplikacija"</string> <string name="permdesc_removeTasks" msgid="1394714352062635493">"Omogućuje aplikaciji uklanjanje zadataka i uklanjanje njihovih aplikacija. Zlonamjerne aplikacije mogu poremetiti rad drugih aplikacija."</string> - <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"postavljanje zaslona kompatibilnost"</string> - <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Aplikaciji omogućuje upravljanje načinom kompatibilnosti zaslona drugih aplikacija. Zlonamjerne aplikacije mogu prekinuti takvo ponašanje ostalih aplikacija."</string> + <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"postavljanje kompatibilnosti sa zaslonom"</string> + <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Aplikaciji omogućuje upravljanje načinom kompatibilnosti aplikacija sa zaslonom. Zlonamjerne aplikacije mogu prekinuti takvo ponašanje ostalih aplikacija."</string> <string name="permlab_setDebugApp" msgid="3022107198686584052">"omogućavanje rješavanja programskih pogrešaka u aplikaciji"</string> <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Omogućuje aplikaciji uključivanje uklanjanja programskih pogrešaka za drugu aplikaciju. Zlonamjerne aplikacije mogu na taj način ukloniti druge aplikacije."</string> <string name="permlab_changeConfiguration" msgid="8214475779521218295">"promjena postavki korisničkog sučelja"</string> @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Nositelju omogućuje da traži paketnu provjeru. Ne bi smjelo biti potrebno za normalne aplikacije."</string> <string name="permlab_serialPort" msgid="546083327654631076">"pristup serijskim priključcima"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Rukovatelju omogućuje pristup serijskim priključcima pomoću značajke SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"pristup pružateljima sadržaja izvana"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Omogućuje vlasniku pristup pružateljima sadržaja iz programske ovojnice. Ne bi trebalo biti potrebno za normalne aplikacije."</string> <string name="save_password_message" msgid="767344687139195790">"Želite li da preglednik zapamti ovu zaporku?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ne sada"</string> <string name="save_password_remember" msgid="6491879678996749466">"Zapamti"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ne može se spojiti na Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ima lošu internetsku vezu."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Izravni Wi-Fi"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Pokreni izravan rad s Wi-Fi mrežom. To će isključiti rad s Wi-Fi klijentom/žarišnom točkom."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Pokretanje izravne Wi-Fi veze nije moguće."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct uključen"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dodirnite za postavke"</string> <string name="accept" msgid="1645267259272829559">"Prihvaćam"</string> <string name="decline" msgid="2112225451706137894">"Odbaci"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozivnica je poslana"</string> diff --git a/core/res/res/values-hu/strings.xml b/core/res/res/values-hu/strings.xml index e125ba8d51b3..54e8b06b4e8d 100644 --- a/core/res/res/values-hu/strings.xml +++ b/core/res/res/values-hu/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lehetővé teszi, hogy a tulajdonos kérelmeket nyújtson be a csomag hitelesítőivel kapcsolatban. A normál alkalmazásoknak erre soha nincs szüksége."</string> <string name="permlab_serialPort" msgid="546083327654631076">"soros portok elérése"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Lehetővé teszi a tulajdonos számára a soros portok elérését a SerialManager API segítségével."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"tartalomszolgáltatók külső elérése"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Lehetővé teszi, hogy a tulajdonos hozzáférjen a tartalomszolgáltatókhoz a shellből. Normál alkalmazásoknál nem szükséges."</string> <string name="save_password_message" msgid="767344687139195790">"Szeretné, hogy a böngésző megjegyezze a jelszót?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Most nem"</string> <string name="save_password_remember" msgid="6491879678996749466">"Megjegyzés"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nem sikerült csatlakozni a Wi-Fi hálózathoz"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" rossz internetkapcsolattal rendelkezik."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct elindítása. A Wi-Fi kliens/hotspot ettől leáll."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nem sikerült elindítani a Wi-Fi Direct kapcsolatot."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"A Wi-Fi Direct be van kapcsolva"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"A beállításokhoz érintse meg"</string> <string name="accept" msgid="1645267259272829559">"Elfogadás"</string> <string name="decline" msgid="2112225451706137894">"Elutasítás"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Meghívó elküldve"</string> diff --git a/core/res/res/values-in/strings.xml b/core/res/res/values-in/strings.xml index 72fcb507f652..ac5d46310a7a 100644 --- a/core/res/res/values-in/strings.xml +++ b/core/res/res/values-in/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Mengizinkan pemegang mengajukan permintaan pemverifikasian paket. Tidak pernah dibutuhkan oleh apl normal."</string> <string name="permlab_serialPort" msgid="546083327654631076">"akses port serial"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Memungkinkan pemegangnya mengakses port serial menggunakan API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"mengakses penyedia konten dari luar"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Memungkinkan pemegang mengakses penyedia konten dari cangkang. Tidak pernah diperlukan untuk apl normal."</string> <string name="save_password_message" msgid="767344687139195790">"Apakah Anda ingin peramban menyimpan sandi ini?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Tidak sekarang"</string> <string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Tidak dapat tersambung ke Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" memiliki sambungan internet yang buruk."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Langsung"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Memulai Wi-Fi Langsung. Opsi ini akan mematikan hotspot/klien Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Tidak dapat memulai Wi-Fi Langsung."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Langsung aktif"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Sentuh untuk setelan"</string> <string name="accept" msgid="1645267259272829559">"Terima"</string> <string name="decline" msgid="2112225451706137894">"Tolak"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Undangan terkirim"</string> diff --git a/core/res/res/values-it/strings.xml b/core/res/res/values-it/strings.xml index ec9457b5bef5..77733b313e13 100644 --- a/core/res/res/values-it/strings.xml +++ b/core/res/res/values-it/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Consente al proprietario di effettuare richieste relative alle verifiche dei pacchetti. Non dovrebbe mai essere necessario per le normali applicazioni."</string> <string name="permlab_serialPort" msgid="546083327654631076">"accesso alle porte seriali"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permette al proprietario di accedere alle porte seriali utilizzando l\'API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accesso a fornitori di contenuti esterni"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Consente al proprietario di accedere ai fornitori di contenuti dalla shell. Non dovrebbe mai essere necessario per le normali applicazioni."</string> <string name="save_password_message" msgid="767344687139195790">"Memorizzare la password nel browser?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Non ora"</string> <string name="save_password_remember" msgid="6491879678996749466">"Memorizza"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Impossibile connettersi alla rete Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ha una connessione Internet debole."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Avvia Wi-Fi Direct. Verrà disattivato il client/hotspot Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Avvio di Wi-Fi Direct non riuscito."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct è attivo"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocca per le impostazioni"</string> <string name="accept" msgid="1645267259272829559">"Accetto"</string> <string name="decline" msgid="2112225451706137894">"Rifiuto"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invito inviato"</string> diff --git a/core/res/res/values-iw/strings.xml b/core/res/res/values-iw/strings.xml index 5fc15c720ac9..a232c69c6388 100644 --- a/core/res/res/values-iw/strings.xml +++ b/core/res/res/values-iw/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"מאפשר למשתמש להגיש בקשות של מאמתי חבילות. הרשאה זו לעולם אינה נחוצה ליישומים רגילים."</string> <string name="permlab_serialPort" msgid="546083327654631076">"גישה ליציאות טוריות"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"מאפשר לבעלים לגשת ליציאות טוריות באמצעות ממשק ה- API של SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"גישה לספקי תוכן באופן חיצוני"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"מאפשר לבעלים לגשת לספקי תוכן מהמעטפת. לעולם לא אמור להיות צורך עבור יישומים רגילים."</string> <string name="save_password_message" msgid="767344687139195790">"האם ברצונך שהדפדפן יזכור סיסמה זו?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"לא כעת"</string> <string name="save_password_remember" msgid="6491879678996749466">"זכור"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"אין אפשרות להתחבר ל-Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" אינו מחובר היטב לאינטרנט."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi ישיר"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"הפעל Wi-Fi ישיר. פעולה זו תכבה את הלקוח/הנקודה החמה של ה-Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"לא ניתן להפעיל Wi-Fi ישיר"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ישיר מופעל"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"גע עבור הגדרות"</string> <string name="accept" msgid="1645267259272829559">"קבל"</string> <string name="decline" msgid="2112225451706137894">"דחה"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"ההזמנה נשלחה"</string> diff --git a/core/res/res/values-ja/strings.xml b/core/res/res/values-ja/strings.xml index 63b8c73f7a3c..66e847ae034b 100644 --- a/core/res/res/values-ja/strings.xml +++ b/core/res/res/values-ja/strings.xml @@ -216,7 +216,7 @@ <string name="permlab_removeTasks" msgid="6821513401870377403">"実行中のアプリの停止"</string> <string name="permdesc_removeTasks" msgid="1394714352062635493">"タスクの削除とアプリの終了をアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、他のアプリの動作が妨害される恐れがあります。"</string> <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"画面互換性の設定"</string> - <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"他のアプリケーションの画面互換性モードをコントロールすることをアプリに許可します。この許可を悪意のあるアプリケーションに利用されると、他のアプリケーションの動作が中断される恐れがあります。"</string> + <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"他のアプリの画面互換性モードをコントロールすることをアプリに許可します。この許可を悪意のあるアプリに利用されると、他のアプリの動作が中断される恐れがあります。"</string> <string name="permlab_setDebugApp" msgid="3022107198686584052">"アプリのデバッグの有効化"</string> <string name="permdesc_setDebugApp" msgid="4474512416299013256">"別のアプリをデバッグモードにすることをアプリに許可します。この許可を悪意のあるアプリに利用されると、他のアプリが強制終了される恐れがあります。"</string> <string name="permlab_changeConfiguration" msgid="8214475779521218295">"UI設定の変更"</string> @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"パッケージベリファイアのリクエストを所有者に許可します。通常のアプリでは不要です。"</string> <string name="permlab_serialPort" msgid="546083327654631076">"シリアルポートへのアクセス"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager APIを使用してシリアルポートにアクセスすることを所有者に許可します。"</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"コンテンツプロバイダへの外部アクセス"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"シェルからコンテンツプロバイダにアクセスすることを権利所有者に許可します。通常のアプリでは必要ありません。"</string> <string name="save_password_message" msgid="767344687139195790">"このパスワードをブラウザで保存しますか?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"今は保存しない"</string> <string name="save_password_remember" msgid="6491879678996749466">"保存"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fiに接続できませんでした"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" はインターネット接続に問題があります。"</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Directを開始します。これによりWi-Fiクライアント/アクセスポイントがOFFになります。"</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Directを開始できませんでした。"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi DirectはONです"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"設定を表示するにはタップしてください"</string> <string name="accept" msgid="1645267259272829559">"同意する"</string> <string name="decline" msgid="2112225451706137894">"同意しない"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"招待状を送信しました"</string> diff --git a/core/res/res/values-ko/strings.xml b/core/res/res/values-ko/strings.xml index f97cbdcd6497..754f79187ff2 100644 --- a/core/res/res/values-ko/strings.xml +++ b/core/res/res/values-ko/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"권한을 가진 프로그램이 패키지 인증을 요청할 수 있도록 허용합니다. 일반 앱에는 필요하지 않습니다."</string> <string name="permlab_serialPort" msgid="546083327654631076">"직렬 포트에 액세스"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"SerialManager API를 사용하여 권한을 가진 프로그램이 직렬 포트에 액세스할 수 있도록 합니다."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"외부에서 콘텐츠 제공자에 액세스"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"권한을 가진 프로그램이 셸에서 콘텐츠 제공자에 액세스하도록 허용합니다. 일반 앱에서는 필요하지 않습니다."</string> <string name="save_password_message" msgid="767344687139195790">"브라우저에 이 비밀번호를 저장하시겠습니까?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"나중에"</string> <string name="save_password_remember" msgid="6491879678996749466">"저장"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Wi-Fi에 연결할 수 없습니다"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 인터넷 연결 상태가 좋지 않습니다."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wi-Fi Direct 작업을 시작합니다. 이 작업을 하면 Wi-Fi 클라이언트/핫스팟 작업이 중지됩니다."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct를 시작하지 못했습니다."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct 켜짐"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"설정으로 이동하려면 터치하세요."</string> <string name="accept" msgid="1645267259272829559">"동의"</string> <string name="decline" msgid="2112225451706137894">"거부"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"초대장을 보냈습니다."</string> diff --git a/core/res/res/values-large/themes.xml b/core/res/res/values-large/themes.xml index 871a131a5af4..448e7c873c11 100644 --- a/core/res/res/values-large/themes.xml +++ b/core/res/res/values-large/themes.xml @@ -33,17 +33,17 @@ please see themes_device_defaults.xml. --> <resources> <style name="Theme.Holo.DialogWhenLarge" - parent="@android:style/Theme.Holo.Dialog.MinWidth"> + parent="@android:style/Theme.Holo.Dialog.FixedSize"> <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> </style> <style name="Theme.Holo.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.Holo.Dialog.NoActionBar.MinWidth"> + parent="@android:style/Theme.Holo.Dialog.NoActionBar.FixedSize"> <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> </style> <style name="Theme.Holo.Light.DialogWhenLarge" - parent="@android:style/Theme.Holo.Light.Dialog.MinWidth"> + parent="@android:style/Theme.Holo.Light.Dialog.FixedSize"> </style> <style name="Theme.Holo.Light.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar.MinWidth"> + parent="@android:style/Theme.Holo.Light.Dialog.NoActionBar.FixedSize"> </style> </resources> diff --git a/core/res/res/values-large/themes_device_defaults.xml b/core/res/res/values-large/themes_device_defaults.xml index 52fff5c135f3..d57e827e89c3 100644 --- a/core/res/res/values-large/themes_device_defaults.xml +++ b/core/res/res/values-large/themes_device_defaults.xml @@ -32,17 +32,17 @@ easier. --> <resources> <style name="Theme.DeviceDefault.DialogWhenLarge" - parent="@android:style/Theme.DeviceDefault.Dialog.MinWidth"> + parent="@android:style/Theme.DeviceDefault.Dialog.FixedSize"> <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> </style> <style name="Theme.DeviceDefault.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar.MinWidth"> + parent="@android:style/Theme.DeviceDefault.Dialog.NoActionBar.FixedSize"> <item name="preferencePanelStyle">@style/PreferencePanel.Dialog</item> </style> <style name="Theme.DeviceDefault.Light.DialogWhenLarge" - parent="@android:style/Theme.DeviceDefault.Light.Dialog.MinWidth"> + parent="@android:style/Theme.DeviceDefault.Light.Dialog.FixedSize"> </style> <style name="Theme.DeviceDefault.Light.DialogWhenLarge.NoActionBar" - parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth"> + parent="@android:style/Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize"> </style> </resources> diff --git a/core/res/res/values-lt/strings.xml b/core/res/res/values-lt/strings.xml index a1694afb6e26..43aa1d3359be 100644 --- a/core/res/res/values-lt/strings.xml +++ b/core/res/res/values-lt/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Savininkui leidžiama teikti užklausas patikrinti paketą. Įprastoms programoms to neturėtų prireikti."</string> <string name="permlab_serialPort" msgid="546083327654631076">"pasiekti nuosekliuosius prievadus"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Leidžiama savininkui pasiekti nuosekliuosius prievadus naudojant „SerialManager“ API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"pasiekti turinio teikėjus iš išorės"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Leidžiama savininkui pasiekti turinio teikėjus naudojant apvalkalą. To niekada neturėtų prireikti naudojant įprastas programas."</string> <string name="save_password_message" msgid="767344687139195790">"Ar norite, kad naršyklė atsimintų šį slaptažodį?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ne dabar"</string> <string name="save_password_remember" msgid="6491879678996749466">"Atsiminti"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nepavyko prisijungti prie „Wi-Fi“"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" turi prastą interneto ryšį."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Tiesioginis „Wi-Fi“ ryšys"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Paleiskite „Wi-Fi Direct“. Bus išjungta „Wi-Fi“ programa / viešosios interneto prieigos taškas."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nepavyko paleisti „Wi-Fi Direct“."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"„Wi-Fi Direct“ įjungta"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Jei norite peržiūrėti nustatymus, palieskite"</string> <string name="accept" msgid="1645267259272829559">"Sutikti"</string> <string name="decline" msgid="2112225451706137894">"Atmesti"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pakvietimas išsiųstas"</string> diff --git a/core/res/res/values-lv/strings.xml b/core/res/res/values-lv/strings.xml index aeec70b31b14..5f84a4885253 100644 --- a/core/res/res/values-lv/strings.xml +++ b/core/res/res/values-lv/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ļauj īpašniekam sūtīt pakotņu verificētāju pieprasījumus. Parastajām lietotnēm tas nekad nav nepieciešams."</string> <string name="permlab_serialPort" msgid="546083327654631076">"piekļuve seriālajiem portiem"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Ļauj īpašniekam piekļūt seriālajiem portiem, izmantojot SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"ārēji piekļūt satura nodrošinātājiem"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Ļauj īpašniekam no čaulas piekļūt satura nodrošinātājiem. Nekad nav nepieciešama parastām lietotnēm."</string> <string name="save_password_message" msgid="767344687139195790">"Vai vēlaties, lai pārlūkprogrammā tiktu saglabāta šī parole?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ne tagad"</string> <string name="save_password_remember" msgid="6491879678996749466">"Atcerēties"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nevarēja izveidot savienojumu ar Wi-Fi."</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ir slikts interneta savienojums."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Palaist programmu Wi-Fi Direct. Tādējādi tiks izslēgta Wi-Fi klienta/tīklāja darbība."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nevarēja palaist programmu Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ir ieslēgts"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Pieskarieties, lai piekļūtu iestatījumiem."</string> <string name="accept" msgid="1645267259272829559">"Piekrist"</string> <string name="decline" msgid="2112225451706137894">"Noraidīt"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Ielūgums ir nosūtīts."</string> diff --git a/core/res/res/values-ms/strings.xml b/core/res/res/values-ms/strings.xml index 0610e88c6b9f..3f240ab24307 100644 --- a/core/res/res/values-ms/strings.xml +++ b/core/res/res/values-ms/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Membenarkan pemegang membuat permintaan pengesah pakej. Tidak sekali-kali diperlukan untuk apl normal."</string> <string name="permlab_serialPort" msgid="546083327654631076">"akses port bersiri"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Membenarkan pemegang mengakses port bersiri menggunakan API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"akses pembekal kandungan secara luaran"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Membolehkan pemegang mengakses pembekal kandungan dari luar. Tidak akan sekali-kali diperlukan untuk apl biasa."</string> <string name="save_password_message" msgid="767344687139195790">"Adakah anda mahu penyemak imbas mengingati kata laluan ini?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Bukan sekarang"</string> <string name="save_password_remember" msgid="6491879678996749466">"Ingat"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Tidak boleh menyambung kepada Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" mempunyai sambungan internet yang kurang baik."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Langsung"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Mulakan Wi-Fi Langsung. Hal ini akan mematikan pengendalian klien/liputan Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Tidak dapat memulakan Wi-Fi Langsung."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct dihidupkan"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Sentuh untuk tetapan"</string> <string name="accept" msgid="1645267259272829559">"Terima"</string> <string name="decline" msgid="2112225451706137894">"Tolak"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Jemputan dihantar"</string> diff --git a/core/res/res/values-nb/strings.xml b/core/res/res/values-nb/strings.xml index e82718ef3aa5..63df5f466a79 100644 --- a/core/res/res/values-nb/strings.xml +++ b/core/res/res/values-nb/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Lar innehaveren sende forespørsler om pakkeverifikatorer. Skal aldri være nødvendig for normale apper."</string> <string name="permlab_serialPort" msgid="546083327654631076">"tilgang til serielle porter"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Gir innehaveren tilgang til serielle porter ved hjelp av SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"gå til innholdsleverandører eksternt"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Lar innehaveren gå til innholdsleverandører fra kommandovinduet. Skal aldri være nødvendig for vanlige apper."</string> <string name="save_password_message" msgid="767344687139195790">"Ønsker du at nettleseren skal huske dette passordet?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ikke nå"</string> <string name="save_password_remember" msgid="6491879678996749466">"Husk"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan ikke koble til Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" har en dårlig Internett-tilkobling."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Start Wi-Fi Direct. Dette deaktiverer Wi-Fi-klienten/-sonen."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kunne ikke starte Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct er slått på"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Berør for å se innstillinger"</string> <string name="accept" msgid="1645267259272829559">"Godta"</string> <string name="decline" msgid="2112225451706137894">"Avslå"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitasjonen er sendt"</string> diff --git a/core/res/res/values-nl/strings.xml b/core/res/res/values-nl/strings.xml index ef4dd6a67591..8e5e8bc54e59 100644 --- a/core/res/res/values-nl/strings.xml +++ b/core/res/res/values-nl/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Hiermee kan de houder verzoeken indienen voor pakketcontroles. Nooit vereist voor normale apps."</string> <string name="permlab_serialPort" msgid="546083327654631076">"toegang krijgen tot seriële poorten"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"De houder toestaan toegang tot seriële poorten te krijgen met de SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"externe toegang tot inhoudsproviders"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Hiermee kan de houder toegang krijgen tot inhoudsproviders via de shell. Nooit vereist voor normale apps."</string> <string name="save_password_message" msgid="767344687139195790">"Wilt u dat de browser dit wachtwoord onthoudt?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Niet nu"</string> <string name="save_password_remember" msgid="6491879678996749466">"Onthouden"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kan geen verbinding maken met Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" heeft een slechte internetverbinding."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Wifi Direct starten. Hierdoor wordt de wifi-client/hotspot uitgeschakeld."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kan Wifi Direct niet starten."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct is actief"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Aanraken voor instellingen"</string> <string name="accept" msgid="1645267259272829559">"Accepteren"</string> <string name="decline" msgid="2112225451706137894">"Weigeren"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Uitnodiging verzonden"</string> diff --git a/core/res/res/values-pl/strings.xml b/core/res/res/values-pl/strings.xml index f231d897d9c5..c17da8ff4714 100644 --- a/core/res/res/values-pl/strings.xml +++ b/core/res/res/values-pl/strings.xml @@ -216,7 +216,7 @@ <string name="permlab_removeTasks" msgid="6821513401870377403">"zatrzymywanie uruchomionych aplikacji"</string> <string name="permdesc_removeTasks" msgid="1394714352062635493">"Umożliwia aplikacji usuwanie zadań i kończenie powiązanych z nimi aplikacji. Złośliwe aplikacje mogą zakłócić działanie innych aplikacji."</string> <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"ustaw zgodność ekranu"</string> - <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Pozwala aplikacji na sterowanie trybem zgodności ekranu innych aplikacji. Złośliwe aplikacje mogą zmienić zachowanie innych aplikacji."</string> + <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"Pozwala aplikacji na sterowanie trybem zgodności ekranu innych aplikacji. Złośliwe aplikacje mogą zmienić zachowanie innych programów."</string> <string name="permlab_setDebugApp" msgid="3022107198686584052">"włączenie debugowania aplikacji"</string> <string name="permdesc_setDebugApp" msgid="4474512416299013256">"Pozwala aplikacji na włączenie debugowania innej aplikacji. Złośliwe aplikacje mogą to wykorzystać do kończenia pracy innych programów."</string> <string name="permlab_changeConfiguration" msgid="8214475779521218295">"zmienianie ustawień interfejsu użytkownika"</string> @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pozwala na wysyłanie żądań weryfikacji pakietu. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string> <string name="permlab_serialPort" msgid="546083327654631076">"dostęp do portów szeregowych"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Umożliwia posiadaczowi dostęp do portów szeregowych przy użyciu interfejsu API narzędzia SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"Dostęp do dostawców treści z zewnątrz"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Pozwala na dostęp do dostawców treści z powłoki. To uprawnienie nie powinno być potrzebne zwykłym aplikacjom."</string> <string name="save_password_message" msgid="767344687139195790">"Czy chcesz, aby zapamiętać to hasło w przeglądarce?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Nie teraz"</string> <string name="save_password_remember" msgid="6491879678996749466">"Zapamiętaj"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nie można połączyć się z siecią Wi-Fi."</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ma powolne połączenie internetowe."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Uruchom Wi-Fi Direct. Spowoduje to wyłączenie klienta lub punktu dostępu Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Nie można uruchomić Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct włączone"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotknij, aby zmienić ustawienia"</string> <string name="accept" msgid="1645267259272829559">"Akceptuj"</string> <string name="decline" msgid="2112225451706137894">"Odrzuć"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Wysłano zaproszenie"</string> diff --git a/core/res/res/values-pt-rPT/strings.xml b/core/res/res/values-pt-rPT/strings.xml index f3f0600904cf..74870ffa98d1 100644 --- a/core/res/res/values-pt-rPT/strings.xml +++ b/core/res/res/values-pt-rPT/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite ao titular solicitar verificadores de pacotes. Nunca deverá ser necessário para aplicações normais."</string> <string name="permlab_serialPort" msgid="546083327654631076">"aceder a portas série"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite ao titular aceder a portas de série através da API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"aceder a fornecedores de conteúdos externamente"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite ao titular aceder a fornecedores de conteúdos a partir da shell. Nunca deverá ser necessário para aplicações normais."</string> <string name="save_password_message" msgid="767344687139195790">"Quer que o browser memorize esta palavra-passe?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Agora não"</string> <string name="save_password_remember" msgid="6491879678996749466">"Lembrar"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Não foi possível ligar a Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tem uma ligação à internet fraca."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar o Wi-Fi Direct. Esta opção desativará o cliente/zona Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Não foi possível iniciar o Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"O Wi-Fi Direct está ativado"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Toque para aceder às definições"</string> <string name="accept" msgid="1645267259272829559">"Aceitar"</string> <string name="decline" msgid="2112225451706137894">"Recusar"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Convite enviado"</string> diff --git a/core/res/res/values-pt/strings.xml b/core/res/res/values-pt/strings.xml index b49c36b61e20..efe6833ff715 100644 --- a/core/res/res/values-pt/strings.xml +++ b/core/res/res/values-pt/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite que o titular solicite verificadores de pacote. Nunca deve ser necessário para aplicativos normais."</string> <string name="permlab_serialPort" msgid="546083327654631076">"acessar portas seriais"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite que o detentor tenha acesso a portas seriais usando a API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"acessar fornec. de conteúdo externamente"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite que o proprietário tenha acesso a fornecedores de conteúdo a partir da camada. Nunca deve ser necessário para aplicativos normais."</string> <string name="save_password_message" msgid="767344687139195790">"Deseja que o navegador lembre desta senha?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Agora não"</string> <string name="save_password_remember" msgid="6491879678996749466">"Lembrar"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Não foi possível se conectar a redes Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" tem uma conexão de baixa qualidade com a Internet."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Iniciar o Wi-Fi Direct. Isso desativará o ponto de acesso/cliente Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Não foi possível iniciar o Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct ativado"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tocar para acessar configurações"</string> <string name="accept" msgid="1645267259272829559">"Aceitar"</string> <string name="decline" msgid="2112225451706137894">"Recusar"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Convite enviado"</string> diff --git a/core/res/res/values-rm/strings.xml b/core/res/res/values-rm/strings.xml index 1447c0e085d2..5b8efa61ed6c 100644 --- a/core/res/res/values-rm/strings.xml +++ b/core/res/res/values-rm/strings.xml @@ -1155,6 +1155,10 @@ <skip /> <!-- no translation found for permdesc_serialPort (2991639985224598193) --> <skip /> + <!-- no translation found for permlab_accessContentProvidersExternally (5077774297943409285) --> + <skip /> + <!-- no translation found for permdesc_accessContentProvidersExternally (4544346486697853685) --> + <skip /> <string name="save_password_message" msgid="767344687139195790">"Vulais Vus ch\'il navigatur memorisescha quest pled-clav?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Betg ussa"</string> <string name="save_password_remember" msgid="6491879678996749466">"Memorisar"</string> @@ -1418,6 +1422,16 @@ <skip /> <!-- no translation found for wifi_watchdog_network_disabled_detailed (5548780776418332675) --> <skip /> + <!-- no translation found for wifi_p2p_dialog_title (97611782659324517) --> + <skip /> + <!-- no translation found for wifi_p2p_turnon_message (2909250942299627244) --> + <skip /> + <!-- no translation found for wifi_p2p_failed_message (3763669677935623084) --> + <skip /> + <!-- no translation found for wifi_p2p_enabled_notification_title (2068321881673734886) --> + <skip /> + <!-- no translation found for wifi_p2p_enabled_notification_message (1638949953993894335) --> + <skip /> <!-- no translation found for accept (1645267259272829559) --> <skip /> <!-- no translation found for decline (2112225451706137894) --> diff --git a/core/res/res/values-ro/strings.xml b/core/res/res/values-ro/strings.xml index f241e8e91139..8ffa16e9f5f7 100644 --- a/core/res/res/values-ro/strings.xml +++ b/core/res/res/values-ro/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Permite proprietarului să efectueze solicitări pentru verificatori de pachete. Nu ar trebui să fie niciodată necesară pentru aplicaţiile obişnuite."</string> <string name="permlab_serialPort" msgid="546083327654631076">"acces la porturi seriale"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Permite posesorului accesul la porturile serial utilizând API-ul SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"accesaţi furniz. de conţin. din exterior"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Permite deţinătorului să acceseze furnizorii de conţinut din interfaţă. Nu ar trebui să fie necesară pentru aplicaţiile normale."</string> <string name="save_password_message" msgid="767344687139195790">"Doriţi ca browserul să reţină această parolă?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Nu acum"</string> <string name="save_password_remember" msgid="6491879678996749466">"Reţineţi"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nu se poate conecta la Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" are o conexiune la internet slabă."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Porniţi Wi-Fi Direct. Acest lucru va dezactiva clientul/hotspotul Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct nu a putut porni."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct este activat"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Atingeţi pentru setări"</string> <string name="accept" msgid="1645267259272829559">"Acceptaţi"</string> <string name="decline" msgid="2112225451706137894">"Refuzaţi"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Invitaţia a fost trimisă."</string> diff --git a/core/res/res/values-ru/strings.xml b/core/res/res/values-ru/strings.xml index 809da8324edc..a6644e5094f4 100644 --- a/core/res/res/values-ru/strings.xml +++ b/core/res/res/values-ru/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Приложение сможет запрашивать проверку пакетов. Это разрешение не используется обычными приложениями."</string> <string name="permlab_serialPort" msgid="546083327654631076">"доступ к последовательным портам"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Открыть владельцу доступ к последовательным портам с помощью SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"доступ к контенту без приложения"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Владелец сможет получить доступ к контенту без использования приложения. Это разрешение не применяется в обычных приложениях."</string> <string name="save_password_message" msgid="767344687139195790">"Вы хотите, чтобы браузер запомнил этот пароль?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Не сейчас"</string> <string name="save_password_remember" msgid="6491879678996749466">"Запомнить"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не удалось подключиться к сети Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" – плохое интернет-соединение."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Начать соединение через Wi-Fi Direct. Модуль Wi-Fi будет отключен."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Не удалось запустить Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct включен"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Нажмите, чтобы открыть настройки"</string> <string name="accept" msgid="1645267259272829559">"Принять"</string> <string name="decline" msgid="2112225451706137894">"Отклонить"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Приглашение отправлено"</string> diff --git a/core/res/res/values-sk/strings.xml b/core/res/res/values-sk/strings.xml index 7d75949ee008..df25ac7a296a 100644 --- a/core/res/res/values-sk/strings.xml +++ b/core/res/res/values-sk/strings.xml @@ -764,6 +764,10 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Umožňuje držiteľovi podávať žiadosti o overenie balíkov. Bežné aplikácie by toto nastavenie nemali nikdy potrebovať."</string> <string name="permlab_serialPort" msgid="546083327654631076">"prístup k sériovým portom"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Držiteľa oprávňuje na prístup k sériovým portom pomocou rozhrania API SerialManager."</string> + <!-- no translation found for permlab_accessContentProvidersExternally (5077774297943409285) --> + <skip /> + <!-- no translation found for permdesc_accessContentProvidersExternally (4544346486697853685) --> + <skip /> <string name="save_password_message" msgid="767344687139195790">"Chcete, aby si prehliadač zapamätal toto heslo?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Teraz nie"</string> <string name="save_password_remember" msgid="6491879678996749466">"Zapamätať"</string> @@ -973,6 +977,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Nepodarilo sa pripojiť k sieti Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" má nekvalitné internetové pripojenie."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Priame pripojenie Wi-Fi"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Spustiť priame pripojenie siete Wi-Fi. Táto možnosť vypne sieť Wi-Fi v režime klient alebo hotspot."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Priame pripojenie siete Wi-Fi sa nepodarilo spustiť"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Priame pripojenie siete Wi-Fi je zapnuté"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Nastavenia otvoríte dotykom"</string> <string name="accept" msgid="1645267259272829559">"Prijať"</string> <string name="decline" msgid="2112225451706137894">"Odmietnuť"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Pozvánka bola odoslaná"</string> diff --git a/core/res/res/values-sl/strings.xml b/core/res/res/values-sl/strings.xml index e152a2f7b817..81e1e6f24082 100644 --- a/core/res/res/values-sl/strings.xml +++ b/core/res/res/values-sl/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Imetniku omogoča zahtevanje preverjanja paketov. Tega nikoli ni treba uporabiti za navadne programe."</string> <string name="permlab_serialPort" msgid="546083327654631076">"dostop do serijskih vrat"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Imetniku omogoča, da z API-jem za SerialManager dostopa do serijskih vrat."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"zunanji dostop do ponudnikov vsebine"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Omogoča imetniku, da dostopa do ponudnikov vsebine iz lupine. Naj ne bi bilo nikoli potrebno za običajne programe"</string> <string name="save_password_message" msgid="767344687139195790">"Ali želite, da si brskalnik zapomni to geslo?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Ne zdaj"</string> <string name="save_password_remember" msgid="6491879678996749466">"Zapomni si"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Z omrežjem Wi-Fi se ni mogoče povezati"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ima slabo internetno povezavo."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Zaženite Wi-Fi Direct. S tem boste izklopili odjemalca/dostopno točko Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Wi-Fi Direct ni bilo mogoče zagnati."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct je vklopljen"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Dotaknite se za nastavitve"</string> <string name="accept" msgid="1645267259272829559">"Sprejmi"</string> <string name="decline" msgid="2112225451706137894">"Zavrni"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Povabilo je poslano"</string> diff --git a/core/res/res/values-sr/strings.xml b/core/res/res/values-sr/strings.xml index 7f1dbcddc0a0..199d318c0821 100644 --- a/core/res/res/values-sr/strings.xml +++ b/core/res/res/values-sr/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Омогућава да власник упућује захтеве верификаторима пакета. Уобичајене апликације никада не би требало да је користе."</string> <string name="permlab_serialPort" msgid="546083327654631076">"приступ серијским портовима"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Омогућава власнику да приступи серијским портовима помоћу SerialManager API-ја."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"приступ добављачима садржаја споља"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Дозвољава власнику да приступа добављачима садржаја из интерфејса. Никада не би требало да буде потребно за обичне апликације."</string> <string name="save_password_message" msgid="767344687139195790">"Желите ли да прегледач запамти ову лозинку?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Не сада"</string> <string name="save_password_remember" msgid="6491879678996749466">"Запамти"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Није било могуће повезати са Wi-Fi мрежом"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" има лошу интернет везу."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Покрените Wi-Fi Direct. Тиме ћете искључити клијента/хотспот за Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Није могуће покренути Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct је укључен"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Додирните за подешавања"</string> <string name="accept" msgid="1645267259272829559">"Прихвати"</string> <string name="decline" msgid="2112225451706137894">"Одбиј"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Позивница је послата"</string> diff --git a/core/res/res/values-sv/strings.xml b/core/res/res/values-sv/strings.xml index 10ecffc82728..be397b0ebe7d 100644 --- a/core/res/res/values-sv/strings.xml +++ b/core/res/res/values-sv/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Tillåter att innehavaren skickar förfrågningar till paketverifierare. Det ska inte behövas för vanliga appar."</string> <string name="permlab_serialPort" msgid="546083327654631076">"åtkomst till serieportar"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Innebär att innehavaren får åtkomst till serieportar med programmeringsgränssnittet för SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"komma åt innehållsleverantörer externt"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Innehavaren kan få åtkomst till innehållsleverantörer från skalet. Ska inte behövas för vanliga appar."</string> <string name="save_password_message" msgid="767344687139195790">"Vill du att webbläsaren ska komma ihåg lösenordet?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Inte nu"</string> <string name="save_password_remember" msgid="6491879678996749466">"Kom ihåg"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Det gick inte att ansluta till Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" har en dålig Internetanslutning."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi direkt"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Starta direkt Wi-Fi-användning. Detta inaktiverar Wi-Fi-användning med klient/trådlös surfzon."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Det gick inte att starta Wi-Fi direkt."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct är aktiverat"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Tryck om du vill visa inställningar"</string> <string name="accept" msgid="1645267259272829559">"Godkänn"</string> <string name="decline" msgid="2112225451706137894">"Avvisa"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Inbjudan har skickats"</string> diff --git a/core/res/res/values-sw/strings.xml b/core/res/res/values-sw/strings.xml index d245685571d4..9005a9b50e05 100644 --- a/core/res/res/values-sw/strings.xml +++ b/core/res/res/values-sw/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Inaruhusu mmiliki kutuma maombi ya vibainishi furushi. Kamwe hazitahitajika kwa programu za kawaida."</string> <string name="permlab_serialPort" msgid="546083327654631076">"kituo tambulishi cha ufikivu"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Inaruhusu mmiliki kufikia vituo tambulishi kwa kutumia KisimamiziTambulishi cha API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"fikia watoa huduma nje"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Inaruhusu mmiliki kufikia watoa huduma kutoka kwa onyesho. Haifai kuhitajika kwa programu za kawaida."</string> <string name="save_password_message" msgid="767344687139195790">"Unataka kuvinjari ili ukumbuke nenosiri hili?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Si Sasa"</string> <string name="save_password_remember" msgid="6491879678996749466">"Kumbuka"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Haikuweza kuunganisha kwa Mtandao-Hewa"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ina muunganisho duni wa Mtandao."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Mtandao hewa Moja kwa moja"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Anzisha Wi-Fi Moja kwa Moja. Hii itazima mteja/mtandao-hewa wa Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Haikuweza kuanzisha Wi-Fi Moja kwa Moja."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi ya Moja kwa Moja imewashwa"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Gusa kwa ajili ya mipangilio"</string> <string name="accept" msgid="1645267259272829559">"Kubali"</string> <string name="decline" msgid="2112225451706137894">"Kataa"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Mwaliko umetumwa"</string> diff --git a/core/res/res/values-sw600dp/dimens.xml b/core/res/res/values-sw600dp/dimens.xml index 431a502bd937..13acdd6c499f 100644 --- a/core/res/res/values-sw600dp/dimens.xml +++ b/core/res/res/values-sw600dp/dimens.xml @@ -68,5 +68,19 @@ <!-- Minimum width for an action button in the menu area of an action bar --> <dimen name="action_button_min_width">64dip</dimen> + + <!-- The platform's desired fixed width for a dialog along the major axis + (the screen is in landscape). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_width_major">50%</item> + <!-- The platform's desired fixed width for a dialog along the minor axis + (the screen is in portrait). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_width_minor">70%</item> + <!-- The platform's desired fixed height for a dialog along the major axis + (the screen is in portrait). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_height_major">60%</item> + <!-- The platform's desired fixed height for a dialog along the minor axis + (the screen is in landscape). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_height_minor">90%</item> + </resources> diff --git a/core/res/res/values-th/strings.xml b/core/res/res/values-th/strings.xml index 97f1c70cb863..41c56f473111 100644 --- a/core/res/res/values-th/strings.xml +++ b/core/res/res/values-th/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"อนุญาตให้ผู้ใช้ส่งคำขอให้มีการยืนยันแพคเกจ ไม่ควรต้องใช้สำหรับแอปพลิเคชันทั่วไป"</string> <string name="permlab_serialPort" msgid="546083327654631076">"เข้าถึงพอร์ตอนุกรม"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"อนุญาตให้ผู้ถือสามารถเข้าถึงพอร์ตอนุกรมโดยใช้ SerialManager API"</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"เข้าถึงผู้ให้บริการเนื้อหาจากภายนอก"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"ช่วยให้เจ้าของสามารถเข้าถึงผู้ให้บริการเนื้อหาจากหน้าจอรับคำสั่งเชลล์ แอปพลิเคชันทั่วไปไม่จำเป็นต้องใช้"</string> <string name="save_password_message" msgid="767344687139195790">"คุณต้องการให้เบราว์เซอร์จำรหัสผ่านนี้หรือไม่"</string> <string name="save_password_notnow" msgid="6389675316706699758">"ยังไม่ใช้งานขณะนี้"</string> <string name="save_password_remember" msgid="6491879678996749466">"จำไว้"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"ไม่สามารถเชื่อมต่อ Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" มีสัญญาณอินเทอร์เน็ตไม่ดี"</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"เริ่มการทำงาน WiFi Direct ซึ่งจะเป็นการปิดการทำงาน WiFi ไคลเอ็นต์/ฮอตสปอต"</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"ไม่สามารถเริ่ม WiFi Direct ได้"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"เปิด Wi-Fi Direct อยู่"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"แตะเพื่อตั้งค่า"</string> <string name="accept" msgid="1645267259272829559">"ยอมรับ"</string> <string name="decline" msgid="2112225451706137894">"ปฏิเสธ"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"ส่งข้อความเชิญแล้ว"</string> diff --git a/core/res/res/values-tl/strings.xml b/core/res/res/values-tl/strings.xml index f59cde4360f4..9548fe90584b 100644 --- a/core/res/res/values-tl/strings.xml +++ b/core/res/res/values-tl/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Pinapayagan ang may-ari na gumawa ng mga kahilingan ng mga taga-verify ng package. Hindi kailanman dapat na kailanganin para sa normal na apps."</string> <string name="permlab_serialPort" msgid="546083327654631076">"mag-access sa mga serial port"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Binibigyang-daan ang may-ari na mag-access ng mga serial port gamit ang SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"panlabas na mag-access ng mga provider ng nilalaman"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Binibigyang-daan ang may-ari na ma-access ang mga provider ng nilalaman mula sa shell. Hindi kailanman dapat kailanganin para sa karaniwang apps."</string> <string name="save_password_message" msgid="767344687139195790">"Gusto mo bang tandaan ng browser ang password na ito?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Hindi ngayon"</string> <string name="save_password_remember" msgid="6491879678996749466">"Tandaan"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Hindi makakonekta sa Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" ay mayroong mahinang koneksyon sa Internet."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Simulan ang Wi-Fi Direct. I-o-off nito ang client/hotspot ng Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Hindi masimulan ang Wi-Fi Direct"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Ang Wi-Fi Direct ay naka-on"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Pindutin para sa mga setting"</string> <string name="accept" msgid="1645267259272829559">"Tanggapin"</string> <string name="decline" msgid="2112225451706137894">"Tanggihan"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Naipadala ang imbitasyon"</string> diff --git a/core/res/res/values-tr/strings.xml b/core/res/res/values-tr/strings.xml index bcaa9a8989a8..6da7fcdeea2f 100644 --- a/core/res/res/values-tr/strings.xml +++ b/core/res/res/values-tr/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Cihazın sahibine, paket doğrulayıcıları için istek yapma izni verir. Normal uygulamalar için gerekli olmaz."</string> <string name="permlab_serialPort" msgid="546083327654631076">"seri bağlantı noktalarına eriş"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"İzin sahibinin, SerialManager API\'sını kullanarak seri bağlantı noktalarına erişmesine olanak sağlar."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"içerik sağlayıcılara harici olarak eriş"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"İzin verilen uygulamaya, Uygulama İş Parçacığının dışından içerik sağlayıcılara erişebilme olanağı verir."</string> <string name="save_password_message" msgid="767344687139195790">"Tarayıcının bu şifreyi anımsamasını istiyor musunuz?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Şimdi değil"</string> <string name="save_password_remember" msgid="6491879678996749466">"Anımsa"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Kablosuz bağlantısı kurulamadı"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" İnternet bağlantısı zayıf."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Kablosuz Doğrudan Bağlantı"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Kablosuz Doğrudan Bağlantıyı başlat. Bu işlem, Kablosuz istemci/hotspot kullanımını kapatacak."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Kablosuz Doğrudan bağlantı başlatılamadı."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Kablosuz Doğrudan özelliği açık"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Ayarlar için dokunun"</string> <string name="accept" msgid="1645267259272829559">"Kabul et"</string> <string name="decline" msgid="2112225451706137894">"Reddet"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Davetiye gönderildi"</string> diff --git a/core/res/res/values-uk/strings.xml b/core/res/res/values-uk/strings.xml index 6288aa16cd99..3fd626476ae0 100644 --- a/core/res/res/values-uk/strings.xml +++ b/core/res/res/values-uk/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Дозволяє власникові робити запити на програми перевірки пакетів. Ніколи не застосовується для звичайних програм."</string> <string name="permlab_serialPort" msgid="546083327654631076">"отримувати доступ до послідовних портів"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Дозволяє власнику отримувати доступ до послідовних портів за допомогою API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"отримув. ззовні доступ до постач. вмісту"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Дозволяє власнику отримувати ззовні доступ до постачальників вмісту. Ніколи не застосовується для звичайних програм."</string> <string name="save_password_message" msgid="767344687139195790">"Хочете, щоб переглядач запам\'ятав цей пароль?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Не зараз"</string> <string name="save_password_remember" msgid="6491879678996749466">"Запам\'ятати"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Не вдалося під’єднатися до мережі Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" має погане з’єднання з Інтернетом."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Запустити Wi-Fi Direct. Це вимкне з’єднання Wi-Fi клієнт/точка доступу."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Не вдалося запустити Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct увімкнено"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Торкніться, щоб побачити налаштування"</string> <string name="accept" msgid="1645267259272829559">"Прийняти"</string> <string name="decline" msgid="2112225451706137894">"Відхилити"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Запрошення надіслано"</string> diff --git a/core/res/res/values-vi/strings.xml b/core/res/res/values-vi/strings.xml index ba81838dbf6a..8503dc56c8fe 100644 --- a/core/res/res/values-vi/strings.xml +++ b/core/res/res/values-vi/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Cho phép chủ sở hữu yêu cầu trình xác minh gói. Không cần thiết cho các ứng dụng thông thường."</string> <string name="permlab_serialPort" msgid="546083327654631076">"truy cập cổng nối tiếp"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Cho phép chủ sở hữu truy cập cổng nối tiếp sử dụng API SerialManager."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"truy cập vào nhà cung cấp nội dung từ bên ngoài"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Cho phép chủ sở hữu truy cập vào nhà cung cấp nội dung từ bên ngoài. Không bao giờ cần cho ứng dụng thông thường."</string> <string name="save_password_message" msgid="767344687139195790">"Bạn có muốn trình duyệt nhớ mật khẩu này không?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Không phải bây giờ"</string> <string name="save_password_remember" msgid="6491879678996749466">"Nhớ"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Không thể kết nối với Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" có kết nối Internet không tốt."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Khởi động Wi-Fi Direct. Việc này sẽ tắt hoạt động của ứng dụng khách/điểm phát sóng Wi-Fi."</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Không thể khởi động Wi-Fi Direct."</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct được bật"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Chạm để cài đặt"</string> <string name="accept" msgid="1645267259272829559">"Đồng ý"</string> <string name="decline" msgid="2112225451706137894">"Từ chối"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Đã gửi thư mời"</string> diff --git a/core/res/res/values-zh-rCN/strings.xml b/core/res/res/values-zh-rCN/strings.xml index cbfc222f737d..bc74518610bf 100644 --- a/core/res/res/values-zh-rCN/strings.xml +++ b/core/res/res/values-zh-rCN/strings.xml @@ -216,7 +216,7 @@ <string name="permlab_removeTasks" msgid="6821513401870377403">"停止正在运行的应用程序"</string> <string name="permdesc_removeTasks" msgid="1394714352062635493">"允许该应用程序删除任务并终止这些任务的应用程序。恶意应用程序可以籍此影响其他应用程序的行为。"</string> <string name="permlab_setScreenCompatibility" msgid="6975387118861842061">"设置屏幕兼容性"</string> - <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允许应用程序控制其他应用程序的屏幕兼容模式。恶意应用程序可以籍此影响其他应用程序的行为。"</string> + <string name="permdesc_setScreenCompatibility" msgid="692043618693917374">"允许该应用程序控制其他应用程序的屏幕兼容模式。恶意应用程序可以籍此影响其他应用程序的行为。"</string> <string name="permlab_setDebugApp" msgid="3022107198686584052">"启用应用程序调试"</string> <string name="permdesc_setDebugApp" msgid="4474512416299013256">"允许该应用程序对其他应用程序启用调试。恶意应用程序可以籍此终止其他的应用程序。"</string> <string name="permlab_changeConfiguration" msgid="8214475779521218295">"更改用户界面设置"</string> @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允许用户请求使用程序包验证程序。普通应用程序绝不需要此权限。"</string> <string name="permlab_serialPort" msgid="546083327654631076">"访问串行端口"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"允许持有人使用 SerialManager API 访问串行端口。"</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"从外部访问内容提供程序"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允许持有者通过界面访问内容提供程序。普通应用程序绝不需要此权限。"</string> <string name="save_password_message" msgid="767344687139195790">"是否希望浏览器记住此密码?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"暂不保存"</string> <string name="save_password_remember" msgid="6491879678996749466">"记住"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"无法连接到 Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 互联网连接状况不佳。"</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"启动 Wi-Fi Direct。此操作将会关闭 Wi-Fi 客户端/热点。"</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"无法启动 Wi-Fi Direct。"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"已启用 Wi-Fi Direct"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"通过触摸进行设置"</string> <string name="accept" msgid="1645267259272829559">"接受"</string> <string name="decline" msgid="2112225451706137894">"拒绝"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"邀请已发送"</string> diff --git a/core/res/res/values-zh-rTW/strings.xml b/core/res/res/values-zh-rTW/strings.xml index 4d6042c713f9..5c89004cb660 100644 --- a/core/res/res/values-zh-rTW/strings.xml +++ b/core/res/res/values-zh-rTW/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"允許應用程式要求驗證套件 (一般應用程式不需使用)。"</string> <string name="permlab_serialPort" msgid="546083327654631076">"存取序列埠"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"允許應用程式使用 SerialManager API 存取序列埠。"</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"從外部存取內容供應端"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"允許應用程式透過文字指令介面存取內容供應端 (一般應用程式不需這項權限)。"</string> <string name="save_password_message" msgid="767344687139195790">"是否記住此密碼?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"現在不要"</string> <string name="save_password_remember" msgid="6491879678996749466">"記住"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"無法連線至 Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" 的網際網路連線狀況不佳。"</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"Wi-Fi Direct"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"啟動 WiFi Direct 作業,這會關閉 WiFi 用戶端/無線基地台作業。"</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"無法啟動 WiFi Direct。"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"Wi-Fi Direct 已開啟"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"輕觸即可設定"</string> <string name="accept" msgid="1645267259272829559">"接受"</string> <string name="decline" msgid="2112225451706137894">"拒絕"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"邀請函已傳送"</string> diff --git a/core/res/res/values-zu/strings.xml b/core/res/res/values-zu/strings.xml index c90fe7665e55..b3cb9a07ca05 100644 --- a/core/res/res/values-zu/strings.xml +++ b/core/res/res/values-zu/strings.xml @@ -764,6 +764,8 @@ <string name="permdesc_bindPackageVerifier" msgid="3180741773233862126">"Ivumela umnikazi ukuthi enze izicelo zezinsiza eziqinisekisa iphakheji. Akumele kudingeke ekusetshenzisweni okujwayelekile."</string> <string name="permlab_serialPort" msgid="546083327654631076">"finyelela kuma- serial port"</string> <string name="permdesc_serialPort" msgid="2991639985224598193">"Ivumela umnikai ukuthi athole inombolo ye-serial ukue angene kwiindawo ze-serial esebenzisa i-SerialManager API."</string> + <string name="permlab_accessContentProvidersExternally" msgid="5077774297943409285">"finyelela ngaphandle abahlinzeki bokuqukethwe"</string> + <string name="permdesc_accessContentProvidersExternally" msgid="4544346486697853685">"Ivumela umphathi ukufinyelela abahlinzeki bokuqukethwe kusuka kumasistimu asebenzayo. Akusoze kwadingeka kwizinhlelo zokusebenza ezivamile."</string> <string name="save_password_message" msgid="767344687139195790">"Ingabe ufuna ukuba isiphequluli sikhumbule lephasiwedi?"</string> <string name="save_password_notnow" msgid="6389675316706699758">"Hha yi manje"</string> <string name="save_password_remember" msgid="6491879678996749466">"Khumbula"</string> @@ -973,6 +975,11 @@ <skip /> <string name="wifi_watchdog_network_disabled" msgid="7904214231651546347">"Ayikwazanga ukuxhuma kwi-Wi-Fi"</string> <string name="wifi_watchdog_network_disabled_detailed" msgid="5548780776418332675">" inoxhumano oluphansi lwe-inthanethi."</string> + <string name="wifi_p2p_dialog_title" msgid="97611782659324517">"I-WiFi Eqondile"</string> + <string name="wifi_p2p_turnon_message" msgid="2909250942299627244">"Qala ukusebenza kwe-WiFi Okuqondile. Lokhu kuzocima ikhasimende le-WiFi/Ukusebenza okwe-hotspot"</string> + <string name="wifi_p2p_failed_message" msgid="3763669677935623084">"Yehlulekile ukuqala i-Wi-Fi Ngqo"</string> + <string name="wifi_p2p_enabled_notification_title" msgid="2068321881673734886">"I-Wi-Fi Direct ivulekile"</string> + <string name="wifi_p2p_enabled_notification_message" msgid="1638949953993894335">"Thinta ukuze uthole izilungiselelo"</string> <string name="accept" msgid="1645267259272829559">"Yamukela"</string> <string name="decline" msgid="2112225451706137894">"Nqaba"</string> <string name="wifi_p2p_invitation_sent_title" msgid="1318975185112070734">"Isimemo sithunyelwe"</string> diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index 16b7ff36d92c..93757303a9ae 100755 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -1556,6 +1556,24 @@ an absolute dimension or a fraction of the screen size in that dimension. --> <attr name="windowMinWidthMinor" format="dimension|fraction" /> + + <!-- A fixed width for the window along the major axis of the screen, + that is, when in landscape. Can be either an absolute dimension + or a fraction of the screen size in that dimension. --> + <attr name="windowFixedWidthMajor" format="dimension|fraction" /> + <!-- A fixed height for the window along the minor axis of the screen, + that is, when in landscape. Can be either an absolute dimension + or a fraction of the screen size in that dimension. --> + <attr name="windowFixedHeightMinor" format="dimension|fraction" /> + + <!-- A fixed width for the window along the minor axis of the screen, + that is, when in portrait. Can be either an absolute dimension + or a fraction of the screen size in that dimension. --> + <attr name="windowFixedWidthMinor" format="dimension|fraction" /> + <!-- A fixed height for the window along the major axis of the screen, + that is, when in portrait. Can be either an absolute dimension + or a fraction of the screen size in that dimension. --> + <attr name="windowFixedHeightMajor" format="dimension|fraction" /> </declare-styleable> <!-- The set of attributes that describe a AlertDialog's theme. --> @@ -2981,7 +2999,10 @@ <attr name="textColorLink" /> <!-- Makes the cursor visible (the default) or invisible. --> <attr name="cursorVisible" format="boolean" /> - <!-- Makes the TextView be at most this many lines tall. --> + <!-- Makes the TextView be at most this many lines tall. + + When used on an editable text, the <code>inputType</code> attribute's value must be + combined with the <code>textMultiLine</code> flag for the maxLines attribute to apply. --> <attr name="maxLines" format="integer" min="0" /> <!-- Makes the TextView be at most this many pixels tall. --> <attr name="maxHeight" /> @@ -2991,7 +3012,10 @@ You could get the same effect by specifying this number in the layout parameters. --> <attr name="height" format="dimension" /> - <!-- Makes the TextView be at least this many lines tall. --> + <!-- Makes the TextView be at least this many lines tall. + + When used on an editable text, the <code>inputType</code> attribute's value must be + combined with the <code>textMultiLine</code> flag for the minLines attribute to apply. --> <attr name="minLines" format="integer" min="0" /> <!-- Makes the TextView be at least this many pixels tall. --> <attr name="minHeight" /> @@ -3022,21 +3046,20 @@ <!-- Constrains the text to a single horizontally scrolling line instead of letting it wrap onto multiple lines, and advances focus instead of inserting a newline when you press the - enter key. Note: for editable text views, it is better - to control this using the textMultiLine flag in the inputType - attribute. (If both singleLine and inputType are supplied, - the inputType flags will override the value of singleLine.) - {@deprecated This attribute is deprecated and is replaced by the textMultiLine flag - in the inputType attribute. Use caution when altering existing layouts, as the - default value of singeLine is false (multi-line mode), but if you specify any - value for inputType, the default is single-line mode. (If both singleLine and - inputType attributes are found, the inputType flags will override the value of - singleLine.) } --> + enter key. + + The default value is false (multi-line wrapped text mode) for non-editable text, but if + you specify any value for inputType, the default is true (single-line input field mode). + + {@deprecated This attribute is deprecated. Use <code>maxLines</code> instead to change + the layout of a static text, and use the <code>textMultiLine</code> flag in the + inputType attribute instead for editable text views (if both singleLine and inputType + are supplied, the inputType flags will override the value of singleLine). } --> <attr name="singleLine" format="boolean" /> <!-- Specifies whether the TextView is enabled or not. {@deprecated Use state_enabled instead}. --> <attr name="enabled" format="boolean" /> <!-- If the text is selectable, select it all when the view takes - focus instead of moving the cursor to the start or end. --> + focus. --> <attr name="selectAllOnFocus" format="boolean" /> <!-- Leave enough room for ascenders and descenders instead of using the font ascent and descent strictly. (Normally true). --> diff --git a/core/res/res/values/dimens.xml b/core/res/res/values/dimens.xml index 82ef68aef302..6d6b86bcdf32 100644 --- a/core/res/res/values/dimens.xml +++ b/core/res/res/values/dimens.xml @@ -108,6 +108,20 @@ is along the major axis (that is the screen is landscape). This may be either a fraction or a dimension. --> <item type="dimen" name="dialog_min_width_major">65%</item> + + <!-- The platform's desired fixed width for a dialog along the major axis + (the screen is in landscape). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_width_major">320dp</item> + <!-- The platform's desired fixed width for a dialog along the minor axis + (the screen is in portrait). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_width_minor">320dp</item> + <!-- The platform's desired fixed height for a dialog along the major axis + (the screen is in portrait). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_height_major">80%</item> + <!-- The platform's desired fixed height for a dialog along the minor axis + (the screen is in landscape). This may be either a fraction or a dimension.--> + <item type="dimen" name="dialog_fixed_height_minor">100%</item> + <!-- Preference activity, vertical padding for the header list --> <dimen name="preference_screen_header_vertical_padding">0dp</dimen> diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index 589e78fa4a93..6a887db4f23f 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -212,6 +212,10 @@ <java-symbol type="attr" name="textAppearanceMisspelledSuggestion" /> <java-symbol type="attr" name="textColorSearchUrl" /> <java-symbol type="attr" name="timePickerStyle" /> + <java-symbol type="attr" name="windowFixedWidthMajor" /> + <java-symbol type="attr" name="windowFixedWidthMinor" /> + <java-symbol type="attr" name="windowFixedHeightMajor" /> + <java-symbol type="attr" name="windowFixedHeightMinor" /> <java-symbol type="bool" name="action_bar_embed_tabs" /> <java-symbol type="bool" name="action_bar_expanded_action_views_exclusive" /> @@ -958,6 +962,7 @@ <java-symbol type="drawable" name="tab_bottom_right_v4" /> <java-symbol type="drawable" name="tab_indicator_v4" /> <java-symbol type="drawable" name="text_select_handle_left" /> + <java-symbol type="drawable" name="text_select_handle_middle" /> <java-symbol type="drawable" name="text_select_handle_right" /> <java-symbol type="drawable" name="unknown_image" /> <java-symbol type="drawable" name="unlock_default" /> @@ -1439,6 +1444,7 @@ <java-symbol type="anim" name="push_down_out" /> <java-symbol type="anim" name="push_up_in" /> <java-symbol type="anim" name="push_up_out" /> + <java-symbol type="bool" name="config_alwaysUseCdmaRssi" /> <java-symbol type="dimen" name="status_bar_icon_size" /> <java-symbol type="dimen" name="system_bar_icon_size" /> <java-symbol type="drawable" name="list_selector_pressed_holo_dark" /> @@ -1476,7 +1482,6 @@ <java-symbol type="string" name="usb_storage_stop_notification_title" /> <java-symbol type="string" name="usb_storage_stop_title" /> <java-symbol type="string" name="usb_storage_title" /> - <java-symbol type="bool" name="config_alwaysUseCdmaRssi" /> <java-symbol type="style" name="Animation.RecentApplications" /> <!-- ImfTest --> diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index 7111d00a6f32..3c1f50d11b6d 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -2248,6 +2248,14 @@ <!-- Description of an application permission which allows the application access serial ports via the SerialManager. [CHAR LIMIT=NONE] --> <string name="permdesc_serialPort">Allows the holder to access serial ports using the SerialManager API.</string> + <!-- Title of an application permission which allows the holder to access content + providers from outside an ApplicationThread. [CHAR LIMIT=40] --> + <string name="permlab_accessContentProvidersExternally">access content providers externally</string> + <!-- Description of an application permission which allows the holder to access + content providers from outside an ApplicationThread. [CHAR LIMIT=NONE] --> + <string name="permdesc_accessContentProvidersExternally">Allows the holder to access content + providers from the shell. Should never be needed for normal apps.</string> + <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Text in the save password dialog, asking if the browser should remember a password. --> <string name="save_password_message">Do you want the browser to remember this password?</string> <!-- If the user enters a password in a form on a website, a dialog will come up asking if they want to save the password. Button in the save password dialog, saying not to remember this password. --> diff --git a/core/res/res/values/themes.xml b/core/res/res/values/themes.xml index 7046fc551002..55438b221267 100644 --- a/core/res/res/values/themes.xml +++ b/core/res/res/values/themes.xml @@ -1580,6 +1580,22 @@ please see themes_device_defaults.xml. <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item> </style> + <!-- Variant of Theme.Holo.Dialog that has a fixed size. --> + <style name="Theme.Holo.Dialog.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + + <!-- Variant of Theme.Holo.Dialog.NoActionBar that has a fixed size. --> + <style name="Theme.Holo.Dialog.NoActionBar.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + <!-- Variant of Theme.Holo.Dialog that does not include a frame (or background). The view hierarchy of the dialog is responsible for drawing all of its pixels. --> @@ -1672,6 +1688,22 @@ please see themes_device_defaults.xml. <item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item> </style> + <!-- Variant of Theme.Holo.Light.Dialog that has a fixed size. --> + <style name="Theme.Holo.Light.Dialog.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + + <!-- Variant of Theme.Holo.Light.Dialog.NoActionBar that has a fixed size. --> + <style name="Theme.Holo.Light.Dialog.NoActionBar.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + <!-- Theme for a window that will be displayed either full-screen on smaller screens (small, normal) or as a dialog on larger screens (large, xlarge). --> diff --git a/core/res/res/values/themes_device_defaults.xml b/core/res/res/values/themes_device_defaults.xml index abe4aad71e4e..7fd981c30aa6 100644 --- a/core/res/res/values/themes_device_defaults.xml +++ b/core/res/res/values/themes_device_defaults.xml @@ -379,6 +379,23 @@ easier. <style name="Theme.DeviceDefault.Dialog.NoActionBar.MinWidth" parent="Theme.Holo.Dialog.NoActionBar.MinWidth" > </style> + + <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> + <style name="Theme.DeviceDefault.Dialog.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + + <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. --> + <style name="Theme.DeviceDefault.Dialog.NoActionBar.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + <!-- DeviceDefault light theme for dialog windows and activities. This changes the window to be floating (not fill the entire screen), and puts a frame around its contents. You can set this theme on an activity if you would like to make an activity that looks like a Dialog.--> @@ -406,6 +423,23 @@ easier. <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.MinWidth" parent="Theme.Holo.Light.Dialog.NoActionBar.MinWidth" > </style> + + <!-- Variant of Theme.DeviceDefault.Dialog that has a fixed size. --> + <style name="Theme.DeviceDefault.Light.Dialog.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + + <!-- Variant of Theme.DeviceDefault.Dialog.NoActionBar that has a fixed size. --> + <style name="Theme.DeviceDefault.Light.Dialog.NoActionBar.FixedSize"> + <item name="windowFixedWidthMajor">@android:dimen/dialog_fixed_width_major</item> + <item name="windowFixedWidthMinor">@android:dimen/dialog_fixed_width_minor</item> + <item name="windowFixedHeightMajor">@android:dimen/dialog_fixed_height_major</item> + <item name="windowFixedHeightMinor">@android:dimen/dialog_fixed_height_minor</item> + </style> + <!-- DeviceDefault theme for a window that will be displayed either full-screen on smaller screens (small, normal) or as a dialog on larger screens (large, xlarge). --> <style name="Theme.DeviceDefault.DialogWhenLarge" parent="Theme.Holo.DialogWhenLarge" > diff --git a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java index 9819c54aad6d..9c1922ff71e7 100644 --- a/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java +++ b/core/tests/ConnectivityManagerTest/src/com/android/connectivitymanagertest/ConnectivityManagerTestRunner.java @@ -46,8 +46,7 @@ public class ConnectivityManagerTestRunner extends InstrumentationTestRunner { // create a new test suite suite.setName("ConnectivityManagerWifiOnlyFunctionalTests"); String[] methodNames = {"testConnectToWifi", "testConnectToWifWithKnownAP", - "testDisconnectWifi", "testDataConnectionOverAMWithWifi", - "testDataConnectionWithWifiToAMToWifi", "testWifiStateChange"}; + "testDisconnectWifi", "testWifiStateChange"}; Class<ConnectivityManagerMobileTest> testClass = ConnectivityManagerMobileTest.class; for (String method: methodNames) { suite.addTest(TestSuite.createTest(testClass, method)); diff --git a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java index 7499f68f87f6..8d778c46b5cf 100644 --- a/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java +++ b/core/tests/bandwidthtests/src/com/android/bandwidthtest/util/ConnectionUtil.java @@ -44,6 +44,8 @@ import com.android.bandwidthtest.NetworkState; import com.android.bandwidthtest.NetworkState.StateTransitionDirection; import com.android.internal.util.AsyncChannel; +import junit.framework.Assert; + import java.io.IOException; import java.net.UnknownHostException; import java.util.List; @@ -453,6 +455,11 @@ public class ConnectionUtil { } catch (InterruptedException e) { e.printStackTrace(); } + if (mNetworkInfo == null) { + Log.v(LOG_TAG, "Do not have networkInfo! Force fetch of network info."); + mNetworkInfo = mCM.getActiveNetworkInfo(); + Assert.assertNotNull(mNetworkInfo); + } if ((mNetworkInfo.getType() != networkType) || (mNetworkInfo.getState() != expectedState)) { Log.v(LOG_TAG, "network state for " + mNetworkInfo.getType() + diff --git a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java index 2fb42379fe5d..3dc140b4ce0a 100644 --- a/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java +++ b/core/tests/coretests/src/android/accessibilityservice/InterrogationActivityTest.java @@ -50,6 +50,12 @@ public class InterrogationActivityTest // Timeout for the accessibility state of an Activity to be fully initialized. private static final int TIMEOUT_PROPAGATE_ACCESSIBILITY_EVENT_MILLIS = 5000; + // Timeout for which non getting accessibility events considers the app idle. + private static final long IDLE_EVENT_TIME_DELTA_MILLIS = 200; + + // Timeout in which to wait for idle device. + private static final long GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS = 1000; + // Handle to a connection to the AccessibilityManagerService private UiTestAutomationBridge mUiTestAutomationBridge; @@ -62,6 +68,8 @@ public class InterrogationActivityTest super.setUp(); mUiTestAutomationBridge = new UiTestAutomationBridge(); mUiTestAutomationBridge.connect(); + mUiTestAutomationBridge.waitForIdle(IDLE_EVENT_TIME_DELTA_MILLIS, + GLOBAL_IDLE_DETECTION_TIMEOUT_MILLIS); mUiTestAutomationBridge.executeCommandAndWaitForAccessibilityEvent(new Runnable() { // wait for the first accessibility event @Override diff --git a/data/etc/platform.xml b/data/etc/platform.xml index 6cd07a31ccc5..8be1db2bb1f6 100644 --- a/data/etc/platform.xml +++ b/data/etc/platform.xml @@ -158,6 +158,7 @@ <assign-permission name="android.permission.BACKUP" uid="shell" /> <assign-permission name="android.permission.FORCE_STOP_PACKAGES" uid="shell" /> <assign-permission name="android.permission.STOP_APP_SWITCHES" uid="shell" /> + <assign-permission name="android.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY" uid="shell" /> <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" /> <assign-permission name="android.permission.ACCESS_DRM" uid="media" /> diff --git a/data/fonts/DroidNaskh-Bold.ttf b/data/fonts/DroidNaskh-Bold.ttf Binary files differindex 6b7d4f0b39d3..692b79678170 100644 --- a/data/fonts/DroidNaskh-Bold.ttf +++ b/data/fonts/DroidNaskh-Bold.ttf diff --git a/data/fonts/DroidNaskh-Regular.ttf b/data/fonts/DroidNaskh-Regular.ttf Binary files differindex d11e1aebd04e..da9a45f150b9 100644 --- a/data/fonts/DroidNaskh-Regular.ttf +++ b/data/fonts/DroidNaskh-Regular.ttf diff --git a/data/fonts/DroidSansFallback.ttf b/data/fonts/DroidSansFallback.ttf Binary files differindex 2379b2d758a1..cfbc66ad0470 100644 --- a/data/fonts/DroidSansFallback.ttf +++ b/data/fonts/DroidSansFallback.ttf diff --git a/data/fonts/DroidSansFallbackFull.ttf b/data/fonts/DroidSansFallbackFull.ttf Binary files differindex 41b015d5a052..0cacabedcaed 100644 --- a/data/fonts/DroidSansFallbackFull.ttf +++ b/data/fonts/DroidSansFallbackFull.ttf diff --git a/drm/java/android/drm/DrmInfo.java b/drm/java/android/drm/DrmInfo.java index 8812bfe5a11e..22d06c7bd20e 100755 --- a/drm/java/android/drm/DrmInfo.java +++ b/drm/java/android/drm/DrmInfo.java @@ -49,6 +49,13 @@ public class DrmInfo { mInfoType = infoType; mMimeType = mimeType; mData = data; + if (!isValid()) { + final String msg = "infoType: " + infoType + "," + + "mimeType: " + mimeType + "," + + "data: " + data; + + throw new IllegalArgumentException(msg); + } } /** @@ -69,6 +76,13 @@ public class DrmInfo { // call would fail with IllegalArgumentException because of mData = null mData = null; } + if (!isValid()) { + final String msg = "infoType: " + infoType + "," + + "mimeType: " + mimeType + "," + + "data: " + mData; + + throw new IllegalArgumentException(); + } } /** diff --git a/drm/java/android/drm/DrmInfoRequest.java b/drm/java/android/drm/DrmInfoRequest.java index 1429fa516e1d..621da413bf97 100755 --- a/drm/java/android/drm/DrmInfoRequest.java +++ b/drm/java/android/drm/DrmInfoRequest.java @@ -67,6 +67,11 @@ public class DrmInfoRequest { public DrmInfoRequest(int infoType, String mimeType) { mInfoType = infoType; mMimeType = mimeType; + if (!isValid()) { + final String msg = "infoType: " + infoType + "," + + "mimeType: " + mimeType; + throw new IllegalArgumentException(msg); + } } /** diff --git a/drm/java/android/drm/DrmInfoStatus.java b/drm/java/android/drm/DrmInfoStatus.java index 5c12ae3c031a..2fe0a78ce9b9 100755 --- a/drm/java/android/drm/DrmInfoStatus.java +++ b/drm/java/android/drm/DrmInfoStatus.java @@ -56,6 +56,10 @@ public class DrmInfoStatus { * @param _mimeType The MIME type. */ public DrmInfoStatus(int _statusCode, int _infoType, ProcessedData _data, String _mimeType) { + if (!DrmInfoRequest.isValidType(_infoType)) { + throw new IllegalArgumentException("infoType: " + _infoType); + } + statusCode = _statusCode; infoType = _infoType; data = _data; diff --git a/drm/java/android/drm/DrmRights.java b/drm/java/android/drm/DrmRights.java index ef9c21dc7dc4..d4afed1607b8 100755 --- a/drm/java/android/drm/DrmRights.java +++ b/drm/java/android/drm/DrmRights.java @@ -103,6 +103,11 @@ public class DrmRights { } mMimeType = mimeType; + if (!isValid()) { + final String msg = "mimeType: " + mMimeType + "," + + "data: " + mData; + throw new IllegalArgumentException(msg); + } } /** @@ -117,17 +122,25 @@ public class DrmRights { mData = data.getData(); String accountId = data.getAccountId(); - if (null != accountId && !accountId.equals("")) { - mAccountId = accountId; + if (null == accountId || !accountId.equals("")) { + throw new IllegalArgumentException("accountId: " + accountId); } + mAccountId = accountId; String subscriptionId = data.getSubscriptionId(); - if (null != subscriptionId && !subscriptionId.equals("")) { - mSubscriptionId = subscriptionId; + if (null == subscriptionId || !subscriptionId.equals("")) { + throw new IllegalArgumentException( + "subscriptionId: " + subscriptionId); } + mSubscriptionId = subscriptionId; } mMimeType = mimeType; + if (!isValid()) { + final String msg = "mimeType: " + mMimeType + "," + + "data: " + mData; + throw new IllegalArgumentException(msg); + } } /** diff --git a/graphics/java/android/renderscript/Program.java b/graphics/java/android/renderscript/Program.java index 3f769eecdd97..4d60ac860621 100644 --- a/graphics/java/android/renderscript/Program.java +++ b/graphics/java/android/renderscript/Program.java @@ -69,6 +69,7 @@ public class Program extends BaseObj { Element mOutputs[]; Type mConstants[]; TextureType mTextures[]; + String mTextureNames[]; int mTextureCount; String mShader; @@ -111,6 +112,16 @@ public class Program extends BaseObj { } /** + * @hide + */ + public String getTextureName(int slot) { + if ((slot < 0) || (slot >= mTextureCount)) { + throw new IllegalArgumentException("Slot ID out of range."); + } + return mTextureNames[slot]; + } + + /** * Binds a constant buffer to be used as uniform inputs to the * program * @@ -180,6 +191,7 @@ public class Program extends BaseObj { Type mConstants[]; Type mTextures[]; TextureType mTextureTypes[]; + String mTextureNames[]; int mInputCount; int mOutputCount; int mConstantCount; @@ -197,6 +209,7 @@ public class Program extends BaseObj { mConstantCount = 0; mTextureCount = 0; mTextureTypes = new TextureType[MAX_TEXTURE]; + mTextureNames = new String[MAX_TEXTURE]; } /** @@ -300,10 +313,28 @@ public class Program extends BaseObj { * @return self */ public BaseProgramBuilder addTexture(TextureType texType) throws IllegalArgumentException { + addTexture(texType, "Tex" + mTextureCount); + return this; + } + + /** + * @hide + * Adds a texture input to the Program + * + * @param texType describes that the texture to append it (2D, + * Cubemap, etc.) + * @param texName what the texture should be called in the + * shader + * @return self + */ + public BaseProgramBuilder addTexture(TextureType texType, String texName) + throws IllegalArgumentException { if(mTextureCount >= MAX_TEXTURE) { throw new IllegalArgumentException("Max texture count exceeded."); } - mTextureTypes[mTextureCount ++] = texType; + mTextureTypes[mTextureCount] = texType; + mTextureNames[mTextureCount] = texName; + mTextureCount ++; return this; } @@ -317,6 +348,8 @@ public class Program extends BaseObj { p.mTextureCount = mTextureCount; p.mTextures = new TextureType[mTextureCount]; System.arraycopy(mTextureTypes, 0, p.mTextures, 0, mTextureCount); + p.mTextureNames = new String[mTextureCount]; + System.arraycopy(mTextureNames, 0, p.mTextureNames, 0, mTextureCount); } } diff --git a/graphics/java/android/renderscript/ProgramFragment.java b/graphics/java/android/renderscript/ProgramFragment.java index 21bace82f74f..ebc15e5070b5 100644 --- a/graphics/java/android/renderscript/ProgramFragment.java +++ b/graphics/java/android/renderscript/ProgramFragment.java @@ -59,6 +59,7 @@ public class ProgramFragment extends Program { public ProgramFragment create() { mRS.validate(); int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2]; + String[] texNames = new String[mTextureCount]; int idx = 0; for (int i=0; i < mInputCount; i++) { @@ -76,9 +77,10 @@ public class ProgramFragment extends Program { for (int i=0; i < mTextureCount; i++) { tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID; tmp[idx++] = mTextureTypes[i].mID; + texNames[i] = mTextureNames[i]; } - int id = mRS.nProgramFragmentCreate(mShader, tmp); + int id = mRS.nProgramFragmentCreate(mShader, texNames, tmp); ProgramFragment pf = new ProgramFragment(id, mRS); initProgram(pf); return pf; diff --git a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java index 0ab73c1b222b..cd31db3a919b 100644 --- a/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java +++ b/graphics/java/android/renderscript/ProgramFragmentFixedFunction.java @@ -47,6 +47,7 @@ public class ProgramFragmentFixedFunction extends ProgramFragment { public ProgramFragmentFixedFunction create() { mRS.validate(); int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2]; + String[] texNames = new String[mTextureCount]; int idx = 0; for (int i=0; i < mInputCount; i++) { @@ -64,9 +65,10 @@ public class ProgramFragmentFixedFunction extends ProgramFragment { for (int i=0; i < mTextureCount; i++) { tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID; tmp[idx++] = mTextureTypes[i].mID; + texNames[i] = mTextureNames[i]; } - int id = mRS.nProgramFragmentCreate(mShader, tmp); + int id = mRS.nProgramFragmentCreate(mShader, texNames, tmp); ProgramFragmentFixedFunction pf = new ProgramFragmentFixedFunction(id, mRS); initProgram(pf); return pf; diff --git a/graphics/java/android/renderscript/ProgramVertex.java b/graphics/java/android/renderscript/ProgramVertex.java index b3c1bd9ee37a..a6cd15b731ea 100644 --- a/graphics/java/android/renderscript/ProgramVertex.java +++ b/graphics/java/android/renderscript/ProgramVertex.java @@ -116,6 +116,7 @@ public class ProgramVertex extends Program { public ProgramVertex create() { mRS.validate(); int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2]; + String[] texNames = new String[mTextureCount]; int idx = 0; for (int i=0; i < mInputCount; i++) { @@ -133,9 +134,10 @@ public class ProgramVertex extends Program { for (int i=0; i < mTextureCount; i++) { tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID; tmp[idx++] = mTextureTypes[i].mID; + texNames[i] = mTextureNames[i]; } - int id = mRS.nProgramVertexCreate(mShader, tmp); + int id = mRS.nProgramVertexCreate(mShader, texNames, tmp); ProgramVertex pv = new ProgramVertex(id, mRS); initProgram(pv); return pv; diff --git a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java index 740d6a5a9566..9a43943da360 100644 --- a/graphics/java/android/renderscript/ProgramVertexFixedFunction.java +++ b/graphics/java/android/renderscript/ProgramVertexFixedFunction.java @@ -70,6 +70,7 @@ public class ProgramVertexFixedFunction extends ProgramVertex { public ProgramVertexFixedFunction create() { mRS.validate(); int[] tmp = new int[(mInputCount + mOutputCount + mConstantCount + mTextureCount) * 2]; + String[] texNames = new String[mTextureCount]; int idx = 0; for (int i=0; i < mInputCount; i++) { @@ -87,9 +88,10 @@ public class ProgramVertexFixedFunction extends ProgramVertex { for (int i=0; i < mTextureCount; i++) { tmp[idx++] = ProgramParam.TEXTURE_TYPE.mID; tmp[idx++] = mTextureTypes[i].mID; + texNames[i] = mTextureNames[i]; } - int id = mRS.nProgramVertexCreate(mShader, tmp); + int id = mRS.nProgramVertexCreate(mShader, texNames, tmp); ProgramVertexFixedFunction pv = new ProgramVertexFixedFunction(id, mRS); initProgram(pv); return pv; diff --git a/graphics/java/android/renderscript/RenderScript.java b/graphics/java/android/renderscript/RenderScript.java index 56303f712b67..95175135cf3a 100644 --- a/graphics/java/android/renderscript/RenderScript.java +++ b/graphics/java/android/renderscript/RenderScript.java @@ -569,15 +569,15 @@ public class RenderScript { validate(); rsnProgramBindSampler(mContext, vpf, slot, s); } - native int rsnProgramFragmentCreate(int con, String shader, int[] params); - synchronized int nProgramFragmentCreate(String shader, int[] params) { + native int rsnProgramFragmentCreate(int con, String shader, String[] texNames, int[] params); + synchronized int nProgramFragmentCreate(String shader, String[] texNames, int[] params) { validate(); - return rsnProgramFragmentCreate(mContext, shader, params); + return rsnProgramFragmentCreate(mContext, shader, texNames, params); } - native int rsnProgramVertexCreate(int con, String shader, int[] params); - synchronized int nProgramVertexCreate(String shader, int[] params) { + native int rsnProgramVertexCreate(int con, String shader, String[] texNames, int[] params); + synchronized int nProgramVertexCreate(String shader, String[] texNames, int[] params) { validate(); - return rsnProgramVertexCreate(mContext, shader, params); + return rsnProgramVertexCreate(mContext, shader, texNames, params); } native int rsnMeshCreate(int con, int[] vtx, int[] idx, int[] prim); diff --git a/graphics/jni/Android.mk b/graphics/jni/Android.mk index 652189fa63a7..ca69e1e5e16c 100644 --- a/graphics/jni/Android.mk +++ b/graphics/jni/Android.mk @@ -6,6 +6,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ + libandroidfw \ libnativehelper \ libRS \ libcutils \ diff --git a/graphics/jni/android_renderscript_RenderScript.cpp b/graphics/jni/android_renderscript_RenderScript.cpp index 4d087b047bf3..27ea8f6ecc8a 100644 --- a/graphics/jni/android_renderscript_RenderScript.cpp +++ b/graphics/jni/android_renderscript_RenderScript.cpp @@ -31,9 +31,9 @@ #include <core/SkTemplates.h> #include <images/SkImageDecoder.h> -#include <utils/Asset.h> -#include <utils/AssetManager.h> -#include <utils/ResourceTypes.h> +#include <androidfw/Asset.h> +#include <androidfw/AssetManager.h> +#include <androidfw/ResourceTypes.h> #include "jni.h" #include "JNIHelp.h" @@ -41,8 +41,8 @@ #include "android_runtime/android_view_Surface.h" #include "android_runtime/android_util_AssetManager.h" -#include <RenderScript.h> -#include <RenderScriptEnv.h> +#include <rs.h> +#include <rsEnv.h> #include <gui/SurfaceTexture.h> #include <gui/SurfaceTextureClient.h> #include <android_runtime/android_graphics_SurfaceTexture.h> @@ -54,13 +54,11 @@ using namespace android; class AutoJavaStringToUTF8 { public: - AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str) - { + AutoJavaStringToUTF8(JNIEnv* env, jstring str) : fEnv(env), fJStr(str) { fCStr = env->GetStringUTFChars(str, NULL); fLength = env->GetStringUTFLength(str); } - ~AutoJavaStringToUTF8() - { + ~AutoJavaStringToUTF8() { fEnv->ReleaseStringUTFChars(fJStr, fCStr); } const char* c_str() const { return fCStr; } @@ -73,6 +71,42 @@ private: jsize fLength; }; +class AutoJavaStringArrayToUTF8 { +public: + AutoJavaStringArrayToUTF8(JNIEnv* env, jobjectArray strings, jsize stringsLength) + : mEnv(env), mStrings(strings), mStringsLength(stringsLength) { + mCStrings = NULL; + mSizeArray = NULL; + if (stringsLength > 0) { + mCStrings = (const char **)calloc(stringsLength, sizeof(char *)); + mSizeArray = (size_t*)calloc(stringsLength, sizeof(size_t)); + for (jsize ct = 0; ct < stringsLength; ct ++) { + jstring s = (jstring)mEnv->GetObjectArrayElement(mStrings, ct); + mCStrings[ct] = mEnv->GetStringUTFChars(s, NULL); + mSizeArray[ct] = mEnv->GetStringUTFLength(s); + } + } + } + ~AutoJavaStringArrayToUTF8() { + for (jsize ct=0; ct < mStringsLength; ct++) { + jstring s = (jstring)mEnv->GetObjectArrayElement(mStrings, ct); + mEnv->ReleaseStringUTFChars(s, mCStrings[ct]); + } + free(mCStrings); + free(mSizeArray); + } + const char **c_str() const { return mCStrings; } + size_t *c_str_len() const { return mSizeArray; } + jsize length() const { return mStringsLength; } + +private: + JNIEnv *mEnv; + jobjectArray mStrings; + const char **mCStrings; + size_t *mSizeArray; + jsize mStringsLength; +}; + // --------------------------------------------------------------------------- static jfieldID gContextId = 0; @@ -322,33 +356,27 @@ nElementCreate(JNIEnv *_env, jobject _this, RsContext con, jint type, jint kind, } static jint -nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, jintArray _ids, jobjectArray _names, jintArray _arraySizes) +nElementCreate2(JNIEnv *_env, jobject _this, RsContext con, + jintArray _ids, jobjectArray _names, jintArray _arraySizes) { int fieldCount = _env->GetArrayLength(_ids); LOG_API("nElementCreate2, con(%p)", con); jint *ids = _env->GetIntArrayElements(_ids, NULL); jint *arraySizes = _env->GetIntArrayElements(_arraySizes, NULL); - const char ** nameArray = (const char **)calloc(fieldCount, sizeof(char *)); - size_t* sizeArray = (size_t*)calloc(fieldCount, sizeof(size_t)); - for (int ct=0; ct < fieldCount; ct++) { - jstring s = (jstring)_env->GetObjectArrayElement(_names, ct); - nameArray[ct] = _env->GetStringUTFChars(s, NULL); - sizeArray[ct] = _env->GetStringUTFLength(s); - } + AutoJavaStringArrayToUTF8 names(_env, _names, fieldCount); + + const char **nameArray = names.c_str(); + size_t *sizeArray = names.c_str_len(); + jint id = (jint)rsElementCreate2(con, (RsElement *)ids, fieldCount, nameArray, fieldCount * sizeof(size_t), sizeArray, (const uint32_t *)arraySizes, fieldCount); - for (int ct=0; ct < fieldCount; ct++) { - jstring s = (jstring)_env->GetObjectArrayElement(_names, ct); - _env->ReleaseStringUTFChars(s, nameArray[ct]); - } + _env->ReleaseIntArrayElements(_ids, ids, JNI_ABORT); _env->ReleaseIntArrayElements(_arraySizes, arraySizes, JNI_ABORT); - free(nameArray); - free(sizeArray); return (jint)id; } @@ -1064,15 +1092,24 @@ nProgramBindSampler(JNIEnv *_env, jobject _this, RsContext con, jint vpf, jint s // --------------------------------------------------------------------------- static jint -nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params) +nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, + jobjectArray texNames, jintArray params) { AutoJavaStringToUTF8 shaderUTF(_env, shader); jint *paramPtr = _env->GetIntArrayElements(params, NULL); jint paramLen = _env->GetArrayLength(params); + int texCount = _env->GetArrayLength(texNames); + AutoJavaStringArrayToUTF8 names(_env, texNames, texCount); + const char ** nameArray = names.c_str(); + size_t* sizeArray = names.c_str_len(); + LOG_API("nProgramFragmentCreate, con(%p), paramLen(%i)", con, paramLen); - jint ret = (jint)rsProgramFragmentCreate(con, shaderUTF.c_str(), shaderUTF.length(), (uint32_t *)paramPtr, paramLen); + jint ret = (jint)rsProgramFragmentCreate(con, shaderUTF.c_str(), shaderUTF.length(), + nameArray, texCount, sizeArray, + (uint32_t *)paramPtr, paramLen); + _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT); return ret; } @@ -1081,7 +1118,8 @@ nProgramFragmentCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shade // --------------------------------------------------------------------------- static jint -nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, jintArray params) +nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, + jobjectArray texNames, jintArray params) { AutoJavaStringToUTF8 shaderUTF(_env, shader); jint *paramPtr = _env->GetIntArrayElements(params, NULL); @@ -1089,7 +1127,15 @@ nProgramVertexCreate(JNIEnv *_env, jobject _this, RsContext con, jstring shader, LOG_API("nProgramVertexCreate, con(%p), paramLen(%i)", con, paramLen); - jint ret = (jint)rsProgramVertexCreate(con, shaderUTF.c_str(), shaderUTF.length(), (uint32_t *)paramPtr, paramLen); + int texCount = _env->GetArrayLength(texNames); + AutoJavaStringArrayToUTF8 names(_env, texNames, texCount); + const char ** nameArray = names.c_str(); + size_t* sizeArray = names.c_str_len(); + + jint ret = (jint)rsProgramVertexCreate(con, shaderUTF.c_str(), shaderUTF.length(), + nameArray, texCount, sizeArray, + (uint32_t *)paramPtr, paramLen); + _env->ReleaseIntArrayElements(params, paramPtr, JNI_ABORT); return ret; } @@ -1351,9 +1397,9 @@ static JNINativeMethod methods[] = { {"rsnProgramBindTexture", "(IIII)V", (void*)nProgramBindTexture }, {"rsnProgramBindSampler", "(IIII)V", (void*)nProgramBindSampler }, -{"rsnProgramFragmentCreate", "(ILjava/lang/String;[I)I", (void*)nProgramFragmentCreate }, +{"rsnProgramFragmentCreate", "(ILjava/lang/String;[Ljava/lang/String;[I)I", (void*)nProgramFragmentCreate }, {"rsnProgramRasterCreate", "(IZI)I", (void*)nProgramRasterCreate }, -{"rsnProgramVertexCreate", "(ILjava/lang/String;[I)I", (void*)nProgramVertexCreate }, +{"rsnProgramVertexCreate", "(ILjava/lang/String;[Ljava/lang/String;[I)I", (void*)nProgramVertexCreate }, {"rsnContextBindRootScript", "(II)V", (void*)nContextBindRootScript }, {"rsnContextBindProgramStore", "(II)V", (void*)nContextBindProgramStore }, diff --git a/include/android_runtime/android_app_NativeActivity.h b/include/android_runtime/android_app_NativeActivity.h index 93fcf69a17a9..a59677a167ce 100644 --- a/include/android_runtime/android_app_NativeActivity.h +++ b/include/android_runtime/android_app_NativeActivity.h @@ -17,7 +17,7 @@ #ifndef _ANDROID_APP_NATIVEACTIVITY_H #define _ANDROID_APP_NATIVEACTIVITY_H -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include <utils/Looper.h> #include <android/native_activity.h> diff --git a/include/android_runtime/android_content_res_Configuration.h b/include/android_runtime/android_content_res_Configuration.h index 2f5a98249cb7..34c4439bbde8 100644 --- a/include/android_runtime/android_content_res_Configuration.h +++ b/include/android_runtime/android_content_res_Configuration.h @@ -17,7 +17,7 @@ #ifndef _ANDROID_CONTENT_RES_CONFIGURATION_H #define _ANDROID_CONTENT_RES_CONFIGURATION_H -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <android/configuration.h> #include "jni.h" diff --git a/include/android_runtime/android_util_AssetManager.h b/include/android_runtime/android_util_AssetManager.h index b6dd9c9f5d8d..8dd933707a6a 100644 --- a/include/android_runtime/android_util_AssetManager.h +++ b/include/android_runtime/android_util_AssetManager.h @@ -17,7 +17,7 @@ #ifndef android_util_AssetManager_H #define android_util_AssetManager_H -#include <utils/AssetManager.h> +#include <androidfw/AssetManager.h> #include "jni.h" diff --git a/include/utils/Asset.h b/include/androidfw/Asset.h index 1fe0e063c886..1fe0e063c886 100644 --- a/include/utils/Asset.h +++ b/include/androidfw/Asset.h diff --git a/include/utils/AssetDir.h b/include/androidfw/AssetDir.h index abf8a3595dfb..abf8a3595dfb 100644 --- a/include/utils/AssetDir.h +++ b/include/androidfw/AssetDir.h diff --git a/include/utils/AssetManager.h b/include/androidfw/AssetManager.h index a8c7ddbd78a6..d95b45e98554 100644 --- a/include/utils/AssetManager.h +++ b/include/androidfw/AssetManager.h @@ -20,14 +20,15 @@ #ifndef __LIBS_ASSETMANAGER_H #define __LIBS_ASSETMANAGER_H -#include <utils/Asset.h> -#include <utils/AssetDir.h> +#include <androidfw/Asset.h> +#include <androidfw/AssetDir.h> +#include <androidfw/ZipFileRO.h> #include <utils/KeyedVector.h> -#include <utils/String8.h> -#include <utils/Vector.h> +#include <utils/SortedVector.h> #include <utils/String16.h> -#include <utils/ZipFileRO.h> +#include <utils/String8.h> #include <utils/threads.h> +#include <utils/Vector.h> /* * Native-app access is via the opaque typedef struct AAssetManager in the C namespace. diff --git a/include/utils/BackupHelpers.h b/include/androidfw/BackupHelpers.h index 1bb04a7123a3..1bb04a7123a3 100644 --- a/include/utils/BackupHelpers.h +++ b/include/androidfw/BackupHelpers.h diff --git a/include/ui/Input.h b/include/androidfw/Input.h index c2cbe1d6e871..f5db6e24e5d5 100644 --- a/include/ui/Input.h +++ b/include/androidfw/Input.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_INPUT_H -#define _UI_INPUT_H +#ifndef _ANDROIDFW_INPUT_H +#define _ANDROIDFW_INPUT_H /** * Native input event structures. @@ -894,4 +894,4 @@ extern String8 getInputDeviceConfigurationFilePathByName( } // namespace android -#endif // _UI_INPUT_H +#endif // _ANDROIDFW_INPUT_H diff --git a/include/ui/InputTransport.h b/include/androidfw/InputTransport.h index 0facce313fa5..a846e6599a91 100644 --- a/include/ui/InputTransport.h +++ b/include/androidfw/InputTransport.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_INPUT_TRANSPORT_H -#define _UI_INPUT_TRANSPORT_H +#ifndef _ANDROIDFW_INPUT_TRANSPORT_H +#define _ANDROIDFW_INPUT_TRANSPORT_H /** * Native input transport. @@ -27,11 +27,12 @@ * The InputConsumer is used by the application to receive events from the input dispatcher. */ -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/Errors.h> #include <utils/Timers.h> #include <utils/RefBase.h> #include <utils/String8.h> +#include <utils/Vector.h> namespace android { @@ -332,4 +333,4 @@ private: } // namespace android -#endif // _UI_INPUT_TRANSPORT_H +#endif // _ANDROIDFW_INPUT_TRANSPORT_H diff --git a/include/ui/KeyCharacterMap.h b/include/androidfw/KeyCharacterMap.h index be14432ae59b..679dd2c04fdf 100644 --- a/include/ui/KeyCharacterMap.h +++ b/include/androidfw/KeyCharacterMap.h @@ -14,12 +14,12 @@ * limitations under the License. */ -#ifndef _UI_KEY_CHARACTER_MAP_H -#define _UI_KEY_CHARACTER_MAP_H +#ifndef _ANDROIDFW_KEY_CHARACTER_MAP_H +#define _ANDROIDFW_KEY_CHARACTER_MAP_H #include <stdint.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> @@ -196,4 +196,4 @@ private: } // namespace android -#endif // _UI_KEY_CHARACTER_MAP_H +#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H diff --git a/include/ui/KeyLayoutMap.h b/include/androidfw/KeyLayoutMap.h index d82d0c8e0ef0..5a6f55035463 100644 --- a/include/ui/KeyLayoutMap.h +++ b/include/androidfw/KeyLayoutMap.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_KEY_LAYOUT_MAP_H -#define _UI_KEY_LAYOUT_MAP_H +#ifndef _ANDROIDFW_KEY_LAYOUT_MAP_H +#define _ANDROIDFW_KEY_LAYOUT_MAP_H #include <stdint.h> #include <utils/Errors.h> @@ -96,4 +96,4 @@ private: } // namespace android -#endif // _UI_KEY_LAYOUT_MAP_H +#endif // _ANDROIDFW_KEY_LAYOUT_MAP_H diff --git a/include/ui/Keyboard.h b/include/androidfw/Keyboard.h index 274f5264f463..ae65198044de 100644 --- a/include/ui/Keyboard.h +++ b/include/androidfw/Keyboard.h @@ -14,10 +14,10 @@ * limitations under the License. */ -#ifndef _UI_KEYBOARD_H -#define _UI_KEYBOARD_H +#ifndef _ANDROIDFW_KEYBOARD_H +#define _ANDROIDFW_KEYBOARD_H -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/Errors.h> #include <utils/String8.h> #include <utils/PropertyMap.h> @@ -116,4 +116,4 @@ extern bool isMetaKey(int32_t keyCode); } // namespace android -#endif // _UI_KEYBOARD_H +#endif // _ANDROIDFW_KEYBOARD_H diff --git a/include/ui/KeycodeLabels.h b/include/androidfw/KeycodeLabels.h index c5bd0c544673..9e4dfcb4df15 100755 --- a/include/ui/KeycodeLabels.h +++ b/include/androidfw/KeycodeLabels.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_KEYCODE_LABELS_H -#define _UI_KEYCODE_LABELS_H +#ifndef _ANDROIDFW_KEYCODE_LABELS_H +#define _ANDROIDFW_KEYCODE_LABELS_H #include <android/keycodes.h> @@ -307,4 +307,4 @@ static const KeycodeLabel AXES[] = { { NULL, -1 } }; -#endif // _UI_KEYCODE_LABELS_H +#endif // _ANDROIDFW_KEYCODE_LABELS_H diff --git a/include/utils/ObbFile.h b/include/androidfw/ObbFile.h index 47559cdd0d61..47559cdd0d61 100644 --- a/include/utils/ObbFile.h +++ b/include/androidfw/ObbFile.h diff --git a/include/ui/PowerManager.h b/include/androidfw/PowerManager.h index dd80318e6349..59e993abfb9c 100644 --- a/include/ui/PowerManager.h +++ b/include/androidfw/PowerManager.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef _UI_POWER_MANAGER_H -#define _UI_POWER_MANAGER_H +#ifndef _ANDROIDFW_POWER_MANAGER_H +#define _ANDROIDFW_POWER_MANAGER_H namespace android { @@ -30,4 +30,4 @@ enum { } // namespace android -#endif // _UI_POWER_MANAGER_H +#endif // _ANDROIDFW_POWER_MANAGER_H diff --git a/include/utils/ResourceTypes.h b/include/androidfw/ResourceTypes.h index c496da6bac56..23bca3e2ad9f 100644 --- a/include/utils/ResourceTypes.h +++ b/include/androidfw/ResourceTypes.h @@ -20,7 +20,7 @@ #ifndef _LIBS_UTILS_RESOURCE_TYPES_H #define _LIBS_UTILS_RESOURCE_TYPES_H -#include <utils/Asset.h> +#include <androidfw/Asset.h> #include <utils/ByteOrder.h> #include <utils/Errors.h> #include <utils/String16.h> diff --git a/include/utils/StreamingZipInflater.h b/include/androidfw/StreamingZipInflater.h index 3ace5d5a83cf..3ace5d5a83cf 100644 --- a/include/utils/StreamingZipInflater.h +++ b/include/androidfw/StreamingZipInflater.h diff --git a/include/ui/VirtualKeyMap.h b/include/androidfw/VirtualKeyMap.h index 7813d9d03241..66340e3f7a96 100644 --- a/include/ui/VirtualKeyMap.h +++ b/include/androidfw/VirtualKeyMap.h @@ -14,12 +14,12 @@ * limitations under the License. */ -#ifndef _UI_VIRTUAL_KEY_MAP_H -#define _UI_VIRTUAL_KEY_MAP_H +#ifndef _ANDROIDFW_VIRTUAL_KEY_MAP_H +#define _ANDROIDFW_VIRTUAL_KEY_MAP_H #include <stdint.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/Errors.h> #include <utils/KeyedVector.h> #include <utils/Tokenizer.h> @@ -76,4 +76,4 @@ private: } // namespace android -#endif // _UI_KEY_CHARACTER_MAP_H +#endif // _ANDROIDFW_KEY_CHARACTER_MAP_H diff --git a/include/utils/ZipFileCRO.h b/include/androidfw/ZipFileCRO.h index 3e42a958bf5f..3e42a958bf5f 100644 --- a/include/utils/ZipFileCRO.h +++ b/include/androidfw/ZipFileCRO.h diff --git a/include/utils/ZipFileRO.h b/include/androidfw/ZipFileRO.h index 547e36a09864..547e36a09864 100644 --- a/include/utils/ZipFileRO.h +++ b/include/androidfw/ZipFileRO.h diff --git a/include/utils/ZipUtils.h b/include/androidfw/ZipUtils.h index 42c42b6c0f54..42c42b6c0f54 100644 --- a/include/utils/ZipUtils.h +++ b/include/androidfw/ZipUtils.h diff --git a/include/common_time/ICommonClock.h b/include/common_time/ICommonClock.h new file mode 100644 index 000000000000..d7073f1337d9 --- /dev/null +++ b/include/common_time/ICommonClock.h @@ -0,0 +1,108 @@ +/* + * 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_ICOMMONCLOCK_H +#define ANDROID_ICOMMONCLOCK_H + +#include <stdint.h> +#include <linux/socket.h> + +#include <binder/IInterface.h> +#include <binder/IServiceManager.h> + +namespace android { + +class ICommonClockListener : public IInterface { + public: + DECLARE_META_INTERFACE(CommonClockListener); + + virtual void onTimelineChanged(uint64_t timelineID) = 0; +}; + +class BnCommonClockListener : public BnInterface<ICommonClockListener> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +class ICommonClock : public IInterface { + public: + DECLARE_META_INTERFACE(CommonClock); + + // Name of the ICommonClock service registered with the service manager. + static const String16 kServiceName; + + // a reserved invalid timeline ID + static const uint64_t kInvalidTimelineID; + + // a reserved invalid error estimate + static const int32_t kErrorEstimateUnknown; + + enum State { + // the device just came up and is trying to discover the master + STATE_INITIAL, + + // the device is a client of a master + STATE_CLIENT, + + // the device is acting as master + STATE_MASTER, + + // the device has lost contact with its master and needs to participate + // in the election of a new master + STATE_RONIN, + + // the device is waiting for announcement of the newly elected master + STATE_WAIT_FOR_ELECTION, + }; + + virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) = 0; + virtual status_t commonTimeToLocalTime(int64_t commonTime, + int64_t* localTime) = 0; + virtual status_t localTimeToCommonTime(int64_t localTime, + int64_t* commonTime) = 0; + virtual status_t getCommonTime(int64_t* commonTime) = 0; + virtual status_t getCommonFreq(uint64_t* freq) = 0; + virtual status_t getLocalTime(int64_t* localTime) = 0; + virtual status_t getLocalFreq(uint64_t* freq) = 0; + virtual status_t getEstimatedError(int32_t* estimate) = 0; + virtual status_t getTimelineID(uint64_t* id) = 0; + virtual status_t getState(State* state) = 0; + virtual status_t getMasterAddr(struct sockaddr_storage* addr) = 0; + + virtual status_t registerListener( + const sp<ICommonClockListener>& listener) = 0; + virtual status_t unregisterListener( + const sp<ICommonClockListener>& listener) = 0; + + // Simple helper to make it easier to connect to the CommonClock service. + static inline sp<ICommonClock> getInstance() { + sp<IBinder> binder = defaultServiceManager()->checkService( + ICommonClock::kServiceName); + sp<ICommonClock> clk = interface_cast<ICommonClock>(binder); + return clk; + } +}; + +class BnCommonClock : public BnInterface<ICommonClock> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_ICOMMONCLOCK_H diff --git a/include/common_time/ICommonTimeConfig.h b/include/common_time/ICommonTimeConfig.h new file mode 100644 index 000000000000..497b666de6f8 --- /dev/null +++ b/include/common_time/ICommonTimeConfig.h @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2012 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_ICOMMONTIMECONFIG_H +#define ANDROID_ICOMMONTIMECONFIG_H + +#include <stdint.h> +#include <linux/socket.h> + +#include <binder/IInterface.h> +#include <binder/IServiceManager.h> + +namespace android { + +class String16; + +class ICommonTimeConfig : public IInterface { + public: + DECLARE_META_INTERFACE(CommonTimeConfig); + + // Name of the ICommonTimeConfig service registered with the service + // manager. + static const String16 kServiceName; + + virtual status_t getMasterElectionPriority(uint8_t *priority) = 0; + virtual status_t setMasterElectionPriority(uint8_t priority) = 0; + virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) = 0; + virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr) = 0; + virtual status_t getMasterElectionGroupId(uint64_t *id) = 0; + virtual status_t setMasterElectionGroupId(uint64_t id) = 0; + virtual status_t getInterfaceBinding(String16& ifaceName) = 0; + virtual status_t setInterfaceBinding(const String16& ifaceName) = 0; + virtual status_t getMasterAnnounceInterval(int *interval) = 0; + virtual status_t setMasterAnnounceInterval(int interval) = 0; + virtual status_t getClientSyncInterval(int *interval) = 0; + virtual status_t setClientSyncInterval(int interval) = 0; + virtual status_t getPanicThreshold(int *threshold) = 0; + virtual status_t setPanicThreshold(int threshold) = 0; + virtual status_t getAutoDisable(bool *autoDisable) = 0; + virtual status_t setAutoDisable(bool autoDisable) = 0; + virtual status_t forceNetworklessMasterMode() = 0; + + // Simple helper to make it easier to connect to the CommonTimeConfig service. + static inline sp<ICommonTimeConfig> getInstance() { + sp<IBinder> binder = defaultServiceManager()->checkService( + ICommonTimeConfig::kServiceName); + sp<ICommonTimeConfig> clk = interface_cast<ICommonTimeConfig>(binder); + return clk; + } +}; + +class BnCommonTimeConfig : public BnInterface<ICommonTimeConfig> { + public: + virtual status_t onTransact(uint32_t code, const Parcel& data, + Parcel* reply, uint32_t flags = 0); +}; + +}; // namespace android + +#endif // ANDROID_ICOMMONTIMECONFIG_H diff --git a/include/common_time/cc_helper.h b/include/common_time/cc_helper.h new file mode 100644 index 000000000000..8c4d5c063d52 --- /dev/null +++ b/include/common_time/cc_helper.h @@ -0,0 +1,72 @@ +/* + * 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 __CC_HELPER_H__ +#define __CC_HELPER_H__ + +#include <stdint.h> +#include <common_time/ICommonClock.h> +#include <utils/threads.h> + +namespace android { + +// CCHelper is a simple wrapper class to help with centralizing access to the +// Common Clock service and implementing lifetime managment, as well as to +// implement a simple policy of making a basic attempt to reconnect to the +// common clock service when things go wrong. +// +// On platforms which run the native common_time service in auto-disable mode, +// the service will go into networkless mode whenever it has no active clients. +// It tracks active clients using registered CommonClockListeners (the callback +// interface for onTimelineChanged) since this provides a convienent death +// handler notification for when the service's clients die unexpectedly. This +// means that users of the common time service should really always have a +// CommonClockListener, unless they know that the time service is not running in +// auto disabled mode, or that there is at least one other registered listener +// active in the system. The CCHelper makes this a little easier by sharing a +// ref counted ICommonClock interface across all clients and automatically +// registering and unregistering a listener whenever there are CCHelper +// instances active in the process. +class CCHelper { + public: + CCHelper(); + ~CCHelper(); + + status_t isCommonTimeValid(bool* valid, uint32_t* timelineID); + status_t commonTimeToLocalTime(int64_t commonTime, int64_t* localTime); + status_t localTimeToCommonTime(int64_t localTime, int64_t* commonTime); + status_t getCommonTime(int64_t* commonTime); + status_t getCommonFreq(uint64_t* freq); + status_t getLocalTime(int64_t* localTime); + status_t getLocalFreq(uint64_t* freq); + + private: + class CommonClockListener : public BnCommonClockListener { + public: + void onTimelineChanged(uint64_t timelineID); + }; + + static bool verifyClock_l(); + + static Mutex lock_; + static sp<ICommonClock> common_clock_; + static sp<ICommonClockListener> common_clock_listener_; + static uint32_t ref_count_; +}; + + +} // namespace android +#endif // __CC_HELPER_H__ diff --git a/include/common_time/local_clock.h b/include/common_time/local_clock.h new file mode 100644 index 000000000000..845d1c21614d --- /dev/null +++ b/include/common_time/local_clock.h @@ -0,0 +1,47 @@ +/* + * 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 __LOCAL_CLOCK_H__ +#define __LOCAL_CLOCK_H__ + +#include <stdint.h> + +#include <hardware/local_time_hal.h> +#include <utils/Errors.h> +#include <utils/threads.h> + +namespace android { + +class LocalClock { + public: + LocalClock(); + + bool initCheck(); + + int64_t getLocalTime(); + uint64_t getLocalFreq(); + status_t setLocalSlew(int16_t rate); + int32_t getDebugLog(struct local_time_debug_event* records, + int max_records); + + private: + static Mutex dev_lock_; + static local_time_hw_device_t* dev_; +}; + +} // namespace android +#endif // __LOCAL_CLOCK_H__ diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h index ac7f6cf22c10..9f2bd3a15cf4 100644 --- a/include/media/AudioTrack.h +++ b/include/media/AudioTrack.h @@ -446,7 +446,7 @@ public: */ status_t dump(int fd, const Vector<String16>& args) const; -private: +protected: /* copying audio tracks is not allowed */ AudioTrack(const AudioTrack& other); AudioTrack& operator = (const AudioTrack& other); @@ -518,10 +518,33 @@ private: int mAuxEffectId; mutable Mutex mLock; status_t mRestoreStatus; + bool mIsTimed; int mPreviousPriority; // before start() int mPreviousSchedulingGroup; }; +class TimedAudioTrack : public AudioTrack +{ +public: + TimedAudioTrack(); + + /* allocate a shared memory buffer that can be passed to queueTimedBuffer */ + status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer); + + /* queue a buffer obtained via allocateTimedBuffer for playback at the + given timestamp. PTS units a microseconds on the media time timeline. + The media time transform (set with setMediaTimeTransform) set by the + audio producer will handle converting from media time to local time + (perhaps going through the common time timeline in the case of + synchronized multiroom audio case) */ + status_t queueTimedBuffer(const sp<IMemory>& buffer, int64_t pts); + + /* define a transform between media time and either common time or + local time */ + enum TargetTimeline {LOCAL_TIME, COMMON_TIME}; + status_t setMediaTimeTransform(const LinearTransform& xform, + TargetTimeline target); +}; }; // namespace android diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h index 433ce7cf52f1..7a2ada032fb3 100644 --- a/include/media/IAudioFlinger.h +++ b/include/media/IAudioFlinger.h @@ -55,6 +55,7 @@ public: uint32_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, + bool isTimed, int *sessionId, status_t *status) = 0; diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h index e4772a17edec..77f3e213c875 100644 --- a/include/media/IAudioTrack.h +++ b/include/media/IAudioTrack.h @@ -24,7 +24,7 @@ #include <utils/Errors.h> #include <binder/IInterface.h> #include <binder/IMemory.h> - +#include <utils/LinearTransform.h> namespace android { @@ -71,6 +71,23 @@ public: */ virtual status_t attachAuxEffect(int effectId) = 0; + + /* Allocate a shared memory buffer suitable for holding timed audio + samples */ + virtual status_t allocateTimedBuffer(size_t size, + sp<IMemory>* buffer) = 0; + + /* Queue a buffer obtained via allocateTimedBuffer for playback at the given + timestamp */ + virtual status_t queueTimedBuffer(const sp<IMemory>& buffer, + int64_t pts) = 0; + + /* Define the linear transform that will be applied to the timestamps + given to queueTimedBuffer (which are expressed in media time). + Target specifies whether this transform converts media time to local time + or Tungsten time. The values for target are defined in AudioTrack.h */ + virtual status_t setMediaTimeTransform(const LinearTransform& xform, + int target) = 0; }; // ---------------------------------------------------------------------------- diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h index 77c82b27ba1b..23a3e4905a63 100644 --- a/include/media/MediaPlayerInterface.h +++ b/include/media/MediaPlayerInterface.h @@ -46,6 +46,9 @@ enum player_type { // The shared library with the test player is passed passed as an // argument to the 'test:' url in the setDataSource call. TEST_PLAYER = 5, + + AAH_RX_PLAYER = 100, + AAH_TX_PLAYER = 101, }; diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h index 3963d9cf5909..70799a638b8a 100644 --- a/include/media/stagefright/ACodec.h +++ b/include/media/stagefright/ACodec.h @@ -22,6 +22,7 @@ #include <android/native_window.h> #include <media/IOMX.h> #include <media/stagefright/foundation/AHierarchicalStateMachine.h> +#include <OMX_Audio.h> namespace android { @@ -37,6 +38,9 @@ struct ACodec : public AHierarchicalStateMachine { kWhatFlushCompleted = 'fcom', kWhatOutputFormatChanged = 'outC', kWhatError = 'erro', + kWhatComponentAllocated = 'cAll', + kWhatComponentConfigured = 'cCon', + kWhatBuffersAllocated = 'allc', }; ACodec(); @@ -47,6 +51,10 @@ struct ACodec : public AHierarchicalStateMachine { void signalResume(); void initiateShutdown(); + void initiateAllocateComponent(const sp<AMessage> &msg); + void initiateConfigureComponent(const sp<AMessage> &msg); + void initiateStart(); + protected: virtual ~ACodec(); @@ -70,6 +78,9 @@ private: kWhatFlush = 'flus', kWhatResume = 'resm', kWhatDrainDeferredMessages = 'drai', + kWhatAllocateComponent = 'allo', + kWhatConfigureComponent = 'conf', + kWhatStart = 'star', }; enum { @@ -118,6 +129,7 @@ private: List<sp<AMessage> > mDeferredQueue; bool mSentFormat; + bool mIsEncoder; status_t allocateBuffersOnPort(OMX_U32 portIndex); status_t freeBuffersOnPort(OMX_U32 portIndex); @@ -132,8 +144,8 @@ private: uint32_t portIndex, IOMX::buffer_id bufferID, ssize_t *index = NULL); - void setComponentRole(bool isEncoder, const char *mime); - void configureCodec(const char *mime, const sp<AMessage> &msg); + status_t setComponentRole(bool isEncoder, const char *mime); + status_t configureCodec(const char *mime, const sp<AMessage> &msg); status_t setVideoPortFormatType( OMX_U32 portIndex, @@ -145,20 +157,37 @@ private: status_t setupVideoDecoder( const char *mime, int32_t width, int32_t height); + status_t setupVideoEncoder( + const char *mime, const sp<AMessage> &msg); + status_t setVideoFormatOnPort( OMX_U32 portIndex, int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat); - status_t setupAACDecoder(int32_t numChannels, int32_t sampleRate); - status_t setupAMRDecoder(bool isWAMR); - status_t setupG711Decoder(int32_t numChannels); + status_t setupAACCodec( + bool encoder, + int32_t numChannels, int32_t sampleRate, int32_t bitRate); + + status_t selectAudioPortFormat( + OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat); + + status_t setupAMRCodec(bool encoder, bool isWAMR, int32_t bitRate); + status_t setupG711Codec(bool encoder, int32_t numChannels); status_t setupRawAudioFormat( OMX_U32 portIndex, int32_t sampleRate, int32_t numChannels); status_t setMinBufferSize(OMX_U32 portIndex, size_t size); + status_t setupMPEG4EncoderParameters(const sp<AMessage> &msg); + status_t setupH263EncoderParameters(const sp<AMessage> &msg); + status_t setupAVCEncoderParameters(const sp<AMessage> &msg); + + status_t verifySupportForProfileAndLevel(int32_t profile, int32_t level); + status_t configureBitrate(int32_t bitrate); + status_t setupErrorCorrectionParameters(); + status_t initNativeWindow(); // Returns true iff all buffers on the given port have status OWNED_BY_US. @@ -173,7 +202,9 @@ private: void sendFormatChange(); - void signalError(OMX_ERRORTYPE error = OMX_ErrorUndefined); + void signalError( + OMX_ERRORTYPE error = OMX_ErrorUndefined, + status_t internalError = UNKNOWN_ERROR); DISALLOW_EVIL_CONSTRUCTORS(ACodec); }; diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h index 79437bfcdb63..f5466e80ba6a 100644 --- a/include/media/stagefright/AudioSource.h +++ b/include/media/stagefright/AudioSource.h @@ -95,6 +95,7 @@ private: int32_t startFrame, int32_t rampDurationFrames, uint8_t *data, size_t bytes); + void queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs); void releaseQueuedFrames_l(); void waitOutstandingEncodingFrames_l(); status_t reset(); diff --git a/include/media/stagefright/MediaCodec.h b/include/media/stagefright/MediaCodec.h new file mode 100644 index 000000000000..8c11c9c67f13 --- /dev/null +++ b/include/media/stagefright/MediaCodec.h @@ -0,0 +1,183 @@ +/* + * Copyright 2012, 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 MEDIA_CODEC_H_ + +#define MEDIA_CODEC_H_ + +#include <gui/ISurfaceTexture.h> +#include <media/stagefright/foundation/AHandler.h> +#include <utils/Vector.h> + +namespace android { + +struct ABuffer; +struct ACodec; +struct AMessage; +struct SoftwareRenderer; +struct SurfaceTextureClient; + +struct MediaCodec : public AHandler { + enum ConfigureFlags { + CONFIGURE_FLAG_ENCODE = 1, + }; + + enum BufferFlags { + BUFFER_FLAG_SYNCFRAME = 1, + BUFFER_FLAG_CODECCONFIG = 2, + BUFFER_FLAG_EOS = 4, + }; + + static sp<MediaCodec> CreateByType( + const sp<ALooper> &looper, const char *mime, bool encoder); + + static sp<MediaCodec> CreateByComponentName( + const sp<ALooper> &looper, const char *name); + + status_t configure( + const sp<AMessage> &format, + const sp<SurfaceTextureClient> &nativeWindow, + uint32_t flags); + + status_t start(); + status_t stop(); + + status_t flush(); + + status_t queueInputBuffer( + size_t index, + size_t offset, + size_t size, + int64_t presentationTimeUs, + uint32_t flags); + + status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs = 0ll); + + status_t dequeueOutputBuffer( + size_t *index, + size_t *offset, + size_t *size, + int64_t *presentationTimeUs, + uint32_t *flags, + int64_t timeoutUs = 0ll); + + status_t renderOutputBufferAndRelease(size_t index); + status_t releaseOutputBuffer(size_t index); + + status_t getOutputFormat(sp<AMessage> *format) const; + + status_t getInputBuffers(Vector<sp<ABuffer> > *buffers) const; + status_t getOutputBuffers(Vector<sp<ABuffer> > *buffers) const; + +protected: + virtual ~MediaCodec(); + virtual void onMessageReceived(const sp<AMessage> &msg); + +private: + enum State { + UNINITIALIZED, + INITIALIZING, + INITIALIZED, + CONFIGURING, + CONFIGURED, + STARTING, + STARTED, + FLUSHING, + STOPPING, + }; + + enum { + kPortIndexInput = 0, + kPortIndexOutput = 1, + }; + + enum { + kWhatInit = 'init', + kWhatConfigure = 'conf', + kWhatStart = 'strt', + kWhatStop = 'stop', + kWhatDequeueInputBuffer = 'deqI', + kWhatQueueInputBuffer = 'queI', + kWhatDequeueOutputBuffer = 'deqO', + kWhatReleaseOutputBuffer = 'relO', + kWhatGetBuffers = 'getB', + kWhatFlush = 'flus', + kWhatGetOutputFormat = 'getO', + kWhatDequeueInputTimedOut = 'dITO', + kWhatDequeueOutputTimedOut = 'dOTO', + kWhatCodecNotify = 'codc', + }; + + enum { + kFlagIsSoftwareCodec = 1, + kFlagOutputFormatChanged = 2, + kFlagOutputBuffersChanged = 4, + kFlagStickyError = 8, + kFlagDequeueInputPending = 16, + kFlagDequeueOutputPending = 32, + }; + + struct BufferInfo { + void *mBufferID; + sp<ABuffer> mData; + sp<AMessage> mNotify; + bool mOwnedByClient; + }; + + State mState; + sp<ALooper> mLooper; + sp<ALooper> mCodecLooper; + sp<ACodec> mCodec; + uint32_t mReplyID; + uint32_t mFlags; + sp<SurfaceTextureClient> mNativeWindow; + SoftwareRenderer *mSoftRenderer; + sp<AMessage> mOutputFormat; + + List<size_t> mAvailPortBuffers[2]; + Vector<BufferInfo> mPortBuffers[2]; + + int32_t mDequeueInputTimeoutGeneration; + uint32_t mDequeueInputReplyID; + + int32_t mDequeueOutputTimeoutGeneration; + uint32_t mDequeueOutputReplyID; + + MediaCodec(const sp<ALooper> &looper); + + static status_t PostAndAwaitResponse( + const sp<AMessage> &msg, sp<AMessage> *response); + + status_t init(const char *name, bool nameIsType, bool encoder); + + void setState(State newState); + void returnBuffersToCodec(); + void returnBuffersToCodecOnPort(int32_t portIndex); + size_t updateBuffers(int32_t portIndex, const sp<AMessage> &msg); + status_t onQueueInputBuffer(const sp<AMessage> &msg); + status_t onReleaseOutputBuffer(const sp<AMessage> &msg); + ssize_t dequeuePortBuffer(int32_t portIndex); + + bool handleDequeueInputBuffer(uint32_t replyID, bool newRequest = false); + bool handleDequeueOutputBuffer(uint32_t replyID, bool newRequest = false); + void cancelPendingDequeueOperations(); + + DISALLOW_EVIL_CONSTRUCTORS(MediaCodec); +}; + +} // namespace android + +#endif // MEDIA_CODEC_H_ diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h index 21d00b8c531b..dd3bf2876999 100644 --- a/include/media/stagefright/MediaErrors.h +++ b/include/media/stagefright/MediaErrors.h @@ -40,6 +40,7 @@ enum { // Not technically an error. INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12, INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13, + INFO_OUTPUT_BUFFERS_CHANGED = MEDIA_ERROR_BASE - 14, // The following constant values should be in sync with // drm/drm_framework_common.h diff --git a/include/media/stagefright/NativeWindowWrapper.h b/include/media/stagefright/NativeWindowWrapper.h index f323cbc1935c..97cc0cebda3e 100644 --- a/include/media/stagefright/NativeWindowWrapper.h +++ b/include/media/stagefright/NativeWindowWrapper.h @@ -18,40 +18,28 @@ #define NATIVE_WINDOW_WRAPPER_H_ -#include <surfaceflinger/Surface.h> #include <gui/SurfaceTextureClient.h> namespace android { -// Both Surface and SurfaceTextureClient are RefBase that implement the -// ANativeWindow interface, but at different addresses. ANativeWindow is not -// a RefBase but acts like one for use with sp<>. This wrapper converts a -// Surface or SurfaceTextureClient into a single reference-counted object -// that holds an sp reference to the underlying Surface or SurfaceTextureClient, -// It provides a method to get the ANativeWindow. +// SurfaceTextureClient derives from ANativeWindow which derives from multiple +// base classes, in order to carry it in AMessages, we'll temporarily wrap it +// into a NativeWindowWrapper. struct NativeWindowWrapper : RefBase { NativeWindowWrapper( - const sp<Surface> &surface) : - mSurface(surface) { } - - NativeWindowWrapper( const sp<SurfaceTextureClient> &surfaceTextureClient) : mSurfaceTextureClient(surfaceTextureClient) { } sp<ANativeWindow> getNativeWindow() const { - if (mSurface != NULL) { - return mSurface; - } else { - return mSurfaceTextureClient; - } + return mSurfaceTextureClient; } - // If needed later we can provide a method to ask what kind of native window + sp<SurfaceTextureClient> getSurfaceTextureClient() const { + return mSurfaceTextureClient; + } private: - // At most one of mSurface and mSurfaceTextureClient will be non-NULL - const sp<Surface> mSurface; const sp<SurfaceTextureClient> mSurfaceTextureClient; DISALLOW_EVIL_CONSTRUCTORS(NativeWindowWrapper); diff --git a/include/media/stagefright/NuMediaExtractor.h b/include/media/stagefright/NuMediaExtractor.h new file mode 100644 index 000000000000..96efdff9eb35 --- /dev/null +++ b/include/media/stagefright/NuMediaExtractor.h @@ -0,0 +1,80 @@ +/* + * Copyright 2012, 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 NU_MEDIA_EXTRACTOR_H_ +#define NU_MEDIA_EXTRACTOR_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> +#include <utils/Vector.h> + +namespace android { + +struct ABuffer; +struct AMessage; +struct MediaBuffer; +struct MediaExtractor; +struct MediaSource; + +struct NuMediaExtractor : public RefBase { + NuMediaExtractor(); + + status_t setDataSource(const char *path); + + size_t countTracks() const; + status_t getTrackFormat(size_t index, sp<AMessage> *format) const; + + status_t selectTrack(size_t index); + + status_t seekTo(int64_t timeUs); + + status_t advance(); + status_t readSampleData(const sp<ABuffer> &buffer); + status_t getSampleTrackIndex(size_t *trackIndex); + status_t getSampleTime(int64_t *sampleTimeUs); + +protected: + virtual ~NuMediaExtractor(); + +private: + enum TrackFlags { + kIsVorbis = 1, + }; + + struct TrackInfo { + sp<MediaSource> mSource; + size_t mTrackIndex; + status_t mFinalResult; + MediaBuffer *mSample; + int64_t mSampleTimeUs; + uint32_t mFlags; // bitmask of "TrackFlags" + }; + + sp<MediaExtractor> mImpl; + + Vector<TrackInfo> mSelectedTracks; + + ssize_t fetchTrackSamples(int64_t seekTimeUs = -1ll); + void releaseTrackSamples(); + + DISALLOW_EVIL_CONSTRUCTORS(NuMediaExtractor); +}; + +} // namespace android + +#endif // NU_MEDIA_EXTRACTOR_H_ + diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h index 7ec54aa213c4..e5416e4fc9d7 100644 --- a/include/media/stagefright/foundation/AMessage.h +++ b/include/media/stagefright/foundation/AMessage.h @@ -25,6 +25,7 @@ namespace android { +struct ABuffer; struct AString; struct Parcel; @@ -50,6 +51,7 @@ struct AMessage : public RefBase { void setPointer(const char *name, void *value); void setString(const char *name, const char *s, ssize_t len = -1); void setObject(const char *name, const sp<RefBase> &obj); + void setBuffer(const char *name, const sp<ABuffer> &buffer); void setMessage(const char *name, const sp<AMessage> &obj); void setRect( @@ -64,6 +66,7 @@ struct AMessage : public RefBase { bool findPointer(const char *name, void **value) const; bool findString(const char *name, AString *value) const; bool findObject(const char *name, sp<RefBase> *obj) const; + bool findBuffer(const char *name, sp<ABuffer> *buffer) const; bool findMessage(const char *name, sp<AMessage> *obj) const; bool findRect( @@ -90,10 +93,6 @@ struct AMessage : public RefBase { AString debugString(int32_t indent = 0) const; -protected: - virtual ~AMessage(); - -private: enum Type { kTypeInt32, kTypeInt64, @@ -105,8 +104,16 @@ private: kTypeObject, kTypeMessage, kTypeRect, + kTypeBuffer, }; + size_t countEntries() const; + const char *getEntryNameAt(size_t index, Type *type) const; + +protected: + virtual ~AMessage(); + +private: uint32_t mWhat; ALooper::handler_id mTarget; @@ -131,7 +138,7 @@ private: }; enum { - kMaxNumItems = 16 + kMaxNumItems = 32 }; Item mItems[kMaxNumItems]; size_t mNumItems; @@ -140,6 +147,9 @@ private: void freeItem(Item *item); const Item *findItem(const char *name, Type type) const; + void setObjectInternal( + const char *name, const sp<RefBase> &obj, Type type); + DISALLOW_EVIL_CONSTRUCTORS(AMessage); }; diff --git a/include/ui/PixelFormat.h b/include/ui/PixelFormat.h index 848c5a114907..fc260c4f4b1d 100644 --- a/include/ui/PixelFormat.h +++ b/include/ui/PixelFormat.h @@ -21,7 +21,6 @@ // skia or SurfaceFlinger are not required to support all of these formats // (either as source or destination) -// XXX: we should consolidate these formats and skia's #ifndef UI_PIXELFORMAT_H #define UI_PIXELFORMAT_H @@ -29,7 +28,6 @@ #include <stdint.h> #include <sys/types.h> #include <utils/Errors.h> -#include <pixelflinger/format.h> #include <hardware/hardware.h> namespace android { @@ -65,10 +63,7 @@ enum { PIXEL_FORMAT_BGRA_8888 = HAL_PIXEL_FORMAT_BGRA_8888, // 4x8-bit BGRA PIXEL_FORMAT_RGBA_5551 = HAL_PIXEL_FORMAT_RGBA_5551, // 16-bit ARGB PIXEL_FORMAT_RGBA_4444 = HAL_PIXEL_FORMAT_RGBA_4444, // 16-bit ARGB - PIXEL_FORMAT_A_8 = GGL_PIXEL_FORMAT_A_8, // 8-bit A - PIXEL_FORMAT_L_8 = GGL_PIXEL_FORMAT_L_8, // 8-bit L (R=G=B=L) - PIXEL_FORMAT_LA_88 = GGL_PIXEL_FORMAT_LA_88, // 16-bit LA - PIXEL_FORMAT_RGB_332 = GGL_PIXEL_FORMAT_RGB_332, // 8-bit RGB + PIXEL_FORMAT_A_8 = 8, // 8-bit A // New formats can be added if they're also defined in // pixelflinger/format.h @@ -76,8 +71,7 @@ enum { typedef int32_t PixelFormat; -struct PixelFormatInfo -{ +struct PixelFormatInfo { enum { INDEX_ALPHA = 0, INDEX_RED = 1, @@ -89,8 +83,6 @@ struct PixelFormatInfo ALPHA = 1, RGB = 2, RGBA = 3, - LUMINANCE = 4, - LUMINANCE_ALPHA = 5, OTHER = 0xFF }; diff --git a/keystore/java/android/security/KeyChain.java b/keystore/java/android/security/KeyChain.java index db6388ac14c8..0fe7bd88dad1 100644 --- a/keystore/java/android/security/KeyChain.java +++ b/keystore/java/android/security/KeyChain.java @@ -124,7 +124,7 @@ public final class KeyChain { public static final String EXTRA_SENDER = "sender"; /** - * Action to bring up the CertInstaller + * Action to bring up the CertInstaller. */ private static final String ACTION_INSTALL = "android.credentials.INSTALL"; @@ -167,6 +167,22 @@ public final class KeyChain { // Compatible with old android.security.Credentials.PKCS12 public static final String EXTRA_PKCS12 = "PKCS12"; + + /** + * @hide TODO This is temporary and will be removed + * Broadcast Action: Indicates the trusted storage has changed. Sent when + * one of this happens: + * + * <ul> + * <li>a new CA is added, + * <li>an existing CA is removed or disabled, + * <li>a disabled CA is enabled, + * <li>trusted storage is reset (all user certs are cleared), + * <li>when permission to access a private key is changed. + * </ul> + */ + public static final String ACTION_STORAGE_CHANGED = "android.security.STORAGE_CHANGED"; + /** * Returns an {@code Intent} that can be used for credential * installation. The intent may be used without any extras, in diff --git a/libs/androidfw/Android.mk b/libs/androidfw/Android.mk new file mode 100644 index 000000000000..c5f8a87659f1 --- /dev/null +++ b/libs/androidfw/Android.mk @@ -0,0 +1,113 @@ +# 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. + +LOCAL_PATH:= $(call my-dir) + +# libandroidfw is partially built for the host (used by build time keymap validation tool) +# These files are common to host and target builds. + +# formerly in libutils +commonUtilsSources:= \ + Asset.cpp \ + AssetDir.cpp \ + AssetManager.cpp \ + ObbFile.cpp \ + ResourceTypes.cpp \ + StreamingZipInflater.cpp \ + ZipFileCRO.cpp \ + ZipFileRO.cpp \ + ZipUtils.cpp + +# formerly in libui +commonUiSources:= \ + Input.cpp \ + Keyboard.cpp \ + KeyCharacterMap.cpp \ + KeyLayoutMap.cpp \ + VirtualKeyMap.cpp + +commonSources:= \ + $(commonUtilsSources) \ + $(commonUiSources) + +# For the host +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= $(commonSources) + +LOCAL_MODULE:= libandroidfw + +LOCAL_MODULE_TAGS := optional + +LOCAL_C_INCLUDES := \ + external/zlib + +include $(BUILD_HOST_STATIC_LIBRARY) + + +# For the device +# ===================================================== + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + $(commonSources) \ + BackupData.cpp \ + BackupHelpers.cpp \ + InputTransport.cpp + +LOCAL_SHARED_LIBRARIES := \ + liblog \ + libcutils \ + libutils \ + libbinder \ + libskia \ + libz + +LOCAL_C_INCLUDES := \ + external/skia/include/core \ + external/icu4c/common \ + external/zlib + +LOCAL_MODULE:= libandroidfw + +LOCAL_MODULE_TAGS := optional + +include $(BUILD_SHARED_LIBRARY) + + +ifeq ($(TARGET_OS),linux) +include $(CLEAR_VARS) +LOCAL_C_INCLUDES += \ + external/skia/include/core \ + external/zlib \ + external/icu4c/common \ + bionic/libc/private +LOCAL_LDLIBS := -lrt -ldl -lpthread +LOCAL_MODULE := libandroidfw +LOCAL_SRC_FILES := $(commonUtilsSources) BackupData.cpp BackupHelpers.cpp +include $(BUILD_STATIC_LIBRARY) +endif + + +# Include subdirectory makefiles +# ============================================================ + +# If we're building with ONE_SHOT_MAKEFILE (mm, mmm), then what the framework +# team really wants is to build the stuff defined by this makefile. +ifeq (,$(ONE_SHOT_MAKEFILE)) +include $(call first-makefiles-under,$(LOCAL_PATH)) +endif diff --git a/libs/utils/Asset.cpp b/libs/androidfw/Asset.cpp index 50e701aa8d89..cb7628d1d5b1 100644 --- a/libs/utils/Asset.cpp +++ b/libs/androidfw/Asset.cpp @@ -21,23 +21,23 @@ #define LOG_TAG "asset" //#define NDEBUG 0 -#include <utils/Asset.h> +#include <androidfw/Asset.h> +#include <androidfw/StreamingZipInflater.h> +#include <androidfw/ZipFileRO.h> +#include <androidfw/ZipUtils.h> #include <utils/Atomic.h> #include <utils/FileMap.h> -#include <utils/StreamingZipInflater.h> -#include <utils/ZipUtils.h> -#include <utils/ZipFileRO.h> #include <utils/Log.h> #include <utils/threads.h> -#include <string.h> -#include <memory.h> -#include <fcntl.h> -#include <errno.h> #include <assert.h> -#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <memory.h> +#include <string.h> #include <sys/stat.h> #include <sys/types.h> +#include <unistd.h> using namespace android; diff --git a/libs/utils/AssetDir.cpp b/libs/androidfw/AssetDir.cpp index c5f664eccf9a..475f521c117b 100644 --- a/libs/utils/AssetDir.cpp +++ b/libs/androidfw/AssetDir.cpp @@ -19,7 +19,7 @@ // implementation is in the header file or in friend functions in // AssetManager. // -#include <utils/AssetDir.h> +#include <androidfw/AssetDir.h> using namespace android; diff --git a/libs/utils/AssetManager.cpp b/libs/androidfw/AssetManager.cpp index 47a2b9953fcc..4829addecf59 100644 --- a/libs/utils/AssetManager.cpp +++ b/libs/androidfw/AssetManager.cpp @@ -21,23 +21,23 @@ #define LOG_TAG "asset" //#define LOG_NDEBUG 0 -#include <utils/AssetManager.h> -#include <utils/AssetDir.h> -#include <utils/Asset.h> +#include <androidfw/Asset.h> +#include <androidfw/AssetDir.h> +#include <androidfw/AssetManager.h> +#include <androidfw/ResourceTypes.h> +#include <androidfw/ZipFileRO.h> #include <utils/Atomic.h> +#include <utils/Log.h> #include <utils/String8.h> -#include <utils/ResourceTypes.h> #include <utils/String8.h> -#include <utils/ZipFileRO.h> -#include <utils/Log.h> -#include <utils/Timers.h> #include <utils/threads.h> +#include <utils/Timers.h> +#include <assert.h> #include <dirent.h> #include <errno.h> -#include <assert.h> -#include <strings.h> #include <fcntl.h> +#include <strings.h> #include <sys/stat.h> #include <unistd.h> diff --git a/libs/utils/BackupData.cpp b/libs/androidfw/BackupData.cpp index f956306136a1..7b1bcba2d130 100644 --- a/libs/utils/BackupData.cpp +++ b/libs/androidfw/BackupData.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "backup_data" -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> #include <utils/ByteOrder.h> #include <stdio.h> diff --git a/libs/utils/BackupHelpers.cpp b/libs/androidfw/BackupHelpers.cpp index f77a8917c7f9..7a817a78995a 100644 --- a/libs/utils/BackupHelpers.cpp +++ b/libs/androidfw/BackupHelpers.cpp @@ -16,7 +16,7 @@ #define LOG_TAG "file_backup_helper" -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> #include <utils/KeyedVector.h> #include <utils/ByteOrder.h> @@ -546,7 +546,7 @@ int write_tarfile(const String8& packageName, const String8& domain, // read/write up to this much at a time. const size_t BUFSIZE = 32 * 1024; - char* buf = new char[BUFSIZE]; + char* buf = (char *)calloc(1,BUFSIZE); char* paxHeader = buf + 512; // use a different chunk of it as separate scratch char* paxData = buf + 1024; @@ -556,9 +556,6 @@ int write_tarfile(const String8& packageName, const String8& domain, goto cleanup; } - // Good to go -- first construct the standard tar header at the start of the buffer - memset(buf, 0, BUFSIZE); - // Magic fields for the ustar file format strcat(buf + 257, "ustar"); strcat(buf + 263, "00"); diff --git a/libs/ui/Input.cpp b/libs/androidfw/Input.cpp index 263c8d919889..ca09caf6a255 100644 --- a/libs/ui/Input.cpp +++ b/libs/androidfw/Input.cpp @@ -24,7 +24,7 @@ #include <unistd.h> #include <ctype.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include <math.h> #include <limits.h> diff --git a/libs/ui/InputTransport.cpp b/libs/androidfw/InputTransport.cpp index ecb3fb5c9870..1ebd75cda17f 100644 --- a/libs/ui/InputTransport.cpp +++ b/libs/androidfw/InputTransport.cpp @@ -20,7 +20,7 @@ #include <cutils/log.h> #include <errno.h> #include <fcntl.h> -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> diff --git a/libs/ui/KeyCharacterMap.cpp b/libs/androidfw/KeyCharacterMap.cpp index 485234c2abbc..6984084370ab 100644 --- a/libs/ui/KeyCharacterMap.cpp +++ b/libs/androidfw/KeyCharacterMap.cpp @@ -19,8 +19,8 @@ #include <stdlib.h> #include <string.h> #include <android/keycodes.h> -#include <ui/Keyboard.h> -#include <ui/KeyCharacterMap.h> +#include <androidfw/Keyboard.h> +#include <androidfw/KeyCharacterMap.h> #include <utils/Log.h> #include <utils/Errors.h> #include <utils/Tokenizer.h> diff --git a/libs/ui/KeyLayoutMap.cpp b/libs/androidfw/KeyLayoutMap.cpp index 44a94207c1c2..15d81ee2ed74 100644 --- a/libs/ui/KeyLayoutMap.cpp +++ b/libs/androidfw/KeyLayoutMap.cpp @@ -18,8 +18,8 @@ #include <stdlib.h> #include <android/keycodes.h> -#include <ui/Keyboard.h> -#include <ui/KeyLayoutMap.h> +#include <androidfw/Keyboard.h> +#include <androidfw/KeyLayoutMap.h> #include <utils/Log.h> #include <utils/Errors.h> #include <utils/Tokenizer.h> diff --git a/libs/ui/Keyboard.cpp b/libs/androidfw/Keyboard.cpp index e4611f71cbd1..e97a5eb6c2c0 100644 --- a/libs/ui/Keyboard.cpp +++ b/libs/androidfw/Keyboard.cpp @@ -20,10 +20,10 @@ #include <unistd.h> #include <limits.h> -#include <ui/Keyboard.h> -#include <ui/KeycodeLabels.h> -#include <ui/KeyLayoutMap.h> -#include <ui/KeyCharacterMap.h> +#include <androidfw/Keyboard.h> +#include <androidfw/KeycodeLabels.h> +#include <androidfw/KeyLayoutMap.h> +#include <androidfw/KeyCharacterMap.h> #include <utils/Errors.h> #include <utils/Log.h> #include <cutils/properties.h> diff --git a/libs/androidfw/MODULE_LICENSE_APACHE2 b/libs/androidfw/MODULE_LICENSE_APACHE2 new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/libs/androidfw/MODULE_LICENSE_APACHE2 diff --git a/libs/androidfw/NOTICE b/libs/androidfw/NOTICE new file mode 100644 index 000000000000..c5b1efa7aac7 --- /dev/null +++ b/libs/androidfw/NOTICE @@ -0,0 +1,190 @@ + + Copyright (c) 2005-2008, 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. + + 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. + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/libs/utils/ObbFile.cpp b/libs/androidfw/ObbFile.cpp index ddf59914b87d..21e06c8db5cd 100644 --- a/libs/utils/ObbFile.cpp +++ b/libs/androidfw/ObbFile.cpp @@ -23,9 +23,9 @@ #define LOG_TAG "ObbFile" +#include <androidfw/ObbFile.h> #include <utils/Compat.h> #include <utils/Log.h> -#include <utils/ObbFile.h> //#define DEBUG 1 diff --git a/libs/utils/ResourceTypes.cpp b/libs/androidfw/ResourceTypes.cpp index 3fa562ec6074..07f3b1624f5b 100644 --- a/libs/utils/ResourceTypes.cpp +++ b/libs/androidfw/ResourceTypes.cpp @@ -17,14 +17,14 @@ #define LOG_TAG "ResourceType" //#define LOG_NDEBUG 0 +#include <androidfw/ResourceTypes.h> #include <utils/Atomic.h> #include <utils/ByteOrder.h> #include <utils/Debug.h> -#include <utils/ResourceTypes.h> +#include <utils/Log.h> #include <utils/String16.h> #include <utils/String8.h> #include <utils/TextOutput.h> -#include <utils/Log.h> #include <stdlib.h> #include <string.h> @@ -379,8 +379,7 @@ status_t ResStringPool::setTo(const void* data, size_t size, bool copyData) size_t charSize; if (mHeader->flags&ResStringPool_header::UTF8_FLAG) { charSize = sizeof(uint8_t); - mCache = (char16_t**)malloc(sizeof(char16_t**)*mHeader->stringCount); - memset(mCache, 0, sizeof(char16_t**)*mHeader->stringCount); + mCache = (char16_t**)calloc(mHeader->stringCount, sizeof(char16_t**)); } else { charSize = sizeof(char16_t); } @@ -3252,16 +3251,14 @@ ssize_t ResTable::getBagLocked(uint32_t resID, const bag_entry** outBag, // Bag not found, we need to compute it! if (!grp->bags) { - grp->bags = (bag_set***)malloc(sizeof(bag_set*)*grp->typeCount); + grp->bags = (bag_set***)calloc(grp->typeCount, sizeof(bag_set*)); if (!grp->bags) return NO_MEMORY; - memset(grp->bags, 0, sizeof(bag_set*)*grp->typeCount); } bag_set** typeSet = grp->bags[t]; if (!typeSet) { - typeSet = (bag_set**)malloc(sizeof(bag_set*)*NENTRY); + typeSet = (bag_set**)calloc(NENTRY, sizeof(bag_set*)); if (!typeSet) return NO_MEMORY; - memset(typeSet, 0, sizeof(bag_set*)*NENTRY); grp->bags[t] = typeSet; } diff --git a/libs/utils/StreamingZipInflater.cpp b/libs/androidfw/StreamingZipInflater.cpp index 8512170ae900..d3fb98d57fac 100644 --- a/libs/utils/StreamingZipInflater.cpp +++ b/libs/androidfw/StreamingZipInflater.cpp @@ -18,8 +18,8 @@ #define LOG_TAG "szipinf" #include <utils/Log.h> +#include <androidfw/StreamingZipInflater.h> #include <utils/FileMap.h> -#include <utils/StreamingZipInflater.h> #include <string.h> #include <stddef.h> #include <assert.h> diff --git a/libs/ui/VirtualKeyMap.cpp b/libs/androidfw/VirtualKeyMap.cpp index 62d5b593b27c..2ba1673d35b7 100644 --- a/libs/ui/VirtualKeyMap.cpp +++ b/libs/androidfw/VirtualKeyMap.cpp @@ -18,7 +18,7 @@ #include <stdlib.h> #include <string.h> -#include <ui/VirtualKeyMap.h> +#include <androidfw/VirtualKeyMap.h> #include <utils/Log.h> #include <utils/Errors.h> #include <utils/Tokenizer.h> diff --git a/libs/utils/ZipFileCRO.cpp b/libs/androidfw/ZipFileCRO.cpp index 55dfd9f873b8..c8df8453df73 100644 --- a/libs/utils/ZipFileCRO.cpp +++ b/libs/androidfw/ZipFileCRO.cpp @@ -14,8 +14,8 @@ * limitations under the License. */ -#include <utils/ZipFileCRO.h> -#include <utils/ZipFileRO.h> +#include <androidfw/ZipFileCRO.h> +#include <androidfw/ZipFileRO.h> using namespace android; diff --git a/libs/utils/ZipFileRO.cpp b/libs/androidfw/ZipFileRO.cpp index 1498aac07e1e..4b7f1e73faa2 100644 --- a/libs/utils/ZipFileRO.cpp +++ b/libs/androidfw/ZipFileRO.cpp @@ -19,7 +19,7 @@ // #define LOG_TAG "zipro" //#define LOG_NDEBUG 0 -#include <utils/ZipFileRO.h> +#include <androidfw/ZipFileRO.h> #include <utils/Log.h> #include <utils/misc.h> #include <utils/threads.h> diff --git a/libs/utils/ZipUtils.cpp b/libs/androidfw/ZipUtils.cpp index 2dbdc1d3800a..db3479d3363d 100644 --- a/libs/utils/ZipUtils.cpp +++ b/libs/androidfw/ZipUtils.cpp @@ -20,8 +20,8 @@ #define LOG_TAG "ziputil" -#include <utils/ZipUtils.h> -#include <utils/ZipFileRO.h> +#include <androidfw/ZipUtils.h> +#include <androidfw/ZipFileRO.h> #include <utils/Log.h> #include <stdlib.h> diff --git a/libs/androidfw/tests/Android.mk b/libs/androidfw/tests/Android.mk new file mode 100644 index 000000000000..d85009b36ecd --- /dev/null +++ b/libs/androidfw/tests/Android.mk @@ -0,0 +1,47 @@ +# Build the unit tests. +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +# Build the unit tests. +test_src_files := \ + InputChannel_test.cpp \ + InputEvent_test.cpp \ + InputPublisherAndConsumer_test.cpp \ + ObbFile_test.cpp \ + ZipFileRO_test.cpp + +shared_libraries := \ + libandroidfw \ + libcutils \ + libutils \ + libbinder \ + libui \ + libstlport \ + libskia + +static_libraries := \ + libgtest \ + libgtest_main + +c_includes := \ + bionic \ + bionic/libstdc++/include \ + external/gtest/include \ + external/stlport/stlport \ + external/skia/include/core + +module_tags := eng tests + +$(foreach file,$(test_src_files), \ + $(eval include $(CLEAR_VARS)) \ + $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ + $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ + $(eval LOCAL_C_INCLUDES := $(c_includes)) \ + $(eval LOCAL_SRC_FILES := $(file)) \ + $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ + $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ + $(eval include $(BUILD_EXECUTABLE)) \ +) + +# Build the manual test programs. +include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/libs/ui/tests/InputChannel_test.cpp b/libs/androidfw/tests/InputChannel_test.cpp index ee422fe7d8c2..0e5d19d01009 100644 --- a/libs/ui/tests/InputChannel_test.cpp +++ b/libs/androidfw/tests/InputChannel_test.cpp @@ -14,9 +14,10 @@ * limitations under the License. */ -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include <utils/Timers.h> #include <utils/StopWatch.h> +#include <utils/StrongPointer.h> #include <gtest/gtest.h> #include <unistd.h> #include <time.h> diff --git a/libs/ui/tests/InputEvent_test.cpp b/libs/androidfw/tests/InputEvent_test.cpp index e21c464a1974..ac5549cdd6dd 100644 --- a/libs/ui/tests/InputEvent_test.cpp +++ b/libs/androidfw/tests/InputEvent_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <ui/Input.h> +#include <androidfw/Input.h> #include <gtest/gtest.h> #include <binder/Parcel.h> diff --git a/libs/ui/tests/InputPublisherAndConsumer_test.cpp b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp index 33030531322e..bb4524742307 100644 --- a/libs/ui/tests/InputPublisherAndConsumer_test.cpp +++ b/libs/androidfw/tests/InputPublisherAndConsumer_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <ui/InputTransport.h> +#include <androidfw/InputTransport.h> #include <utils/Timers.h> #include <utils/StopWatch.h> #include <gtest/gtest.h> diff --git a/libs/utils/tests/ObbFile_test.cpp b/libs/androidfw/tests/ObbFile_test.cpp index 46b30c2a6de0..09d4d7d985a9 100644 --- a/libs/utils/tests/ObbFile_test.cpp +++ b/libs/androidfw/tests/ObbFile_test.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "ObbFile_test" +#include <androidfw/ObbFile.h> #include <utils/Log.h> -#include <utils/ObbFile.h> #include <utils/RefBase.h> #include <utils/String8.h> diff --git a/libs/utils/tests/ZipFileRO_test.cpp b/libs/androidfw/tests/ZipFileRO_test.cpp index 7a1d0bd958a3..344f97416b65 100644 --- a/libs/utils/tests/ZipFileRO_test.cpp +++ b/libs/androidfw/tests/ZipFileRO_test.cpp @@ -15,8 +15,8 @@ */ #define LOG_TAG "ZipFileRO_test" +#include <androidfw/ZipFileRO.h> #include <utils/Log.h> -#include <utils/ZipFileRO.h> #include <gtest/gtest.h> diff --git a/libs/common_time/Android.mk b/libs/common_time/Android.mk new file mode 100644 index 000000000000..526f17b44a5c --- /dev/null +++ b/libs/common_time/Android.mk @@ -0,0 +1,21 @@ +LOCAL_PATH:= $(call my-dir) +# +# libcommon_time_client +# (binder marshalers for ICommonClock as well as common clock and local clock +# helper code) +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := libcommon_time_client +LOCAL_MODULE_TAGS := optional +LOCAL_SRC_FILES := cc_helper.cpp \ + local_clock.cpp \ + ICommonClock.cpp \ + ICommonTimeConfig.cpp \ + utils.cpp +LOCAL_SHARED_LIBRARIES := libbinder \ + libhardware \ + libutils + +include $(BUILD_SHARED_LIBRARY) diff --git a/libs/common_time/ICommonClock.cpp b/libs/common_time/ICommonClock.cpp new file mode 100644 index 000000000000..28b43acff16a --- /dev/null +++ b/libs/common_time/ICommonClock.cpp @@ -0,0 +1,432 @@ +/* + * Copyright (C) 2012 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 <linux/socket.h> + +#include <common_time/ICommonClock.h> +#include <binder/Parcel.h> + +#include "utils.h" + +namespace android { + +/***** ICommonClock *****/ + +enum { + IS_COMMON_TIME_VALID = IBinder::FIRST_CALL_TRANSACTION, + COMMON_TIME_TO_LOCAL_TIME, + LOCAL_TIME_TO_COMMON_TIME, + GET_COMMON_TIME, + GET_COMMON_FREQ, + GET_LOCAL_TIME, + GET_LOCAL_FREQ, + GET_ESTIMATED_ERROR, + GET_TIMELINE_ID, + GET_STATE, + GET_MASTER_ADDRESS, + REGISTER_LISTENER, + UNREGISTER_LISTENER, +}; + +const String16 ICommonClock::kServiceName("common_time.clock"); +const uint64_t ICommonClock::kInvalidTimelineID = 0; +const int32_t ICommonClock::kErrorEstimateUnknown = 0x7FFFFFFF; + +class BpCommonClock : public BpInterface<ICommonClock> +{ + public: + BpCommonClock(const sp<IBinder>& impl) + : BpInterface<ICommonClock>(impl) {} + + virtual status_t isCommonTimeValid(bool* valid, uint32_t* timelineID) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(IS_COMMON_TIME_VALID, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *valid = reply.readInt32(); + *timelineID = reply.readInt32(); + } + } + return status; + } + + virtual status_t commonTimeToLocalTime(int64_t commonTime, + int64_t* localTime) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + data.writeInt64(commonTime); + status_t status = remote()->transact(COMMON_TIME_TO_LOCAL_TIME, + data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *localTime = reply.readInt64(); + } + } + return status; + } + + virtual status_t localTimeToCommonTime(int64_t localTime, + int64_t* commonTime) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + data.writeInt64(localTime); + status_t status = remote()->transact(LOCAL_TIME_TO_COMMON_TIME, + data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *commonTime = reply.readInt64(); + } + } + return status; + } + + virtual status_t getCommonTime(int64_t* commonTime) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_COMMON_TIME, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *commonTime = reply.readInt64(); + } + } + return status; + } + + virtual status_t getCommonFreq(uint64_t* freq) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_COMMON_FREQ, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *freq = reply.readInt64(); + } + } + return status; + } + + virtual status_t getLocalTime(int64_t* localTime) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_LOCAL_TIME, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *localTime = reply.readInt64(); + } + } + return status; + } + + virtual status_t getLocalFreq(uint64_t* freq) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_LOCAL_FREQ, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *freq = reply.readInt64(); + } + } + return status; + } + + virtual status_t getEstimatedError(int32_t* estimate) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_ESTIMATED_ERROR, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *estimate = reply.readInt32(); + } + } + return status; + } + + virtual status_t getTimelineID(uint64_t* id) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_TIMELINE_ID, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *id = static_cast<uint64_t>(reply.readInt64()); + } + } + return status; + } + + virtual status_t getState(State* state) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_STATE, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *state = static_cast<State>(reply.readInt32()); + } + } + return status; + } + + virtual status_t getMasterAddr(struct sockaddr_storage* addr) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_ADDRESS, data, &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) + deserializeSockaddr(&reply, addr); + } + return status; + } + + virtual status_t registerListener( + const sp<ICommonClockListener>& listener) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + + status_t status = remote()->transact(REGISTER_LISTENER, data, &reply); + + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t unregisterListener( + const sp<ICommonClockListener>& listener) { + Parcel data, reply; + data.writeInterfaceToken(ICommonClock::getInterfaceDescriptor()); + data.writeStrongBinder(listener->asBinder()); + status_t status = remote()->transact(UNREGISTER_LISTENER, data, &reply); + + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } +}; + +IMPLEMENT_META_INTERFACE(CommonClock, "android.os.ICommonClock"); + +status_t BnCommonClock::onTransact(uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags) { + switch(code) { + case IS_COMMON_TIME_VALID: { + CHECK_INTERFACE(ICommonClock, data, reply); + bool valid; + uint32_t timelineID; + status_t status = isCommonTimeValid(&valid, &timelineID); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(valid); + reply->writeInt32(timelineID); + } + return OK; + } break; + + case COMMON_TIME_TO_LOCAL_TIME: { + CHECK_INTERFACE(ICommonClock, data, reply); + int64_t commonTime = data.readInt64(); + int64_t localTime; + status_t status = commonTimeToLocalTime(commonTime, &localTime); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(localTime); + } + return OK; + } break; + + case LOCAL_TIME_TO_COMMON_TIME: { + CHECK_INTERFACE(ICommonClock, data, reply); + int64_t localTime = data.readInt64(); + int64_t commonTime; + status_t status = localTimeToCommonTime(localTime, &commonTime); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(commonTime); + } + return OK; + } break; + + case GET_COMMON_TIME: { + CHECK_INTERFACE(ICommonClock, data, reply); + int64_t commonTime; + status_t status = getCommonTime(&commonTime); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(commonTime); + } + return OK; + } break; + + case GET_COMMON_FREQ: { + CHECK_INTERFACE(ICommonClock, data, reply); + uint64_t freq; + status_t status = getCommonFreq(&freq); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(freq); + } + return OK; + } break; + + case GET_LOCAL_TIME: { + CHECK_INTERFACE(ICommonClock, data, reply); + int64_t localTime; + status_t status = getLocalTime(&localTime); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(localTime); + } + return OK; + } break; + + case GET_LOCAL_FREQ: { + CHECK_INTERFACE(ICommonClock, data, reply); + uint64_t freq; + status_t status = getLocalFreq(&freq); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(freq); + } + return OK; + } break; + + case GET_ESTIMATED_ERROR: { + CHECK_INTERFACE(ICommonClock, data, reply); + int32_t error; + status_t status = getEstimatedError(&error); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(error); + } + return OK; + } break; + + case GET_TIMELINE_ID: { + CHECK_INTERFACE(ICommonClock, data, reply); + uint64_t id; + status_t status = getTimelineID(&id); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(static_cast<int64_t>(id)); + } + return OK; + } break; + + case GET_STATE: { + CHECK_INTERFACE(ICommonClock, data, reply); + State state; + status_t status = getState(&state); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(static_cast<int32_t>(state)); + } + return OK; + } break; + + case GET_MASTER_ADDRESS: { + CHECK_INTERFACE(ICommonClock, data, reply); + struct sockaddr_storage addr; + status_t status = getMasterAddr(&addr); + + if ((status == OK) && !canSerializeSockaddr(&addr)) { + status = UNKNOWN_ERROR; + } + + reply->writeInt32(status); + + if (status == OK) { + serializeSockaddr(reply, &addr); + } + + return OK; + } break; + + case REGISTER_LISTENER: { + CHECK_INTERFACE(ICommonClock, data, reply); + sp<ICommonClockListener> listener = + interface_cast<ICommonClockListener>(data.readStrongBinder()); + status_t status = registerListener(listener); + reply->writeInt32(status); + return OK; + } break; + + case UNREGISTER_LISTENER: { + CHECK_INTERFACE(ICommonClock, data, reply); + sp<ICommonClockListener> listener = + interface_cast<ICommonClockListener>(data.readStrongBinder()); + status_t status = unregisterListener(listener); + reply->writeInt32(status); + return OK; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +/***** ICommonClockListener *****/ + +enum { + ON_TIMELINE_CHANGED = IBinder::FIRST_CALL_TRANSACTION, +}; + +class BpCommonClockListener : public BpInterface<ICommonClockListener> +{ + public: + BpCommonClockListener(const sp<IBinder>& impl) + : BpInterface<ICommonClockListener>(impl) {} + + virtual void onTimelineChanged(uint64_t timelineID) { + Parcel data, reply; + data.writeInterfaceToken( + ICommonClockListener::getInterfaceDescriptor()); + data.writeInt64(timelineID); + remote()->transact(ON_TIMELINE_CHANGED, data, &reply); + } +}; + +IMPLEMENT_META_INTERFACE(CommonClockListener, + "android.os.ICommonClockListener"); + +status_t BnCommonClockListener::onTransact( + uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { + switch(code) { + case ON_TIMELINE_CHANGED: { + CHECK_INTERFACE(ICommonClockListener, data, reply); + uint32_t timelineID = data.readInt64(); + onTimelineChanged(timelineID); + return NO_ERROR; + } break; + } + + return BBinder::onTransact(code, data, reply, flags); +} + +}; // namespace android diff --git a/libs/common_time/ICommonTimeConfig.cpp b/libs/common_time/ICommonTimeConfig.cpp new file mode 100644 index 000000000000..8eb37cb8064a --- /dev/null +++ b/libs/common_time/ICommonTimeConfig.cpp @@ -0,0 +1,508 @@ +/* + * 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. + */ +#include <linux/socket.h> + +#include <common_time/ICommonTimeConfig.h> +#include <binder/Parcel.h> + +#include "utils.h" + +namespace android { + +/***** ICommonTimeConfig *****/ + +enum { + GET_MASTER_ELECTION_PRIORITY = IBinder::FIRST_CALL_TRANSACTION, + SET_MASTER_ELECTION_PRIORITY, + GET_MASTER_ELECTION_ENDPOINT, + SET_MASTER_ELECTION_ENDPOINT, + GET_MASTER_ELECTION_GROUP_ID, + SET_MASTER_ELECTION_GROUP_ID, + GET_INTERFACE_BINDING, + SET_INTERFACE_BINDING, + GET_MASTER_ANNOUNCE_INTERVAL, + SET_MASTER_ANNOUNCE_INTERVAL, + GET_CLIENT_SYNC_INTERVAL, + SET_CLIENT_SYNC_INTERVAL, + GET_PANIC_THRESHOLD, + SET_PANIC_THRESHOLD, + GET_AUTO_DISABLE, + SET_AUTO_DISABLE, + FORCE_NETWORKLESS_MASTER_MODE, +}; + +const String16 ICommonTimeConfig::kServiceName("common_time.config"); + +class BpCommonTimeConfig : public BpInterface<ICommonTimeConfig> +{ + public: + BpCommonTimeConfig(const sp<IBinder>& impl) + : BpInterface<ICommonTimeConfig>(impl) {} + + virtual status_t getMasterElectionPriority(uint8_t *priority) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_ELECTION_PRIORITY, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *priority = static_cast<uint8_t>(reply.readInt32()); + } + } + + return status; + } + + virtual status_t setMasterElectionPriority(uint8_t priority) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeInt32(static_cast<int32_t>(priority)); + status_t status = remote()->transact(SET_MASTER_ELECTION_PRIORITY, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_ELECTION_ENDPOINT, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + deserializeSockaddr(&reply, addr); + } + } + + return status; + } + + virtual status_t setMasterElectionEndpoint( + const struct sockaddr_storage *addr) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + if (!canSerializeSockaddr(addr)) + return BAD_VALUE; + if (NULL == addr) { + data.writeInt32(0); + } else { + data.writeInt32(1); + serializeSockaddr(&data, addr); + } + status_t status = remote()->transact(SET_MASTER_ELECTION_ENDPOINT, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getMasterElectionGroupId(uint64_t *id) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_ELECTION_GROUP_ID, + data, + &reply); + + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *id = static_cast<uint64_t>(reply.readInt64()); + } + } + + return status; + } + + virtual status_t setMasterElectionGroupId(uint64_t id) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeInt64(id); + status_t status = remote()->transact(SET_MASTER_ELECTION_GROUP_ID, + data, + &reply); + + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getInterfaceBinding(String16& ifaceName) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_INTERFACE_BINDING, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + ifaceName = reply.readString16(); + } + } + + return status; + } + + virtual status_t setInterfaceBinding(const String16& ifaceName) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeString16(ifaceName); + status_t status = remote()->transact(SET_INTERFACE_BINDING, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getMasterAnnounceInterval(int *interval) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_MASTER_ANNOUNCE_INTERVAL, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *interval = reply.readInt32(); + } + } + + return status; + } + + virtual status_t setMasterAnnounceInterval(int interval) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeInt32(interval); + status_t status = remote()->transact(SET_MASTER_ANNOUNCE_INTERVAL, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getClientSyncInterval(int *interval) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_CLIENT_SYNC_INTERVAL, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *interval = reply.readInt32(); + } + } + + return status; + } + + virtual status_t setClientSyncInterval(int interval) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeInt32(interval); + status_t status = remote()->transact(SET_CLIENT_SYNC_INTERVAL, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getPanicThreshold(int *threshold) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_PANIC_THRESHOLD, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *threshold = reply.readInt32(); + } + } + + return status; + } + + virtual status_t setPanicThreshold(int threshold) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeInt32(threshold); + status_t status = remote()->transact(SET_PANIC_THRESHOLD, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t getAutoDisable(bool *autoDisable) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(GET_AUTO_DISABLE, + data, + &reply); + if (status == OK) { + status = reply.readInt32(); + if (status == OK) { + *autoDisable = (0 != reply.readInt32()); + } + } + + return status; + } + + virtual status_t setAutoDisable(bool autoDisable) { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + data.writeInt32(autoDisable ? 1 : 0); + status_t status = remote()->transact(SET_AUTO_DISABLE, + data, + &reply); + + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } + + virtual status_t forceNetworklessMasterMode() { + Parcel data, reply; + data.writeInterfaceToken(ICommonTimeConfig::getInterfaceDescriptor()); + status_t status = remote()->transact(FORCE_NETWORKLESS_MASTER_MODE, + data, + &reply); + + if (status == OK) { + status = reply.readInt32(); + } + + return status; + } +}; + +IMPLEMENT_META_INTERFACE(CommonTimeConfig, "android.os.ICommonTimeConfig"); + +status_t BnCommonTimeConfig::onTransact(uint32_t code, + const Parcel& data, + Parcel* reply, + uint32_t flags) { + switch(code) { + case GET_MASTER_ELECTION_PRIORITY: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + uint8_t priority; + status_t status = getMasterElectionPriority(&priority); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(static_cast<int32_t>(priority)); + } + return OK; + } break; + + case SET_MASTER_ELECTION_PRIORITY: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + uint8_t priority = static_cast<uint8_t>(data.readInt32()); + status_t status = setMasterElectionPriority(priority); + reply->writeInt32(status); + return OK; + } break; + + case GET_MASTER_ELECTION_ENDPOINT: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + struct sockaddr_storage addr; + status_t status = getMasterElectionEndpoint(&addr); + + if ((status == OK) && !canSerializeSockaddr(&addr)) { + status = UNKNOWN_ERROR; + } + + reply->writeInt32(status); + + if (status == OK) { + serializeSockaddr(reply, &addr); + } + + return OK; + } break; + + case SET_MASTER_ELECTION_ENDPOINT: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + struct sockaddr_storage addr; + int hasAddr = data.readInt32(); + + status_t status; + if (hasAddr) { + deserializeSockaddr(&data, &addr); + status = setMasterElectionEndpoint(&addr); + } else { + status = setMasterElectionEndpoint(&addr); + } + + reply->writeInt32(status); + return OK; + } break; + + case GET_MASTER_ELECTION_GROUP_ID: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + uint64_t id; + status_t status = getMasterElectionGroupId(&id); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt64(id); + } + return OK; + } break; + + case SET_MASTER_ELECTION_GROUP_ID: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + uint64_t id = static_cast<uint64_t>(data.readInt64()); + status_t status = setMasterElectionGroupId(id); + reply->writeInt32(status); + return OK; + } break; + + case GET_INTERFACE_BINDING: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + String16 ret; + status_t status = getInterfaceBinding(ret); + reply->writeInt32(status); + if (status == OK) { + reply->writeString16(ret); + } + return OK; + } break; + + case SET_INTERFACE_BINDING: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + String16 ifaceName; + ifaceName = data.readString16(); + status_t status = setInterfaceBinding(ifaceName); + reply->writeInt32(status); + return OK; + } break; + + case GET_MASTER_ANNOUNCE_INTERVAL: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + int interval; + status_t status = getMasterAnnounceInterval(&interval); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(interval); + } + return OK; + } break; + + case SET_MASTER_ANNOUNCE_INTERVAL: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + int interval = data.readInt32(); + status_t status = setMasterAnnounceInterval(interval); + reply->writeInt32(status); + return OK; + } break; + + case GET_CLIENT_SYNC_INTERVAL: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + int interval; + status_t status = getClientSyncInterval(&interval); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(interval); + } + return OK; + } break; + + case SET_CLIENT_SYNC_INTERVAL: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + int interval = data.readInt32(); + status_t status = setClientSyncInterval(interval); + reply->writeInt32(status); + return OK; + } break; + + case GET_PANIC_THRESHOLD: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + int threshold; + status_t status = getPanicThreshold(&threshold); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(threshold); + } + return OK; + } break; + + case SET_PANIC_THRESHOLD: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + int threshold = data.readInt32(); + status_t status = setPanicThreshold(threshold); + reply->writeInt32(status); + return OK; + } break; + + case GET_AUTO_DISABLE: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + bool autoDisable; + status_t status = getAutoDisable(&autoDisable); + reply->writeInt32(status); + if (status == OK) { + reply->writeInt32(autoDisable ? 1 : 0); + } + return OK; + } break; + + case SET_AUTO_DISABLE: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + bool autoDisable = (0 != data.readInt32()); + status_t status = setAutoDisable(autoDisable); + reply->writeInt32(status); + return OK; + } break; + + case FORCE_NETWORKLESS_MASTER_MODE: { + CHECK_INTERFACE(ICommonTimeConfig, data, reply); + status_t status = forceNetworklessMasterMode(); + reply->writeInt32(status); + return OK; + } break; + } + return BBinder::onTransact(code, data, reply, flags); +} + +}; // namespace android + diff --git a/libs/common_time/cc_helper.cpp b/libs/common_time/cc_helper.cpp new file mode 100644 index 000000000000..8d8556cb7f54 --- /dev/null +++ b/libs/common_time/cc_helper.cpp @@ -0,0 +1,129 @@ +/* + * 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. + */ + +#include <stdint.h> + +#include <common_time/cc_helper.h> +#include <common_time/ICommonClock.h> +#include <utils/threads.h> + +namespace android { + +Mutex CCHelper::lock_; +sp<ICommonClock> CCHelper::common_clock_; +sp<ICommonClockListener> CCHelper::common_clock_listener_; +uint32_t CCHelper::ref_count_ = 0; + +bool CCHelper::verifyClock_l() { + bool ret = false; + + if (common_clock_ == NULL) { + common_clock_ = ICommonClock::getInstance(); + if (common_clock_ == NULL) + goto bailout; + } + + if (ref_count_ > 0) { + if (common_clock_listener_ == NULL) { + common_clock_listener_ = new CommonClockListener(); + if (common_clock_listener_ == NULL) + goto bailout; + + if (OK != common_clock_->registerListener(common_clock_listener_)) + goto bailout; + } + } + + ret = true; + +bailout: + if (!ret) { + common_clock_listener_ = NULL; + common_clock_ = NULL; + } + return ret; +} + +CCHelper::CCHelper() { + Mutex::Autolock lock(&lock_); + ref_count_++; + verifyClock_l(); +} + +CCHelper::~CCHelper() { + Mutex::Autolock lock(&lock_); + + assert(ref_count_ > 0); + ref_count_--; + + // If we were the last CCHelper instance in the system, and we had + // previously register a listener, unregister it now so that the common time + // service has the chance to go into auto-disabled mode. + if (!ref_count_ && + (common_clock_ != NULL) && + (common_clock_listener_ != NULL)) { + common_clock_->unregisterListener(common_clock_listener_); + common_clock_listener_ = NULL; + } +} + +void CCHelper::CommonClockListener::onTimelineChanged(uint64_t timelineID) { + // do nothing; listener is only really used as a token so the server can + // find out when clients die. +} + +// Helper methods which attempts to make calls to the common time binder +// service. If the first attempt fails with DEAD_OBJECT, the helpers will +// attempt to make a connection to the service again (assuming that the process +// hosting the service had crashed and the client proxy we are holding is dead) +// If the second attempt fails, or no connection can be made, the we let the +// error propagate up the stack and let the caller deal with the situation as +// best they can. +#define CCHELPER_METHOD(decl, call) \ + status_t CCHelper::decl { \ + Mutex::Autolock lock(&lock_); \ + \ + if (!verifyClock_l()) \ + return DEAD_OBJECT; \ + \ + status_t status = common_clock_->call; \ + if (DEAD_OBJECT == status) { \ + if (!verifyClock_l()) \ + return DEAD_OBJECT; \ + status = common_clock_->call; \ + } \ + \ + return status; \ + } + +#define VERIFY_CLOCK() + +CCHELPER_METHOD(isCommonTimeValid(bool* valid, uint32_t* timelineID), + isCommonTimeValid(valid, timelineID)) +CCHELPER_METHOD(commonTimeToLocalTime(int64_t commonTime, int64_t* localTime), + commonTimeToLocalTime(commonTime, localTime)) +CCHELPER_METHOD(localTimeToCommonTime(int64_t localTime, int64_t* commonTime), + localTimeToCommonTime(localTime, commonTime)) +CCHELPER_METHOD(getCommonTime(int64_t* commonTime), + getCommonTime(commonTime)) +CCHELPER_METHOD(getCommonFreq(uint64_t* freq), + getCommonFreq(freq)) +CCHELPER_METHOD(getLocalTime(int64_t* localTime), + getLocalTime(localTime)) +CCHELPER_METHOD(getLocalFreq(uint64_t* freq), + getLocalFreq(freq)) + +} // namespace android diff --git a/libs/common_time/local_clock.cpp b/libs/common_time/local_clock.cpp new file mode 100644 index 000000000000..a7c61fc76aa5 --- /dev/null +++ b/libs/common_time/local_clock.cpp @@ -0,0 +1,92 @@ +/* + * 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 "common_time" +#include <utils/Log.h> + +#include <assert.h> +#include <stdint.h> + +#include <common_time/local_clock.h> +#include <hardware/hardware.h> +#include <hardware/local_time_hal.h> +#include <utils/Errors.h> +#include <utils/threads.h> + +namespace android { + +Mutex LocalClock::dev_lock_; +local_time_hw_device_t* LocalClock::dev_ = NULL; + +LocalClock::LocalClock() { + int res; + const hw_module_t* mod; + + AutoMutex lock(&dev_lock_); + + if (dev_ != NULL) + return; + + res = hw_get_module_by_class(LOCAL_TIME_HARDWARE_MODULE_ID, NULL, &mod); + if (res) { + ALOGE("Failed to open local time HAL module (res = %d)", res); + } else { + res = local_time_hw_device_open(mod, &dev_); + if (res) { + ALOGE("Failed to open local time HAL device (res = %d)", res); + dev_ = NULL; + } + } +} + +bool LocalClock::initCheck() { + return (NULL != dev_); +} + +int64_t LocalClock::getLocalTime() { + assert(NULL != dev_); + assert(NULL != dev_->get_local_time); + + return dev_->get_local_time(dev_); +} + +uint64_t LocalClock::getLocalFreq() { + assert(NULL != dev_); + assert(NULL != dev_->get_local_freq); + + return dev_->get_local_freq(dev_); +} + +status_t LocalClock::setLocalSlew(int16_t rate) { + assert(NULL != dev_); + + if (!dev_->set_local_slew) + return INVALID_OPERATION; + + return static_cast<status_t>(dev_->set_local_slew(dev_, rate)); +} + +int32_t LocalClock::getDebugLog(struct local_time_debug_event* records, + int max_records) { + assert(NULL != dev_); + + if (!dev_->get_debug_log) + return INVALID_OPERATION; + + return dev_->get_debug_log(dev_, records, max_records); +} + +} // namespace android diff --git a/libs/common_time/utils.cpp b/libs/common_time/utils.cpp new file mode 100644 index 000000000000..65391711b0bd --- /dev/null +++ b/libs/common_time/utils.cpp @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2012 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 <arpa/inet.h> +#include <linux/socket.h> + +#include <binder/Parcel.h> + +namespace android { + +bool canSerializeSockaddr(const struct sockaddr_storage* addr) { + switch (addr->ss_family) { + case AF_INET: + case AF_INET6: + return true; + default: + return false; + } +} + +void serializeSockaddr(Parcel* p, const struct sockaddr_storage* addr) { + switch (addr->ss_family) { + case AF_INET: { + const struct sockaddr_in* s = + reinterpret_cast<const struct sockaddr_in*>(addr); + p->writeInt32(AF_INET); + p->writeInt32(ntohl(s->sin_addr.s_addr)); + p->writeInt32(static_cast<int32_t>(ntohs(s->sin_port))); + } break; + + case AF_INET6: { + const struct sockaddr_in6* s = + reinterpret_cast<const struct sockaddr_in6*>(addr); + const int32_t* a = + reinterpret_cast<const int32_t*>(s->sin6_addr.s6_addr); + p->writeInt32(AF_INET6); + p->writeInt32(ntohl(a[0])); + p->writeInt32(ntohl(a[1])); + p->writeInt32(ntohl(a[2])); + p->writeInt32(ntohl(a[3])); + p->writeInt32(static_cast<int32_t>(ntohs(s->sin6_port))); + p->writeInt32(ntohl(s->sin6_flowinfo)); + p->writeInt32(ntohl(s->sin6_scope_id)); + } break; + } +} + +void deserializeSockaddr(const Parcel* p, struct sockaddr_storage* addr) { + memset(addr, 0, sizeof(addr)); + + addr->ss_family = p->readInt32(); + switch(addr->ss_family) { + case AF_INET: { + struct sockaddr_in* s = + reinterpret_cast<struct sockaddr_in*>(addr); + s->sin_addr.s_addr = htonl(p->readInt32()); + s->sin_port = htons(static_cast<uint16_t>(p->readInt32())); + } break; + + case AF_INET6: { + struct sockaddr_in6* s = + reinterpret_cast<struct sockaddr_in6*>(addr); + int32_t* a = reinterpret_cast<int32_t*>(s->sin6_addr.s6_addr); + + a[0] = htonl(p->readInt32()); + a[1] = htonl(p->readInt32()); + a[2] = htonl(p->readInt32()); + a[3] = htonl(p->readInt32()); + s->sin6_port = htons(static_cast<uint16_t>(p->readInt32())); + s->sin6_flowinfo = htonl(p->readInt32()); + s->sin6_scope_id = htonl(p->readInt32()); + } break; + } +} + +} // namespace android diff --git a/libs/common_time/utils.h b/libs/common_time/utils.h new file mode 100644 index 000000000000..ce79d0d74cec --- /dev/null +++ b/libs/common_time/utils.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2012 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_LIBCOMMONCLOCK_UTILS_H +#define ANDROID_LIBCOMMONCLOCK_UTILS_H + +#include <linux/socket.h> + +#include <binder/Parcel.h> +#include <utils/Errors.h> + +namespace android { + +extern bool canSerializeSockaddr(const struct sockaddr_storage* addr); +extern void serializeSockaddr(Parcel* p, const struct sockaddr_storage* addr); +extern status_t deserializeSockaddr(const Parcel* p, + struct sockaddr_storage* addr); + +}; // namespace android + +#endif // ANDROID_LIBCOMMONCLOCK_UTILS_H diff --git a/libs/hwui/Debug.h b/libs/hwui/Debug.h index 16a3d73a121f..55a860ebc69b 100644 --- a/libs/hwui/Debug.h +++ b/libs/hwui/Debug.h @@ -30,7 +30,7 @@ #define DEBUG_MEMORY_USAGE 0 // Turn on to enable debugging of cache flushes -#define DEBUG_CACHE_FLUSH 1 +#define DEBUG_CACHE_FLUSH 0 // Turn on to enable layers debugging when rendered as regions #define DEBUG_LAYERS_AS_REGIONS 0 diff --git a/libs/hwui/DisplayListRenderer.cpp b/libs/hwui/DisplayListRenderer.cpp index 1a11fbcfad68..f9088aca3718 100644 --- a/libs/hwui/DisplayListRenderer.cpp +++ b/libs/hwui/DisplayListRenderer.cpp @@ -226,6 +226,11 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { while (!mReader.eof()) { int op = mReader.readInt(); + if (op & OP_MAY_BE_SKIPPED_MASK) { + int skip = mReader.readInt(); + ALOGD("%sSkip %d", (char*) indent, skip); + op &= ~OP_MAY_BE_SKIPPED_MASK; + } switch (op) { case DrawGLFunction: { @@ -316,8 +321,9 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { DisplayList* displayList = getDisplayList(); uint32_t width = getUInt(); uint32_t height = getUInt(); - ALOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op], - displayList, width, height, level + 1); + int32_t flags = getInt(); + ALOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op], + displayList, width, height, flags, level + 1); renderer.outputDisplayList(displayList, level + 1); } break; @@ -551,7 +557,7 @@ void DisplayList::output(OpenGLRenderer& renderer, uint32_t level) { * in the output() function, since that function processes the same list of opcodes for the * purposes of logging display list info for a given view. */ -bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) { +bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level) { bool needsInvalidate = false; TextContainer text; mReader.rewind(); @@ -572,6 +578,18 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) int saveCount = renderer.getSaveCount() - 1; while (!mReader.eof()) { int op = mReader.readInt(); + if (op & OP_MAY_BE_SKIPPED_MASK) { + int32_t skip = mReader.readInt() * 4; + if (CC_LIKELY(flags & kReplayFlag_ClipChildren)) { + mReader.skip(skip); + DISPLAY_LIST_LOGD("%s%s skipping %d bytes", (char*) indent, + OP_NAMES[op & ~OP_MAY_BE_SKIPPED_MASK], skip); + continue; + } else { + op &= ~OP_MAY_BE_SKIPPED_MASK; + ALOGD("%s", OP_NAMES[op]); + } + } logBuffer.writeCommand(level, op); switch (op) { @@ -584,7 +602,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case Save: { - int rendererNum = getInt(); + int32_t rendererNum = getInt(); DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], rendererNum); renderer.save(rendererNum); } @@ -595,7 +613,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case RestoreToCount: { - int restoreCount = saveCount + getInt(); + int32_t restoreCount = saveCount + getInt(); DISPLAY_LIST_LOGD("%s%s %d", (char*) indent, OP_NAMES[op], restoreCount); renderer.restoreToCount(restoreCount); } @@ -606,7 +624,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f3 = getFloat(); float f4 = getFloat(); SkPaint* paint = getPaint(renderer); - int flags = getInt(); + int32_t flags = getInt(); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %p, 0x%x", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, paint, flags); renderer.saveLayer(f1, f2, f3, f4, paint, flags); @@ -617,8 +635,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - int alpha = getInt(); - int flags = getInt(); + int32_t alpha = getInt(); + int32_t flags = getInt(); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d, 0x%x", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, alpha, flags); renderer.saveLayerAlpha(f1, f2, f3, f4, alpha, flags); @@ -668,7 +686,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f2 = getFloat(); float f3 = getFloat(); float f4 = getFloat(); - int regionOp = getInt(); + int32_t regionOp = getInt(); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %d", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, regionOp); renderer.clipRect(f1, f2, f3, f4, (SkRegion::Op) regionOp); @@ -678,10 +696,11 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) DisplayList* displayList = getDisplayList(); uint32_t width = getUInt(); uint32_t height = getUInt(); - DISPLAY_LIST_LOGD("%s%s %p, %dx%d, %d", (char*) indent, OP_NAMES[op], - displayList, width, height, level + 1); + int32_t flags = getInt(); + DISPLAY_LIST_LOGD("%s%s %p, %dx%d, 0x%x %d", (char*) indent, OP_NAMES[op], + displayList, width, height, flags, level + 1); needsInvalidate |= renderer.drawDisplayList(displayList, width, height, - dirty, level + 1); + dirty, flags, level + 1); } break; case DrawLayer: { @@ -730,7 +749,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case DrawBitmapMesh: { - int verticesCount = 0; + int32_t verticesCount = 0; uint32_t colorsCount = 0; SkBitmap* bitmap = getBitmap(); @@ -738,7 +757,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) uint32_t meshHeight = getInt(); float* vertices = getFloats(verticesCount); bool hasColors = getInt(); - int* colors = hasColors ? getInts(colorsCount) : NULL; + int32_t* colors = hasColors ? getInts(colorsCount) : NULL; SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); @@ -771,8 +790,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case DrawColor: { - int color = getInt(); - int xferMode = getInt(); + int32_t color = getInt(); + int32_t xferMode = getInt(); DISPLAY_LIST_LOGD("%s%s 0x%x %d", (char*) indent, OP_NAMES[op], color, xferMode); renderer.drawColor(color, (SkXfermode::Mode) xferMode); } @@ -829,7 +848,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float f4 = getFloat(); float f5 = getFloat(); float f6 = getFloat(); - int i1 = getInt(); + int32_t i1 = getInt(); SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, %.2f, %.2f, %.2f, %d, %p", (char*) indent, OP_NAMES[op], f1, f2, f3, f4, f5, f6, i1, paint); @@ -844,7 +863,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case DrawLines: { - int count = 0; + int32_t count = 0; float* points = getFloats(count); SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); @@ -852,7 +871,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case DrawPoints: { - int count = 0; + int32_t count = 0; float* points = getFloats(count); SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s", (char*) indent, OP_NAMES[op]); @@ -861,7 +880,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) break; case DrawText: { getText(&text); - int count = getInt(); + int32_t count = getInt(); float x = getFloat(); float y = getFloat(); SkPaint* paint = getPaint(renderer); @@ -873,8 +892,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) break; case DrawPosText: { getText(&text); - int count = getInt(); - int positionsCount = 0; + int32_t count = getInt(); + int32_t positionsCount = 0; float* positions = getFloats(positionsCount); SkPaint* paint = getPaint(renderer); DISPLAY_LIST_LOGD("%s%s %s, %d, %d, %p", (char*) indent, @@ -913,7 +932,7 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) float radius = getFloat(); float dx = getFloat(); float dy = getFloat(); - int color = getInt(); + int32_t color = getInt(); DISPLAY_LIST_LOGD("%s%s %.2f, %.2f, %.2f, 0x%x", (char*) indent, OP_NAMES[op], radius, dx, dy, color); renderer.setupShadow(radius, dx, dy, color); @@ -925,8 +944,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) } break; case SetupPaintFilter: { - int clearBits = getInt(); - int setBits = getInt(); + int32_t clearBits = getInt(); + int32_t setBits = getInt(); DISPLAY_LIST_LOGD("%s%s 0x%x, 0x%x", (char*) indent, OP_NAMES[op], clearBits, setBits); renderer.setupPaintFilter(clearBits, setBits); @@ -949,7 +968,8 @@ bool DisplayList::replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level) // Base structure /////////////////////////////////////////////////////////////////////////////// -DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE), mHasDrawOps(false) { +DisplayListRenderer::DisplayListRenderer(): mWriter(MIN_WRITER_SIZE), + mTranslateX(0.0f), mTranslateY(0.0f), mHasTranslate(false), mHasDrawOps(false) { } DisplayListRenderer::~DisplayListRenderer() { @@ -1019,6 +1039,7 @@ void DisplayListRenderer::prepareDirty(float left, float top, void DisplayListRenderer::finish() { insertRestoreToCount(); + insertTranlate(); OpenGLRenderer::finish(); } @@ -1043,15 +1064,18 @@ int DisplayListRenderer::save(int flags) { void DisplayListRenderer::restore() { if (mRestoreSaveCount < 0) { - addOp(DisplayList::Restore); - } else { - mRestoreSaveCount--; + restoreToCount(getSaveCount() - 1); + return; } + + mRestoreSaveCount--; + insertTranlate(); OpenGLRenderer::restore(); } void DisplayListRenderer::restoreToCount(int saveCount) { mRestoreSaveCount = saveCount; + insertTranlate(); OpenGLRenderer::restoreToCount(saveCount); } @@ -1074,8 +1098,10 @@ int DisplayListRenderer::saveLayerAlpha(float left, float top, float right, floa } void DisplayListRenderer::translate(float dx, float dy) { - addOp(DisplayList::Translate); - addPoint(dx, dy); + mHasTranslate = true; + mTranslateX += dx; + mTranslateY += dy; + insertRestoreToCount(); OpenGLRenderer::translate(dx, dy); } @@ -1118,12 +1144,15 @@ bool DisplayListRenderer::clipRect(float left, float top, float right, float bot } bool DisplayListRenderer::drawDisplayList(DisplayList* displayList, - uint32_t width, uint32_t height, Rect& dirty, uint32_t level) { + uint32_t width, uint32_t height, Rect& dirty, int32_t flags, uint32_t level) { // dirty is an out parameter and should not be recorded, // it matters only when replaying the display list - addOp(DisplayList::DrawDisplayList); + const bool reject = quickReject(0.0f, 0.0f, width, height); + uint32_t* location = addOp(DisplayList::DrawDisplayList, reject); addDisplayList(displayList); addSize(width, height); + addInt(flags); + addSkip(location); return false; } @@ -1134,30 +1163,38 @@ void DisplayListRenderer::drawLayer(Layer* layer, float x, float y, SkPaint* pai addPaint(paint); } -void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, - SkPaint* paint) { - addOp(DisplayList::DrawBitmap); +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint) { + const bool reject = quickReject(left, top, left + bitmap->width(), top + bitmap->height()); + uint32_t* location = addOp(DisplayList::DrawBitmap, reject); addBitmap(bitmap); addPoint(left, top); addPaint(paint); + addSkip(location); } -void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, - SkPaint* paint) { - addOp(DisplayList::DrawBitmapMatrix); +void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint) { + Rect r(0.0f, 0.0f, bitmap->width(), bitmap->height()); + const mat4 transform(*matrix); + transform.mapRect(r); + + const bool reject = quickReject(r.left, r.top, r.right, r.bottom); + uint32_t* location = addOp(DisplayList::DrawBitmapMatrix, reject); addBitmap(bitmap); addMatrix(matrix); addPaint(paint); + addSkip(location); } void DisplayListRenderer::drawBitmap(SkBitmap* bitmap, float srcLeft, float srcTop, float srcRight, float srcBottom, float dstLeft, float dstTop, float dstRight, float dstBottom, SkPaint* paint) { - addOp(DisplayList::DrawBitmapRect); + const bool reject = quickReject(dstLeft, dstTop, dstRight, dstBottom); + uint32_t* location = addOp(DisplayList::DrawBitmapRect, reject); addBitmap(bitmap); addBounds(srcLeft, srcTop, srcRight, srcBottom); addBounds(dstLeft, dstTop, dstRight, dstBottom); addPaint(paint); + addSkip(location); } void DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int meshHeight, @@ -1179,13 +1216,15 @@ void DisplayListRenderer::drawBitmapMesh(SkBitmap* bitmap, int meshWidth, int me void DisplayListRenderer::drawPatch(SkBitmap* bitmap, const int32_t* xDivs, const int32_t* yDivs, const uint32_t* colors, uint32_t width, uint32_t height, int8_t numColors, float left, float top, float right, float bottom, SkPaint* paint) { - addOp(DisplayList::DrawPatch); + const bool reject = quickReject(left, top, right, bottom); + uint32_t* location = addOp(DisplayList::DrawPatch, reject); addBitmap(bitmap); addInts(xDivs, width); addInts(yDivs, height); addUInts(colors, numColors); addBounds(left, top, right, bottom); addPaint(paint); + addSkip(location); } void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { @@ -1196,17 +1235,23 @@ void DisplayListRenderer::drawColor(int color, SkXfermode::Mode mode) { void DisplayListRenderer::drawRect(float left, float top, float right, float bottom, SkPaint* paint) { - addOp(DisplayList::DrawRect); + const bool reject = paint->getStyle() == SkPaint::kFill_Style && + quickReject(left, top, right, bottom); + uint32_t* location = addOp(DisplayList::DrawRect, reject); addBounds(left, top, right, bottom); addPaint(paint); + addSkip(location); } void DisplayListRenderer::drawRoundRect(float left, float top, float right, float bottom, float rx, float ry, SkPaint* paint) { - addOp(DisplayList::DrawRoundRect); + const bool reject = paint->getStyle() == SkPaint::kFill_Style && + quickReject(left, top, right, bottom); + uint32_t* location = addOp(DisplayList::DrawRoundRect, reject); addBounds(left, top, right, bottom); addPoint(rx, ry); addPaint(paint); + addSkip(location); } void DisplayListRenderer::drawCircle(float x, float y, float radius, SkPaint* paint) { @@ -1233,9 +1278,15 @@ void DisplayListRenderer::drawArc(float left, float top, float right, float bott } void DisplayListRenderer::drawPath(SkPath* path, SkPaint* paint) { - addOp(DisplayList::DrawPath); + float left, top, offset; + uint32_t width, height; + computePathBounds(path, paint, left, top, offset, width, height); + + const bool reject = quickReject(left - offset, top - offset, width, height); + uint32_t* location = addOp(DisplayList::DrawPath, reject); addPath(path); addPaint(paint); + addSkip(location); } void DisplayListRenderer::drawLines(float* points, int count, SkPaint* paint) { @@ -1252,11 +1303,8 @@ void DisplayListRenderer::drawPoints(float* points, int count, SkPaint* paint) { void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, float x, float y, SkPaint* paint, float length) { - if (count <= 0) return; - addOp(DisplayList::DrawText); - addText(text, bytesCount); - addInt(count); - addPoint(x, y); + if (!text || count <= 0) return; + // TODO: We should probably make a copy of the paint instead of modifying // it; modifying the paint will change its generationID the first // time, which might impact caches. More investigation needed to @@ -1265,13 +1313,27 @@ void DisplayListRenderer::drawText(const char* text, int bytesCount, int count, // its own copy as it does right now. // Beware: this needs Glyph encoding (already done on the Paint constructor) paint->setAntiAlias(true); + if (length < 0.0f) length = paint->measureText(text, bytesCount); + + bool reject = false; + if (CC_LIKELY(paint->getTextAlign() == SkPaint::kLeft_Align)) { + SkPaint::FontMetrics metrics; + paint->getFontMetrics(&metrics, 0.0f); + reject = quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom); + } + + uint32_t* location = addOp(DisplayList::DrawText, reject); + addText(text, bytesCount); + addInt(count); + addPoint(x, y); addPaint(paint); - addFloat(length < 0.0f ? paint->measureText(text, bytesCount) : length); + addFloat(length); + addSkip(location); } void DisplayListRenderer::drawPosText(const char* text, int bytesCount, int count, const float* positions, SkPaint* paint) { - if (count <= 0) return; + if (!text || count <= 0) return; addOp(DisplayList::DrawPosText); addText(text, bytesCount); addInt(count); diff --git a/libs/hwui/DisplayListRenderer.h b/libs/hwui/DisplayListRenderer.h index 46506e4e9a96..90a714526623 100644 --- a/libs/hwui/DisplayListRenderer.h +++ b/libs/hwui/DisplayListRenderer.h @@ -42,6 +42,7 @@ namespace uirenderer { /////////////////////////////////////////////////////////////////////////////// #define MIN_WRITER_SIZE 4096 +#define OP_MAY_BE_SKIPPED_MASK 0xff000000 // Debug #if DEBUG_DISPLAY_LIST @@ -110,13 +111,18 @@ public: DrawGLFunction, }; + // See flags defined in DisplayList.java + enum ReplayFlag { + kReplayFlag_ClipChildren = 0x1 + }; + static const char* OP_NAMES[]; void initFromDisplayListRenderer(const DisplayListRenderer& recorder, bool reusing = false); ANDROID_API size_t getSize(); - bool replay(OpenGLRenderer& renderer, Rect& dirty, uint32_t level = 0); + bool replay(OpenGLRenderer& renderer, Rect& dirty, int32_t flags, uint32_t level = 0); void output(OpenGLRenderer& renderer, uint32_t level = 0); @@ -167,11 +173,11 @@ private: return (SkiaColorFilter*) getInt(); } - inline int getIndex() { + inline int32_t getIndex() { return mReader.readInt(); } - inline int getInt() { + inline int32_t getInt() { return mReader.readInt(); } @@ -209,7 +215,7 @@ private: return (uint32_t*) mReader.skip(count * sizeof(uint32_t)); } - float* getFloats(int& count) { + float* getFloats(int32_t& count) { count = getInt(); return (float*) mReader.skip(count * sizeof(float)); } @@ -279,7 +285,7 @@ public: virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, - Rect& dirty, uint32_t level = 0); + Rect& dirty, int32_t flags, uint32_t level = 0); virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, SkMatrix* matrix, SkPaint* paint); @@ -358,13 +364,45 @@ private: } } - inline void addOp(DisplayList::Op drawOp) { + void insertTranlate() { + if (mHasTranslate) { + if (mTranslateX != 0.0f || mTranslateY != 0.0f) { + mWriter.writeInt(DisplayList::Translate); + addPoint(mTranslateX, mTranslateY); + mTranslateX = mTranslateY = 0.0f; + } + mHasTranslate = false; + } + } + + inline void addOp(const DisplayList::Op drawOp) { insertRestoreToCount(); + insertTranlate(); mWriter.writeInt(drawOp); mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList; } - inline void addInt(int value) { + uint32_t* addOp(const DisplayList::Op drawOp, const bool reject) { + insertRestoreToCount(); + insertTranlate(); + mHasDrawOps = mHasDrawOps || drawOp >= DisplayList::DrawDisplayList; + if (reject) { + mWriter.writeInt(OP_MAY_BE_SKIPPED_MASK | drawOp); + mWriter.writeInt(0); + uint32_t* location = reject ? mWriter.peek32(mWriter.size() - 4) : NULL; + return location; + } + mWriter.writeInt(drawOp); + return NULL; + } + + inline void addSkip(uint32_t* location) { + if (location) { + *location = (int32_t) (mWriter.peek32(mWriter.size() - 4) - location); + } + } + + inline void addInt(int32_t value) { mWriter.writeInt(value); } @@ -391,9 +429,9 @@ private: mWriter.writeScalar(value); } - void addFloats(const float* values, int count) { + void addFloats(const float* values, int32_t count) { mWriter.writeInt(count); - for (int i = 0; i < count; i++) { + for (int32_t i = 0; i < count; i++) { mWriter.writeScalar(values[i]); } } @@ -424,7 +462,8 @@ private: SkPath* pathCopy = mPathMap.valueFor(path); if (pathCopy == NULL || pathCopy->getGenerationID() != path->getGenerationID()) { pathCopy = new SkPath(*path); - mPathMap.add(path, pathCopy); + // replaceValueFor() performs an add if the entry doesn't exist + mPathMap.replaceValueFor(path, pathCopy); mPaths.add(pathCopy); } @@ -440,7 +479,8 @@ private: SkPaint* paintCopy = mPaintMap.valueFor(paint); if (paintCopy == NULL || paintCopy->getGenerationID() != paint->getGenerationID()) { paintCopy = new SkPaint(*paint); - mPaintMap.add(paint, paintCopy); + // replaceValueFor() performs an add if the entry doesn't exist + mPaintMap.replaceValueFor(paint, paintCopy); mPaints.add(paintCopy); } @@ -482,7 +522,8 @@ private: // TODO: We also need to handle generation ID changes in compose shaders if (shaderCopy == NULL || shaderCopy->getGenerationId() != shader->getGenerationId()) { shaderCopy = shader->copy(); - mShaderMap.add(shader, shaderCopy); + // replaceValueFor() performs an add if the entry doesn't exist + mShaderMap.replaceValueFor(shader, shaderCopy); mShaders.add(shaderCopy); Caches::getInstance().resourceCache.incrementRefcount(shaderCopy); } @@ -513,6 +554,11 @@ private: SkWriter32 mWriter; int mRestoreSaveCount; + + float mTranslateX; + float mTranslateY; + bool mHasTranslate; + bool mHasDrawOps; friend class DisplayList; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index afae70fddb5d..55e2ca5d7f24 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1321,7 +1321,7 @@ void OpenGLRenderer::finishDrawTexture() { /////////////////////////////////////////////////////////////////////////////// bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, - Rect& dirty, uint32_t level) { + Rect& dirty, int32_t flags, uint32_t level) { if (quickReject(0.0f, 0.0f, width, height)) { return false; } @@ -1329,7 +1329,7 @@ bool OpenGLRenderer::drawDisplayList(DisplayList* displayList, uint32_t width, u // All the usual checks and setup operations (quickReject, setupDraw, etc.) // will be performed by the display list itself if (displayList && displayList->isRenderable()) { - return displayList->replay(*this, dirty, level); + return displayList->replay(*this, dirty, flags, level); } return false; @@ -2189,8 +2189,7 @@ void OpenGLRenderer::drawText(const char* text, int bytesCount, int count, SkPaint::FontMetrics metrics; paint->getFontMetrics(&metrics, 0.0f); // If no length was specified, just perform the hit test on the Y axis - if (quickReject(x, y + metrics.fTop, - x + (length >= 0.0f ? length : INT_MAX / 2), y + metrics.fBottom)) { + if (quickReject(x, y + metrics.fTop, x + length, y + metrics.fBottom)) { return; } @@ -2298,6 +2297,7 @@ void OpenGLRenderer::drawPath(SkPath* path, SkPaint* paint) { mCaches.activeTexture(0); + // TODO: Perform early clip test before we rasterize the path const PathTexture* texture = mCaches.pathCache.get(path, paint); if (!texture) return; const AutoTexture autoCleanup(texture); diff --git a/libs/hwui/OpenGLRenderer.h b/libs/hwui/OpenGLRenderer.h index 3c2d09e81769..3f63c3fe11a1 100644 --- a/libs/hwui/OpenGLRenderer.h +++ b/libs/hwui/OpenGLRenderer.h @@ -98,7 +98,7 @@ public: virtual bool clipRect(float left, float top, float right, float bottom, SkRegion::Op op); virtual bool drawDisplayList(DisplayList* displayList, uint32_t width, uint32_t height, - Rect& dirty, uint32_t level = 0); + Rect& dirty, int32_t flags, uint32_t level = 0); virtual void outputDisplayList(DisplayList* displayList, uint32_t level = 0); virtual void drawLayer(Layer* layer, float x, float y, SkPaint* paint); virtual void drawBitmap(SkBitmap* bitmap, float left, float top, SkPaint* paint); diff --git a/libs/hwui/PathCache.cpp b/libs/hwui/PathCache.cpp index e893f7a95adb..e09c24381d9d 100644 --- a/libs/hwui/PathCache.cpp +++ b/libs/hwui/PathCache.cpp @@ -24,6 +24,23 @@ namespace android { namespace uirenderer { +// Defined in ShapeCache.h +void computePathBounds(const SkPath *path, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height) { + const SkRect& bounds = path->getBounds(); + + const float pathWidth = fmax(bounds.width(), 1.0f); + const float pathHeight = fmax(bounds.height(), 1.0f); + + left = bounds.fLeft; + top = bounds.fTop; + + offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); + + width = uint32_t(pathWidth + offset * 2.0 + 0.5); + height = uint32_t(pathHeight + offset * 2.0 + 0.5); +} + /////////////////////////////////////////////////////////////////////////////// // Path cache /////////////////////////////////////////////////////////////////////////////// @@ -69,6 +86,9 @@ PathTexture* PathCache::get(SkPath* path, SkPaint* paint) { PathCacheEntry entry(path, paint); PathTexture* texture = mCache.get(entry); + float left, top, offset; + uint32_t width, height; + if (!texture) { texture = addTexture(entry, path, paint); } else if (path->getGenerationID() != texture->generation) { diff --git a/libs/hwui/ShapeCache.h b/libs/hwui/ShapeCache.h index 30ce69081244..f180e942adc6 100644 --- a/libs/hwui/ShapeCache.h +++ b/libs/hwui/ShapeCache.h @@ -489,18 +489,16 @@ void ShapeCache<Entry>::removeTexture(PathTexture* texture) { } } +void computePathBounds(const SkPath *path, const SkPaint* paint, + float& left, float& top, float& offset, uint32_t& width, uint32_t& height); + template<class Entry> PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *path, const SkPaint* paint) { - const SkRect& bounds = path->getBounds(); - - const float pathWidth = fmax(bounds.width(), 1.0f); - const float pathHeight = fmax(bounds.height(), 1.0f); - - const float offset = (int) floorf(fmax(paint->getStrokeWidth(), 1.0f) * 1.5f + 0.5f); - const uint32_t width = uint32_t(pathWidth + offset * 2.0 + 0.5); - const uint32_t height = uint32_t(pathHeight + offset * 2.0 + 0.5); + float left, top, offset; + uint32_t width, height; + computePathBounds(path, paint, left, top, offset, width, height); if (width > mMaxTextureSize || height > mMaxTextureSize) { ALOGW("Shape %s too large to be rendered into a texture (%dx%d, max=%dx%d)", @@ -517,8 +515,8 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat } PathTexture* texture = new PathTexture; - texture->left = bounds.fLeft; - texture->top = bounds.fTop; + texture->left = left; + texture->top = top; texture->offset = offset; texture->width = width; texture->height = height; @@ -542,7 +540,7 @@ PathTexture* ShapeCache<Entry>::addTexture(const Entry& entry, const SkPath *pat SkSafeUnref(pathPaint.setXfermode(mode)); SkCanvas canvas(bitmap); - canvas.translate(-bounds.fLeft + offset, -bounds.fTop + offset); + canvas.translate(-left + offset, -top + offset); canvas.drawPath(*path, pathPaint); generateTexture(bitmap, texture); diff --git a/libs/rs/driver/rsdProgram.cpp b/libs/rs/driver/rsdProgram.cpp index 852b6bfa3dac..fa4cb0f4a70f 100644 --- a/libs/rs/driver/rsdProgram.cpp +++ b/libs/rs/driver/rsdProgram.cpp @@ -34,8 +34,11 @@ using namespace android; using namespace android::renderscript; bool rsdProgramVertexInit(const Context *rsc, const ProgramVertex *pv, - const char* shader, size_t shaderLen) { - RsdShader *drv = new RsdShader(pv, GL_VERTEX_SHADER, shader, shaderLen); + const char* shader, size_t shaderLen, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength) { + RsdShader *drv = new RsdShader(pv, GL_VERTEX_SHADER, shader, shaderLen, + textureNames, textureNamesCount, textureNamesLength); pv->mHal.drv = drv; return drv->createShader(); @@ -78,8 +81,11 @@ void rsdProgramVertexDestroy(const Context *rsc, const ProgramVertex *pv) { } bool rsdProgramFragmentInit(const Context *rsc, const ProgramFragment *pf, - const char* shader, size_t shaderLen) { - RsdShader *drv = new RsdShader(pf, GL_FRAGMENT_SHADER, shader, shaderLen); + const char* shader, size_t shaderLen, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength) { + RsdShader *drv = new RsdShader(pf, GL_FRAGMENT_SHADER, shader, shaderLen, + textureNames, textureNamesCount, textureNamesLength); pf->mHal.drv = drv; return drv->createShader(); diff --git a/libs/rs/driver/rsdProgramFragment.h b/libs/rs/driver/rsdProgramFragment.h index 366cb40d2962..b03a9fe65ac1 100644 --- a/libs/rs/driver/rsdProgramFragment.h +++ b/libs/rs/driver/rsdProgramFragment.h @@ -22,7 +22,9 @@ bool rsdProgramFragmentInit(const android::renderscript::Context *rsc, const android::renderscript::ProgramFragment *, - const char* shader, uint32_t shaderLen); + const char* shader, size_t shaderLen, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); void rsdProgramFragmentSetActive(const android::renderscript::Context *rsc, const android::renderscript::ProgramFragment *); void rsdProgramFragmentDestroy(const android::renderscript::Context *rsc, diff --git a/libs/rs/driver/rsdProgramVertex.h b/libs/rs/driver/rsdProgramVertex.h index e99857298165..f917a4123747 100644 --- a/libs/rs/driver/rsdProgramVertex.h +++ b/libs/rs/driver/rsdProgramVertex.h @@ -21,7 +21,9 @@ bool rsdProgramVertexInit(const android::renderscript::Context *rsc, const android::renderscript::ProgramVertex *, - const char* shader, uint32_t shaderLen); + const char* shader, size_t shaderLen, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); void rsdProgramVertexSetActive(const android::renderscript::Context *rsc, const android::renderscript::ProgramVertex *); void rsdProgramVertexDestroy(const android::renderscript::Context *rsc, diff --git a/libs/rs/driver/rsdShader.cpp b/libs/rs/driver/rsdShader.cpp index 0e5b3888eed1..1e73b95b6cd6 100644 --- a/libs/rs/driver/rsdShader.cpp +++ b/libs/rs/driver/rsdShader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2011 The Android Open Source Project + * Copyright (C) 2011-2012 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. @@ -30,14 +30,16 @@ using namespace android; using namespace android::renderscript; RsdShader::RsdShader(const Program *p, uint32_t type, - const char * shaderText, size_t shaderLength) { - + const char * shaderText, size_t shaderLength, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength) { mUserShader.setTo(shaderText, shaderLength); mRSProgram = p; mType = type; initMemberVars(); initAttribAndUniformArray(); - init(); + init(textureNames, textureNamesCount, textureNamesLength); + createTexturesString(textureNames, textureNamesCount, textureNamesLength); } RsdShader::~RsdShader() { @@ -65,25 +67,26 @@ void RsdShader::initMemberVars() { mIsValid = false; } -void RsdShader::init() { +void RsdShader::init(const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength) { uint32_t attribCount = 0; uint32_t uniformCount = 0; for (uint32_t ct=0; ct < mRSProgram->mHal.state.inputElementsCount; ct++) { - initAddUserElement(mRSProgram->mHal.state.inputElements[ct], mAttribNames, NULL, &attribCount, RS_SHADER_ATTR); + initAddUserElement(mRSProgram->mHal.state.inputElements[ct], mAttribNames, + NULL, &attribCount, RS_SHADER_ATTR); } for (uint32_t ct=0; ct < mRSProgram->mHal.state.constantsCount; ct++) { - initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(), mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI); + initAddUserElement(mRSProgram->mHal.state.constantTypes[ct]->getElement(), + mUniformNames, mUniformArraySizes, &uniformCount, RS_SHADER_UNI); } mTextureUniformIndexStart = uniformCount; - char buf[256]; for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) { - snprintf(buf, sizeof(buf), "UNI_Tex%i", ct); - mUniformNames[uniformCount].setTo(buf); + mUniformNames[uniformCount].setTo("UNI_"); + mUniformNames[uniformCount].append(textureNames[ct], textureNamesLength[ct]); mUniformArraySizes[uniformCount] = 1; uniformCount++; } - } String8 RsdShader::getGLSLInputString() const { @@ -135,22 +138,25 @@ void RsdShader::appendAttributes() { } } -void RsdShader::appendTextures() { - char buf[256]; - for (uint32_t ct=0; ct < mRSProgram->mHal.state.texturesCount; ct++) { +void RsdShader::createTexturesString(const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength) { + mShaderTextures.setTo(""); + for (uint32_t ct = 0; ct < mRSProgram->mHal.state.texturesCount; ct ++) { if (mRSProgram->mHal.state.textureTargets[ct] == RS_TEXTURE_2D) { Allocation *a = mRSProgram->mHal.state.textures[ct]; if (a && a->mHal.state.surfaceTextureID) { - snprintf(buf, sizeof(buf), "uniform samplerExternalOES UNI_Tex%i;\n", ct); + mShaderTextures.append("uniform samplerExternalOES UNI_"); } else { - snprintf(buf, sizeof(buf), "uniform sampler2D UNI_Tex%i;\n", ct); + mShaderTextures.append("uniform sampler2D UNI_"); } mTextureTargets[ct] = GL_TEXTURE_2D; } else { - snprintf(buf, sizeof(buf), "uniform samplerCube UNI_Tex%i;\n", ct); + mShaderTextures.append("uniform samplerCube UNI_"); mTextureTargets[ct] = GL_TEXTURE_CUBE_MAP; } - mShader.append(buf); + + mShaderTextures.append(textureNames[ct], textureNamesLength[ct]); + mShaderTextures.append(";\n"); } } @@ -161,7 +167,7 @@ bool RsdShader::createShader() { } appendUserConstants(); appendAttributes(); - appendTextures(); + mShader.append(mShaderTextures); mShader.append(mUserShader); @@ -418,7 +424,8 @@ void RsdShader::setupTextures(const Context *rsc, RsdShaderCache *sc) { DrvAllocation *drvTex = (DrvAllocation *)mRSProgram->mHal.state.textures[ct]->mHal.drv; if (drvTex->glTarget != GL_TEXTURE_2D && drvTex->glTarget != GL_TEXTURE_CUBE_MAP) { - ALOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", (uint)this, ct); + ALOGE("Attempting to bind unknown texture to shader id %u, texture unit %u", + (uint)this, ct); rsc->setError(RS_ERROR_BAD_SHADER, "Non-texture allocation bound to a shader"); } RSD_CALL_GL(glBindTexture, drvTex->glTarget, drvTex->textureID); diff --git a/libs/rs/driver/rsdShader.h b/libs/rs/driver/rsdShader.h index 3f0d6eae909e..e32145fad548 100644 --- a/libs/rs/driver/rsdShader.h +++ b/libs/rs/driver/rsdShader.h @@ -39,7 +39,9 @@ class RsdShader { public: RsdShader(const android::renderscript::Program *p, uint32_t type, - const char * shaderText, uint32_t shaderLength); + const char * shaderText, uint32_t shaderLength, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); virtual ~RsdShader(); bool createShader(); @@ -67,19 +69,27 @@ protected: // Applies to vertex and fragment shaders only void appendUserConstants(); - void setupUserConstants(const android::renderscript::Context *rsc, RsdShaderCache *sc, bool isFragment); - void initAddUserElement(const android::renderscript::Element *e, android::String8 *names, uint32_t *arrayLengths, uint32_t *count, const char *prefix); + void setupUserConstants(const android::renderscript::Context *rsc, + RsdShaderCache *sc, bool isFragment); + void initAddUserElement(const android::renderscript::Element *e, + android::String8 *names, uint32_t *arrayLengths, + uint32_t *count, const char *prefix); void setupTextures(const android::renderscript::Context *rsc, RsdShaderCache *sc); - void setupSampler(const android::renderscript::Context *rsc, const android::renderscript::Sampler *s, const android::renderscript::Allocation *tex); + void setupSampler(const android::renderscript::Context *rsc, + const android::renderscript::Sampler *s, + const android::renderscript::Allocation *tex); void appendAttributes(); void appendTextures(); + void createTexturesString(const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); void initAttribAndUniformArray(); mutable bool mDirty; android::String8 mShader; android::String8 mUserShader; + android::String8 mShaderTextures; uint32_t mShaderID; uint32_t mType; @@ -93,10 +103,14 @@ protected: int32_t mTextureUniformIndexStart; - void logUniform(const android::renderscript::Element *field, const float *fd, uint32_t arraySize ); - void setUniform(const android::renderscript::Context *rsc, const android::renderscript::Element *field, const float *fd, int32_t slot, uint32_t arraySize ); + void logUniform(const android::renderscript::Element *field, + const float *fd, uint32_t arraySize); + void setUniform(const android::renderscript::Context *rsc, + const android::renderscript::Element *field, + const float *fd, int32_t slot, uint32_t arraySize ); void initMemberVars(); - void init(); + void init(const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); }; #endif //ANDROID_RSD_SHADER_H diff --git a/libs/rs/RenderScript.h b/libs/rs/rs.h index 6d5426882bda..fbcaf4a2e413 100644 --- a/libs/rs/RenderScript.h +++ b/libs/rs/rs.h @@ -24,7 +24,7 @@ extern "C" { #endif -#include "RenderScriptDefines.h" +#include "rsDefines.h" // // A3D loading and object update code. diff --git a/libs/rs/rs.spec b/libs/rs/rs.spec index 491474ec2cd0..cf4a3918771c 100644 --- a/libs/rs/rs.spec +++ b/libs/rs/rs.spec @@ -85,167 +85,167 @@ AllocationIoReceive { ContextFinish { - sync - } + sync + } ContextBindRootScript { - param RsScript sampler - } + param RsScript sampler + } ContextBindProgramStore { - param RsProgramStore pgm - } + param RsProgramStore pgm + } ContextBindProgramFragment { - param RsProgramFragment pgm - } + param RsProgramFragment pgm + } ContextBindProgramVertex { - param RsProgramVertex pgm - } + param RsProgramVertex pgm + } ContextBindProgramRaster { - param RsProgramRaster pgm - } + param RsProgramRaster pgm + } ContextBindFont { - param RsFont pgm - } + param RsFont pgm + } ContextPause { - } + } ContextResume { - } + } ContextSetSurface { - param uint32_t width - param uint32_t height - param RsNativeWindow sur + param uint32_t width + param uint32_t height + param RsNativeWindow sur sync - } + } ContextDump { - param int32_t bits + param int32_t bits } ContextSetPriority { - param int32_t priority - } + param int32_t priority + } ContextDestroyWorker { sync } AssignName { - param RsObjectBase obj - param const char *name - } + param RsObjectBase obj + param const char *name + } ObjDestroy { - param RsAsyncVoidPtr objPtr - } + param RsAsyncVoidPtr objPtr + } ElementCreate { direct - param RsDataType mType - param RsDataKind mKind - param bool mNormalized - param uint32_t mVectorSize - ret RsElement - } + param RsDataType mType + param RsDataKind mKind + param bool mNormalized + param uint32_t mVectorSize + ret RsElement + } ElementCreate2 { direct - param const RsElement * elements - param const char ** names - param const uint32_t * arraySize - ret RsElement - } + param const RsElement * elements + param const char ** names + param const uint32_t * arraySize + ret RsElement + } AllocationCopyToBitmap { - param RsAllocation alloc - param void * data - } + param RsAllocation alloc + param void * data + } Allocation1DData { - param RsAllocation va - param uint32_t xoff - param uint32_t lod - param uint32_t count - param const void *data - } + param RsAllocation va + param uint32_t xoff + param uint32_t lod + param uint32_t count + param const void *data + } Allocation1DElementData { - param RsAllocation va - param uint32_t x - param uint32_t lod - param const void *data - param size_t comp_offset - } + param RsAllocation va + param uint32_t x + param uint32_t lod + param const void *data + param size_t comp_offset + } Allocation2DData { - param RsAllocation va - param uint32_t xoff - param uint32_t yoff - param uint32_t lod - param RsAllocationCubemapFace face - param uint32_t w - param uint32_t h - param const void *data - } + param RsAllocation va + param uint32_t xoff + param uint32_t yoff + param uint32_t lod + param RsAllocationCubemapFace face + param uint32_t w + param uint32_t h + param const void *data + } Allocation2DElementData { - param RsAllocation va - param uint32_t x - param uint32_t y - param uint32_t lod - param RsAllocationCubemapFace face - param const void *data - param size_t element_offset - } + param RsAllocation va + param uint32_t x + param uint32_t y + param uint32_t lod + param RsAllocationCubemapFace face + param const void *data + param size_t element_offset + } AllocationGenerateMipmaps { - param RsAllocation va + param RsAllocation va } AllocationRead { - param RsAllocation va - param void * data - } + param RsAllocation va + param void * data + } AllocationSyncAll { - param RsAllocation va - param RsAllocationUsageType src + param RsAllocation va + param RsAllocationUsageType src } AllocationResize1D { - param RsAllocation va - param uint32_t dimX - } + param RsAllocation va + param uint32_t dimX + } AllocationResize2D { - param RsAllocation va - param uint32_t dimX - param uint32_t dimY - } + param RsAllocation va + param uint32_t dimX + param uint32_t dimY + } AllocationCopy2DRange { - param RsAllocation dest - param uint32_t destXoff - param uint32_t destYoff - param uint32_t destMip - param uint32_t destFace - param uint32_t width - param uint32_t height - param RsAllocation src - param uint32_t srcXoff - param uint32_t srcYoff - param uint32_t srcMip - param uint32_t srcFace - } + param RsAllocation dest + param uint32_t destXoff + param uint32_t destYoff + param uint32_t destMip + param uint32_t destFace + param uint32_t width + param uint32_t height + param RsAllocation src + param uint32_t srcXoff + param uint32_t srcYoff + param uint32_t srcMip + param uint32_t srcFace + } SamplerCreate { direct @@ -259,26 +259,26 @@ SamplerCreate { } ScriptBindAllocation { - param RsScript vtm - param RsAllocation va - param uint32_t slot - } + param RsScript vtm + param RsAllocation va + param uint32_t slot + } ScriptSetTimeZone { - param RsScript s - param const char * timeZone - } + param RsScript s + param const char * timeZone + } ScriptInvoke { - param RsScript s - param uint32_t slot - } + param RsScript s + param uint32_t slot + } ScriptInvokeV { - param RsScript s - param uint32_t slot - param const void * data - } + param RsScript s + param uint32_t slot + param const void * data + } ScriptForEach { param RsScript s @@ -289,125 +289,127 @@ ScriptForEach { } ScriptSetVarI { - param RsScript s - param uint32_t slot - param int value - } + param RsScript s + param uint32_t slot + param int value + } ScriptSetVarObj { - param RsScript s - param uint32_t slot - param RsObjectBase value - } + param RsScript s + param uint32_t slot + param RsObjectBase value + } ScriptSetVarJ { - param RsScript s - param uint32_t slot - param int64_t value - } + param RsScript s + param uint32_t slot + param int64_t value + } ScriptSetVarF { - param RsScript s - param uint32_t slot - param float value - } + param RsScript s + param uint32_t slot + param float value + } ScriptSetVarD { - param RsScript s - param uint32_t slot - param double value - } + param RsScript s + param uint32_t slot + param double value + } ScriptSetVarV { - param RsScript s - param uint32_t slot - param const void * data - } + param RsScript s + param uint32_t slot + param const void * data + } ScriptCCreate { param const char * resName param const char * cacheDir - param const char * text - ret RsScript - } + param const char * text + ret RsScript + } ProgramStoreCreate { - direct - param bool colorMaskR - param bool colorMaskG - param bool colorMaskB - param bool colorMaskA + direct + param bool colorMaskR + param bool colorMaskG + param bool colorMaskB + param bool colorMaskA param bool depthMask param bool ditherEnable - param RsBlendSrcFunc srcFunc - param RsBlendDstFunc destFunc + param RsBlendSrcFunc srcFunc + param RsBlendDstFunc destFunc param RsDepthFunc depthFunc - ret RsProgramStore - } + ret RsProgramStore + } ProgramRasterCreate { - direct - param bool pointSprite - param RsCullMode cull - ret RsProgramRaster + direct + param bool pointSprite + param RsCullMode cull + ret RsProgramRaster } ProgramBindConstants { - param RsProgram vp - param uint32_t slot - param RsAllocation constants - } + param RsProgram vp + param uint32_t slot + param RsAllocation constants + } ProgramBindTexture { - param RsProgramFragment pf - param uint32_t slot - param RsAllocation a - } + param RsProgramFragment pf + param uint32_t slot + param RsAllocation a + } ProgramBindSampler { - param RsProgramFragment pf - param uint32_t slot - param RsSampler s - } + param RsProgramFragment pf + param uint32_t slot + param RsSampler s + } ProgramFragmentCreate { - direct - param const char * shaderText - param const uint32_t * params - ret RsProgramFragment - } + direct + param const char * shaderText + param const char ** textureNames + param const uint32_t * params + ret RsProgramFragment + } ProgramVertexCreate { - direct - param const char * shaderText - param const uint32_t * params - ret RsProgramVertex - } + direct + param const char * shaderText + param const char ** textureNames + param const uint32_t * params + ret RsProgramVertex + } FontCreateFromFile { - param const char *name - param float fontSize - param uint32_t dpi - ret RsFont - } + param const char *name + param float fontSize + param uint32_t dpi + ret RsFont + } FontCreateFromMemory { - param const char *name - param float fontSize - param uint32_t dpi - param const void *data - ret RsFont - } + param const char *name + param float fontSize + param uint32_t dpi + param const void *data + ret RsFont + } MeshCreate { - param RsAllocation *vtx - param RsAllocation *idx - param uint32_t *primType - ret RsMesh - } + param RsAllocation *vtx + param RsAllocation *idx + param uint32_t *primType + ret RsMesh + } PathCreate { param RsPathPrimitive pp @@ -417,4 +419,3 @@ PathCreate { param float quality ret RsPath } - diff --git a/libs/rs/rsContext.cpp b/libs/rs/rsContext.cpp index adaefc6017bf..95ac76e84fd9 100644 --- a/libs/rs/rsContext.cpp +++ b/libs/rs/rsContext.cpp @@ -263,6 +263,10 @@ void * Context::threadProc(void *vrsc) { rsc->timerSet(RS_TIMER_IDLE); #ifndef ANDROID_RS_SERIALIZE + if (!rsc->mRootScript.get() || !rsc->mHasSurface || rsc->mPaused) { + targetRate = 0; + } + if (vsyncRate != targetRate) { displayEvent.setVsyncRate(targetRate); vsyncRate = targetRate; diff --git a/libs/rs/RenderScriptDefines.h b/libs/rs/rsDefines.h index 990ef266f7ec..990ef266f7ec 100644 --- a/libs/rs/RenderScriptDefines.h +++ b/libs/rs/rsDefines.h diff --git a/libs/rs/RenderScriptEnv.h b/libs/rs/rsEnv.h index b82eaf1d6c59..b82eaf1d6c59 100644 --- a/libs/rs/RenderScriptEnv.h +++ b/libs/rs/rsEnv.h diff --git a/libs/rs/rsFileA3D.h b/libs/rs/rsFileA3D.h index 056b5af4c1d5..baf81de5083a 100644 --- a/libs/rs/rsFileA3D.h +++ b/libs/rs/rsFileA3D.h @@ -17,11 +17,11 @@ #ifndef ANDROID_RS_FILE_A3D_H #define ANDROID_RS_FILE_A3D_H -#include "RenderScript.h" +#include "rs.h" #include "rsMesh.h" +#include <androidfw/Asset.h> #include <utils/String8.h> -#include <utils/Asset.h> #include "rsStream.h" #include <stdio.h> diff --git a/libs/rs/rsFont.cpp b/libs/rs/rsFont.cpp index 4f21b3b0140d..c4276cfe8261 100644 --- a/libs/rs/rsFont.cpp +++ b/libs/rs/rsFont.cpp @@ -490,8 +490,14 @@ void FontState::initRenderState() { shaderString.append(" gl_FragColor = col;\n"); shaderString.append("}\n"); - ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 4); - ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, RS_KIND_USER, false, 1); + const char *textureNames[] = { "Tex0" }; + const size_t textureNamesLengths[] = { 4 }; + size_t numTextures = sizeof(textureNamesLengths)/sizeof(*textureNamesLengths); + + ObjectBaseRef<const Element> colorElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, + RS_KIND_USER, false, 4); + ObjectBaseRef<const Element> gammaElem = Element::createRef(mRSC, RS_TYPE_FLOAT_32, + RS_KIND_USER, false, 1); Element::Builder builder; builder.add(colorElem.get(), "Color", 1); builder.add(gammaElem.get(), "Gamma", 1); @@ -506,14 +512,17 @@ void FontState::initRenderState() { tmp[3] = RS_TEXTURE_2D; mFontShaderFConstant.set(Allocation::createAllocation(mRSC, inputType.get(), - RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS)); - ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), - shaderString.length(), tmp, 4); + RS_ALLOCATION_USAGE_SCRIPT | + RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS)); + ProgramFragment *pf = new ProgramFragment(mRSC, shaderString.string(), shaderString.length(), + textureNames, numTextures, textureNamesLengths, + tmp, 4); mFontShaderF.set(pf); mFontShaderF->bindAllocation(mRSC, mFontShaderFConstant.get(), 0); mFontSampler.set(Sampler::getSampler(mRSC, RS_SAMPLER_NEAREST, RS_SAMPLER_NEAREST, - RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP).get()); + RS_SAMPLER_CLAMP, RS_SAMPLER_CLAMP, + RS_SAMPLER_CLAMP).get()); mFontShaderF->bindSampler(mRSC, 0, mFontSampler.get()); mFontProgramStore.set(ProgramStore::getProgramStore(mRSC, true, true, true, true, @@ -525,10 +534,12 @@ void FontState::initRenderState() { } void FontState::initTextTexture() { - ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8, RS_KIND_PIXEL_A, true, 1); + ObjectBaseRef<const Element> alphaElem = Element::createRef(mRSC, RS_TYPE_UNSIGNED_8, + RS_KIND_PIXEL_A, true, 1); // We will allocate a texture to initially hold 32 character bitmaps - ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(), 1024, 256, 0, false, false); + ObjectBaseRef<Type> texType = Type::getTypeRef(mRSC, alphaElem.get(), + 1024, 256, 0, false, false); Allocation *cacheAlloc = Allocation::createAllocation(mRSC, texType.get(), RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_TEXTURE); diff --git a/libs/rs/rsFont.h b/libs/rs/rsFont.h index 4ca794dcee1c..88c479534096 100644 --- a/libs/rs/rsFont.h +++ b/libs/rs/rsFont.h @@ -17,7 +17,7 @@ #ifndef ANDROID_RS_FONT_H #define ANDROID_RS_FONT_H -#include "RenderScript.h" +#include "rs.h" #include "rsStream.h" #include <utils/String8.h> #include <utils/Vector.h> diff --git a/libs/rs/rsMesh.h b/libs/rs/rsMesh.h index 166b5d3e27c2..8eea4279e3f7 100644 --- a/libs/rs/rsMesh.h +++ b/libs/rs/rsMesh.h @@ -18,7 +18,7 @@ #define ANDROID_RS_MESH_H -#include "RenderScript.h" +#include "rs.h" // --------------------------------------------------------------------------- namespace android { diff --git a/libs/rs/rsPath.h b/libs/rs/rsPath.h index dac795ef44b3..7c055037d5ab 100644 --- a/libs/rs/rsPath.h +++ b/libs/rs/rsPath.h @@ -18,7 +18,7 @@ #define ANDROID_RS_PATH_H -#include "RenderScript.h" +#include "rs.h" // --------------------------------------------------------------------------- namespace android { diff --git a/libs/rs/rsProgram.cpp b/libs/rs/rsProgram.cpp index 8061515e24fa..7114f29d0509 100644 --- a/libs/rs/rsProgram.cpp +++ b/libs/rs/rsProgram.cpp @@ -20,8 +20,8 @@ using namespace android; using namespace android::renderscript; -Program::Program(Context *rsc, const char * shaderText, uint32_t shaderLength, - const uint32_t * params, uint32_t paramLength) +Program::Program(Context *rsc, const char * shaderText, size_t shaderLength, + const uint32_t * params, size_t paramLength) : ProgramBase(rsc) { initMemberVars(); diff --git a/libs/rs/rsProgram.h b/libs/rs/rsProgram.h index 06fc3eca71ab..d03293066803 100644 --- a/libs/rs/rsProgram.h +++ b/libs/rs/rsProgram.h @@ -58,8 +58,8 @@ public: }; Hal mHal; - Program(Context *, const char * shaderText, uint32_t shaderLength, - const uint32_t * params, uint32_t paramLength); + Program(Context *, const char * shaderText, size_t shaderLength, + const uint32_t * params, size_t paramLength); virtual ~Program(); virtual bool freeChildren(); diff --git a/libs/rs/rsProgramFragment.cpp b/libs/rs/rsProgramFragment.cpp index 4e73ca63e90b..bebde1e0cb53 100644 --- a/libs/rs/rsProgramFragment.cpp +++ b/libs/rs/rsProgramFragment.cpp @@ -20,16 +20,18 @@ using namespace android; using namespace android::renderscript; -ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText, - uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength) +ProgramFragment::ProgramFragment(Context *rsc, const char * shaderText, size_t shaderLength, + const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength, + + const uint32_t * params, size_t paramLength) : Program(rsc, shaderText, shaderLength, params, paramLength) { mConstantColor[0] = 1.f; mConstantColor[1] = 1.f; mConstantColor[2] = 1.f; mConstantColor[3] = 1.f; - mRSC->mHal.funcs.fragment.init(mRSC, this, mUserShader.string(), mUserShader.length()); + mRSC->mHal.funcs.fragment.init(mRSC, this, mUserShader.string(), mUserShader.length(), + textureNames, textureNamesCount, textureNamesLength); } ProgramFragment::~ProgramFragment() { @@ -110,8 +112,8 @@ void ProgramFragmentState::init(Context *rsc) { Allocation *constAlloc = Allocation::createAllocation(rsc, inputType.get(), RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS); - ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(), - shaderString.length(), tmp, 2); + ProgramFragment *pf = new ProgramFragment(rsc, shaderString.string(), shaderString.length(), + NULL, 0, NULL, tmp, 2); pf->bindAllocation(rsc, constAlloc, 0); pf->setConstantColor(rsc, 1.0f, 1.0f, 1.0f, 1.0f); @@ -127,9 +129,14 @@ namespace android { namespace renderscript { RsProgramFragment rsi_ProgramFragmentCreate(Context *rsc, const char * shaderText, - size_t shaderLength, const uint32_t * params, - size_t paramLength) { - ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, params, paramLength); + size_t shaderLength, + const char** textureNames, + size_t textureNamesCount, + const size_t *textureNamesLength, + const uint32_t * params, size_t paramLength) { + ProgramFragment *pf = new ProgramFragment(rsc, shaderText, shaderLength, + textureNames, textureNamesCount, textureNamesLength, + params, paramLength); pf->incUserRef(); //ALOGE("rsi_ProgramFragmentCreate %p", pf); return pf; diff --git a/libs/rs/rsProgramFragment.h b/libs/rs/rsProgramFragment.h index d6e20cd192b9..4eb28e7db171 100644 --- a/libs/rs/rsProgramFragment.h +++ b/libs/rs/rsProgramFragment.h @@ -27,9 +27,9 @@ class ProgramFragmentState; class ProgramFragment : public Program { public: - ProgramFragment(Context *rsc, const char * shaderText, - uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength); + ProgramFragment(Context *rsc, const char * shaderText, size_t shaderLength, + const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength, + const uint32_t * params, size_t paramLength); virtual ~ProgramFragment(); virtual void setup(Context *, ProgramFragmentState *); diff --git a/libs/rs/rsProgramVertex.cpp b/libs/rs/rsProgramVertex.cpp index 871caacd6f66..c8a53eab2012 100644 --- a/libs/rs/rsProgramVertex.cpp +++ b/libs/rs/rsProgramVertex.cpp @@ -21,11 +21,13 @@ using namespace android; using namespace android::renderscript; -ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText, - uint32_t shaderLength, const uint32_t * params, - uint32_t paramLength) +ProgramVertex::ProgramVertex(Context *rsc, const char * shaderText, size_t shaderLength, + const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength, + + const uint32_t * params, size_t paramLength) : Program(rsc, shaderText, shaderLength, params, paramLength) { - mRSC->mHal.funcs.vertex.init(mRSC, this, mUserShader.string(), mUserShader.length()); + mRSC->mHal.funcs.vertex.init(mRSC, this, mUserShader.string(), mUserShader.length(), + textureNames, textureNamesCount, textureNamesLength); } ProgramVertex::~ProgramVertex() { @@ -191,8 +193,8 @@ void ProgramVertexState::init(Context *rsc) { tmp[2] = RS_PROGRAM_PARAM_INPUT; tmp[3] = (uint32_t)attrElem.get(); - ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(), - shaderString.length(), tmp, 4); + ProgramVertex *pv = new ProgramVertex(rsc, shaderString.string(), shaderString.length(), + NULL, 0, NULL, tmp, 4); Allocation *alloc = Allocation::createAllocation(rsc, inputType.get(), RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_GRAPHICS_CONSTANTS); pv->bindAllocation(rsc, alloc, 0); @@ -229,10 +231,13 @@ void ProgramVertexState::deinit(Context *rsc) { namespace android { namespace renderscript { -RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText, - size_t shaderLength, const uint32_t * params, - size_t paramLength) { - ProgramVertex *pv = new ProgramVertex(rsc, shaderText, shaderLength, params, paramLength); +RsProgramVertex rsi_ProgramVertexCreate(Context *rsc, const char * shaderText, size_t shaderLength, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength, + const uint32_t * params, size_t paramLength) { + ProgramVertex *pv = new ProgramVertex(rsc, shaderText, shaderLength, + textureNames, textureNamesCount, textureNamesLength, + params, paramLength); pv->incUserRef(); return pv; } diff --git a/libs/rs/rsProgramVertex.h b/libs/rs/rsProgramVertex.h index 5cfdd8be355d..67c2a88840f6 100644 --- a/libs/rs/rsProgramVertex.h +++ b/libs/rs/rsProgramVertex.h @@ -27,8 +27,9 @@ class ProgramVertexState; class ProgramVertex : public Program { public: - ProgramVertex(Context *,const char * shaderText, uint32_t shaderLength, - const uint32_t * params, uint32_t paramLength); + ProgramVertex(Context *,const char * shaderText, size_t shaderLength, + const char** textureNames, size_t textureNamesCount, const size_t *textureNamesLength, + const uint32_t * params, size_t paramLength); virtual ~ProgramVertex(); virtual void setup(Context *rsc, ProgramVertexState *state); diff --git a/libs/rs/rsSampler.h b/libs/rs/rsSampler.h index 654cd9ce1a06..013e4ca32370 100644 --- a/libs/rs/rsSampler.h +++ b/libs/rs/rsSampler.h @@ -18,7 +18,7 @@ #define ANDROID_RS_SAMPLER_H #include "rsAllocation.h" -#include "RenderScript.h" +#include "rs.h" // --------------------------------------------------------------------------- namespace android { diff --git a/libs/rs/rsScriptC.h b/libs/rs/rsScriptC.h index c89d9b13eeec..92e1f4ff3ec0 100644 --- a/libs/rs/rsScriptC.h +++ b/libs/rs/rsScriptC.h @@ -19,7 +19,7 @@ #include "rsScript.h" -#include "RenderScriptEnv.h" +#include "rsEnv.h" #ifndef ANDROID_RS_SERIALIZE #include "bcinfo/BitcodeTranslator.h" diff --git a/libs/rs/rsThreadIO.cpp b/libs/rs/rsThreadIO.cpp index 4f3057317ab9..7182f53a9517 100644 --- a/libs/rs/rsThreadIO.cpp +++ b/libs/rs/rsThreadIO.cpp @@ -31,6 +31,8 @@ using namespace android::renderscript; ThreadIO::ThreadIO() { mRunning = true; + mPureFifo = false; + mMaxInlineSize = 1024; } ThreadIO::~ThreadIO() { @@ -65,6 +67,16 @@ void ThreadIO::clientShutdown() { mToClient.shutdown(); } +void ThreadIO::coreWrite(const void *data, size_t len) { + //ALOGV("core write %p %i", data, (int)len); + mToCore.writeAsync(data, len, true); +} + +void ThreadIO::coreRead(void *data, size_t len) { + //ALOGV("core read %p %i", data, (int)len); + mToCore.read(data, len); +} + void ThreadIO::coreSetReturn(const void *data, size_t dataLen) { uint32_t buf; if (data == NULL) { @@ -91,6 +103,7 @@ void ThreadIO::setTimeoutCallback(void (*cb)(void *), void *dat, uint64_t timeou bool ThreadIO::playCoreCommands(Context *con, int waitFd) { bool ret = false; + const bool isLocal = !isPureFifo(); uint8_t buf[2 * 1024]; const CoreCmdHeader *cmd = (const CoreCmdHeader *)&buf[0]; @@ -120,14 +133,19 @@ bool ThreadIO::playCoreCommands(Context *con, int waitFd) { } if (p[0].revents) { - size_t r = mToCore.read(&buf[0], sizeof(CoreCmdHeader)); - mToCore.read(&buf[sizeof(CoreCmdHeader)], cmd->bytes); - - if (r != sizeof(CoreCmdHeader)) { - // exception or timeout occurred. - break; + size_t r = 0; + if (isLocal) { + r = mToCore.read(&buf[0], sizeof(CoreCmdHeader)); + mToCore.read(&buf[sizeof(CoreCmdHeader)], cmd->bytes); + if (r != sizeof(CoreCmdHeader)) { + // exception or timeout occurred. + break; + } + } else { + r = mToCore.read((void *)&cmd->cmdID, sizeof(cmd->cmdID)); } + ret = true; if (con->props.mLogTimes) { con->timerSet(Context::RS_TIMER_INTERNAL); @@ -138,7 +156,12 @@ bool ThreadIO::playCoreCommands(Context *con, int waitFd) { rsAssert(cmd->cmdID < (sizeof(gPlaybackFuncs) / sizeof(void *))); ALOGE("playCoreCommands error con %p, cmd %i", con, cmd->cmdID); } - gPlaybackFuncs[cmd->cmdID](con, data, cmd->bytes); + + if (isLocal) { + gPlaybackFuncs[cmd->cmdID](con, data, cmd->bytes); + } else { + gPlaybackRemoteFuncs[cmd->cmdID](con, this); + } if (con->props.mLogTimes) { con->timerSet(Context::RS_TIMER_IDLE); diff --git a/libs/rs/rsThreadIO.h b/libs/rs/rsThreadIO.h index 62e3e33efb60..cb7d4abca5c5 100644 --- a/libs/rs/rsThreadIO.h +++ b/libs/rs/rsThreadIO.h @@ -34,6 +34,13 @@ public: void init(); void shutdown(); + size_t getMaxInlineSize() { + return mMaxInlineSize; + } + bool isPureFifo() { + return mPureFifo; + } + // Plays back commands from the client. // Returns true if any commands were processed. bool playCoreCommands(Context *con, int waitFd); @@ -42,8 +49,16 @@ public: void * coreHeader(uint32_t, size_t dataLen); void coreCommit(); + void coreSetReturn(const void *data, size_t dataLen); void coreGetReturn(void *data, size_t dataLen); + void coreWrite(const void *data, size_t len); + void coreRead(void *data, size_t len); + + void asyncSetReturn(const void *data, size_t dataLen); + void asyncGetReturn(void *data, size_t dataLen); + void asyncWrite(const void *data, size_t len); + void asyncRead(void *data, size_t len); RsMessageToClientType getClientHeader(size_t *receiveLen, uint32_t *usrID); @@ -65,6 +80,8 @@ protected: ClientCmdHeader mLastClientHeader; bool mRunning; + bool mPureFifo; + size_t mMaxInlineSize; FifoSocket mToClient; FifoSocket mToCore; diff --git a/libs/rs/rsUtils.h b/libs/rs/rsUtils.h index db6f59204d12..a9a992a773ec 100644 --- a/libs/rs/rsUtils.h +++ b/libs/rs/rsUtils.h @@ -34,7 +34,7 @@ #include <math.h> -#include "RenderScript.h" +#include "rs.h" namespace android { namespace renderscript { diff --git a/libs/rs/rs_hal.h b/libs/rs/rs_hal.h index 12ace43f9b19..e4bf17f3be2a 100644 --- a/libs/rs/rs_hal.h +++ b/libs/rs/rs_hal.h @@ -17,7 +17,7 @@ #ifndef RS_HAL_H #define RS_HAL_H -#include <RenderScriptDefines.h> +#include <rsDefines.h> struct ANativeWindow; @@ -178,14 +178,18 @@ typedef struct { struct { bool (*init)(const Context *rsc, const ProgramVertex *pv, - const char* shader, size_t shaderLen); + const char* shader, size_t shaderLen, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); void (*setActive)(const Context *rsc, const ProgramVertex *pv); void (*destroy)(const Context *rsc, const ProgramVertex *pv); } vertex; struct { bool (*init)(const Context *rsc, const ProgramFragment *pf, - const char* shader, size_t shaderLen); + const char* shader, size_t shaderLen, + const char** textureNames, size_t textureNamesCount, + const size_t *textureNamesLength); void (*setActive)(const Context *rsc, const ProgramFragment *pf); void (*destroy)(const Context *rsc, const ProgramFragment *pf); } fragment; diff --git a/libs/rs/rsg_generator.c b/libs/rs/rsg_generator.c index 385c8b58935b..99c305e0702c 100644 --- a/libs/rs/rsg_generator.c +++ b/libs/rs/rsg_generator.c @@ -238,7 +238,7 @@ void printApiCpp(FILE *f) { //fprintf(f, " ALOGE(\"add command %s\\n\");\n", api->name); if (hasInlineDataPointers(api)) { fprintf(f, " RS_CMD_%s *cmd = NULL;\n", api->name); - fprintf(f, " if (dataSize < 1024) {;\n"); + fprintf(f, " if (dataSize < io->getMaxInlineSize()) {;\n"); fprintf(f, " cmd = static_cast<RS_CMD_%s *>(io->coreHeader(RS_CMD_ID_%s, dataSize + size));\n", api->name, api->name); fprintf(f, " } else {\n"); fprintf(f, " cmd = static_cast<RS_CMD_%s *>(io->coreHeader(RS_CMD_ID_%s, size));\n", api->name, api->name); @@ -252,7 +252,7 @@ void printApiCpp(FILE *f) { const VarType *vt = &api->params[ct2]; needFlush += vt->ptrLevel; if (vt->ptrLevel && hasInlineDataPointers(api)) { - fprintf(f, " if (dataSize < 1024) {\n"); + fprintf(f, " if (dataSize < io->getMaxInlineSize()) {\n"); fprintf(f, " memcpy(payload, %s, %s_length);\n", vt->name, vt->name); fprintf(f, " cmd->%s = (", vt->name); printVarType(f, vt); @@ -272,7 +272,7 @@ void printApiCpp(FILE *f) { fprintf(f, " io->coreCommit();\n"); if (hasInlineDataPointers(api)) { - fprintf(f, " if (dataSize >= 1024) {\n"); + fprintf(f, " if (dataSize >= io->getMaxInlineSize()) {\n"); fprintf(f, " io->coreGetReturn(NULL, 0);\n"); fprintf(f, " }\n"); } else if (api->ret.typeName[0]) { @@ -288,81 +288,71 @@ void printApiCpp(FILE *f) { fprintf(f, "};\n\n"); + // Generate a remote sender function + const char * str = "core"; + if (api->direct) { + str = "async"; + } + fprintf(f, "static "); printFuncDecl(f, api, "RF_", 0, 0); fprintf(f, "\n{\n"); - fprintf(f, " Fifo *f = NULL;\n"); - fprintf(f, " RS_CMD_%s cmd;\n", api->name); - fprintf(f, " const uint32_t cmdSize = sizeof(cmd);\n"); + fprintf(f, " ThreadIO *io = &((Context *)rsc)->mIO;\n"); fprintf(f, " const uint32_t cmdID = RS_CMD_ID_%s;\n", api->name); - fprintf(f, " f->writeAsync(&cmdID, sizeof(cmdID));\n"); - fprintf(f, " intptr_t offset = cmdSize;\n"); - fprintf(f, " uint32_t dataSize = 0;\n"); + fprintf(f, " io->%sWrite(&cmdID, sizeof(cmdID));\n\n", str); + for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; - if (vt->isConst && vt->ptrLevel) { - switch(vt->ptrLevel) { - case 1: - fprintf(f, " dataSize += %s_length;\n", vt->name); - break; - case 2: - fprintf(f, " for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name); - fprintf(f, " dataSize += %s_length[ct];\n", vt->name); - fprintf(f, " }\n"); - break; - default: - printf("pointer level not handled!!"); - } + if (vt->ptrLevel == 0) { + fprintf(f, " io->%sWrite(& %s, sizeof(%s));\n", str, vt->name, vt->name); } } fprintf(f, "\n"); for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; - switch(vt->ptrLevel) { - case 0: - fprintf(f, " cmd.%s = %s;\n", vt->name, vt->name); - break; - case 1: - fprintf(f, " cmd.%s = (", vt->name); - printVarType(f, vt); - fprintf(f, ")offset;\n"); - fprintf(f, " offset += %s_length;\n", vt->name); - break; - case 2: - fprintf(f, " cmd.%s = (", vt->name); - printVarType(f, vt); - fprintf(f, ")offset;\n"); + if ((vt->ptrLevel == 1) && (vt->isConst)) { + fprintf(f, " io->%sWrite(%s, %s_length);\n", str, vt->name, vt->name); + } + } + fprintf(f, "\n"); + + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + if ((vt->ptrLevel == 2) && (vt->isConst)) { fprintf(f, " for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name); - fprintf(f, " offset += %s_length[ct];\n", vt->name); + fprintf(f, " io->%sWrite(%s[ct], %s_length[ct]);\n", str, vt->name, vt->name); fprintf(f, " }\n"); - break; - default: - fprintf(stderr, "pointer level not handled!!"); } } fprintf(f, "\n"); - fprintf(f, " f->writeAsync(&cmd, cmdSize);\n"); for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; - if (vt->ptrLevel == 1) { - fprintf(f, " f->writeAsync(%s, %s_length);\n", vt->name, vt->name); + if ((vt->ptrLevel == 1) && (!vt->isConst)) { + fprintf(f, " io->%sGetReturn(%s, %s_length);\n", str, vt->name, vt->name); } - if (vt->ptrLevel == 2) { + } + fprintf(f, "\n"); + + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + if ((vt->ptrLevel == 2) && (!vt->isConst)) { fprintf(f, " for (size_t ct = 0; ct < (%s_length_length / sizeof(%s_length)); ct++) {\n", vt->name, vt->name); - fprintf(f, " f->writeAsync(%s, %s_length[ct]);\n", vt->name, vt->name); - fprintf(f, " offset += %s_length[ct];\n", vt->name); + fprintf(f, " io->%sGetReturn(%s[ct], %s_length[ct]);\n", str, vt->name, vt->name); fprintf(f, " }\n"); } } + fprintf(f, "\n"); if (api->ret.typeName[0]) { fprintf(f, " "); printVarType(f, &api->ret); fprintf(f, " retValue;\n"); - fprintf(f, " f->writeWaitReturn(&retValue, sizeof(retValue));\n"); + fprintf(f, " io->%sGetReturn(&retValue, sizeof(retValue));\n", str); fprintf(f, " return retValue;\n"); + } else /*if (api->sync)*/ { + fprintf(f, " io->%sGetReturn(NULL, 0);\n", str); } fprintf(f, "}\n\n"); } @@ -418,7 +408,6 @@ void printPlaybackCpp(FILE *f) { fprintf(f, "#include \"rsDevice.h\"\n"); fprintf(f, "#include \"rsContext.h\"\n"); fprintf(f, "#include \"rsThreadIO.h\"\n"); - //fprintf(f, "#include \"rsgApiStructs.h\"\n"); fprintf(f, "#include \"rsgApiFuncDecl.h\"\n"); fprintf(f, "\n"); fprintf(f, "namespace android {\n"); @@ -434,8 +423,6 @@ void printPlaybackCpp(FILE *f) { } fprintf(f, "void rsp_%s(Context *con, const void *vp, size_t cmdSizeBytes) {\n", api->name); - - //fprintf(f, " ALOGE(\"play command %s\\n\");\n", api->name); fprintf(f, " const RS_CMD_%s *cmd = static_cast<const RS_CMD_%s *>(vp);\n", api->name, api->name); if (hasInlineDataPointers(api)) { @@ -488,30 +475,40 @@ void printPlaybackCpp(FILE *f) { const ApiEntry * api = &apis[ct]; int needFlush = 0; - fprintf(f, "void rspr_%s(Context *con, Fifo *f, uint8_t *scratch, size_t scratchSize) {\n", api->name); - - //fprintf(f, " ALOGE(\"play command %s\\n\");\n", api->name); + fprintf(f, "void rspr_%s(Context *con, ThreadIO *io) {\n", api->name); fprintf(f, " RS_CMD_%s cmd;\n", api->name); - fprintf(f, " f->read(&cmd, sizeof(cmd));\n"); for (ct2=0; ct2 < api->paramCount; ct2++) { const VarType *vt = &api->params[ct2]; - needFlush += vt->ptrLevel; + if (vt->ptrLevel == 0) { + fprintf(f, " io->coreRead(&cmd.%s, sizeof(cmd.%s));\n", vt->name, vt->name); + } + } + fprintf(f, "\n"); + + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; if (vt->ptrLevel == 1) { fprintf(f, " cmd.%s = (", vt->name); printVarType(f, vt); - fprintf(f, ")scratch;\n"); - fprintf(f, " f->read(scratch, cmd.%s_length);\n", vt->name); - fprintf(f, " scratch += cmd.%s_length;\n", vt->name); + fprintf(f, ")malloc(cmd.%s_length);\n", vt->name); + + if (vt->isConst) { + fprintf(f, " if (cmd.%s_length) io->coreRead((void *)cmd.%s, cmd.%s_length);\n", vt->name, vt->name, vt->name); + } } + } + fprintf(f, "\n"); + + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; if (vt->ptrLevel == 2) { - fprintf(f, " size_t sum_%s = 0;\n", vt->name); fprintf(f, " for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name); - fprintf(f, " ((size_t *)scratch)[ct] = cmd.%s_length[ct];\n", vt->name); - fprintf(f, " sum_%s += cmd.%s_length[ct];\n", vt->name, vt->name); + fprintf(f, " cmd.%s = (", vt->name); + printVarType(f, vt); + fprintf(f, ")malloc(cmd.%s_length[ct]);\n", vt->name); + fprintf(f, " io->coreRead(& cmd.%s, cmd.%s_length[ct]);\n", vt->name, vt->name); fprintf(f, " }\n"); - fprintf(f, " f->read(scratch, sum_%s);\n", vt->name); - fprintf(f, " scratch += sum_%s;\n", vt->name); } } fprintf(f, "\n"); @@ -535,10 +532,42 @@ void printPlaybackCpp(FILE *f) { } fprintf(f, ");\n"); + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + if ((vt->ptrLevel == 1) && (!vt->isConst)) { + fprintf(f, " io->coreSetReturn((void *)cmd.%s, cmd.%s_length);\n", vt->name, vt->name); + } + } + + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + if ((vt->ptrLevel == 2) && (!vt->isConst)) { + fprintf(f, " for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name); + fprintf(f, " io->coreSetReturn((void *)cmd.%s[ct], cmd.%s_length[ct]);\n", vt->name, vt->name); + fprintf(f, " }\n"); + } + } + fprintf(f, "\n"); + if (api->ret.typeName[0]) { - fprintf(f, " f->readReturn(&ret, sizeof(ret));\n"); - } else if (needFlush) { - fprintf(f, " f->readReturn(NULL, 0);\n"); + fprintf(f, " io->coreSetReturn(&ret, sizeof(ret));\n"); + } else /*if (needFlush)*/ { + fprintf(f, " io->coreSetReturn(NULL, 0);\n"); + } + + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + if (vt->ptrLevel == 1) { + fprintf(f, " free((void *)cmd.%s);\n", vt->name); + } + } + for (ct2=0; ct2 < api->paramCount; ct2++) { + const VarType *vt = &api->params[ct2]; + if (vt->ptrLevel == 2) { + fprintf(f, " for (size_t ct = 0; ct < (cmd.%s_length_length / sizeof(cmd.%s_length)); ct++) {\n", vt->name, vt->name); + fprintf(f, " free((void *)cmd.%s);\n", vt->name); + fprintf(f, " }\n"); + } } fprintf(f, "};\n\n"); @@ -608,7 +637,7 @@ int main(int argc, char **argv) { fprintf(f, " uint32_t size;\n"); fprintf(f, "} RsPlaybackRemoteHeader;\n\n"); fprintf(f, "typedef void (*RsPlaybackLocalFunc)(Context *, const void *, size_t sizeBytes);\n"); - fprintf(f, "typedef void (*RsPlaybackRemoteFunc)(Context *, Fifo *, uint8_t *scratch, size_t scratchSize);\n"); + fprintf(f, "typedef void (*RsPlaybackRemoteFunc)(Context *, ThreadIO *);\n"); fprintf(f, "extern RsPlaybackLocalFunc gPlaybackFuncs[%i];\n", apiCount + 1); fprintf(f, "extern RsPlaybackRemoteFunc gPlaybackRemoteFuncs[%i];\n", apiCount + 1); diff --git a/libs/ui/Android.mk b/libs/ui/Android.mk index f8b44523306a..c0292915db42 100644 --- a/libs/ui/Android.mk +++ b/libs/ui/Android.mk @@ -13,41 +13,14 @@ # limitations under the License. LOCAL_PATH:= $(call my-dir) - -# libui is partially built for the host (used by build time keymap validation tool) -# These files are common to host and target builds. -commonSources:= \ - Input.cpp \ - Keyboard.cpp \ - KeyLayoutMap.cpp \ - KeyCharacterMap.cpp \ - VirtualKeyMap.cpp - -# For the host -# ===================================================== - -include $(CLEAR_VARS) - -LOCAL_SRC_FILES:= $(commonSources) - -LOCAL_MODULE:= libui - -include $(BUILD_HOST_STATIC_LIBRARY) - - -# For the device -# ===================================================== - include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ - $(commonSources) \ EGLUtils.cpp \ FramebufferNativeWindow.cpp \ GraphicBuffer.cpp \ GraphicBufferAllocator.cpp \ GraphicBufferMapper.cpp \ - InputTransport.cpp \ PixelFormat.cpp \ Rect.cpp \ Region.cpp @@ -56,14 +29,11 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libEGL \ - libpixelflinger \ - libhardware \ - libhardware_legacy \ - libskia \ - libbinder + libhardware -LOCAL_C_INCLUDES := \ - external/skia/include/core +ifneq ($(BOARD_FRAMEBUFFER_FORCE_FORMAT),) +LOCAL_CFLAGS += -DFRAMEBUFFER_FORCE_FORMAT=$(BOARD_FRAMEBUFFER_FORCE_FORMAT) +endif LOCAL_MODULE:= libui diff --git a/libs/ui/FramebufferNativeWindow.cpp b/libs/ui/FramebufferNativeWindow.cpp index d1dca0c906db..26d4823db110 100644 --- a/libs/ui/FramebufferNativeWindow.cpp +++ b/libs/ui/FramebufferNativeWindow.cpp @@ -100,6 +100,18 @@ FramebufferNativeWindow::FramebufferNativeWindow() mNumFreeBuffers = NUM_FRAME_BUFFERS; mBufferHead = mNumBuffers-1; + /* + * This does not actually change the framebuffer format. It merely + * fakes this format to surfaceflinger so that when it creates + * framebuffer surfaces it will use this format. It's really a giant + * HACK to allow interworking with buggy gralloc+GPU driver + * implementations. You should *NEVER* need to set this for shipping + * devices. + */ +#ifdef FRAMEBUFFER_FORCE_FORMAT + *((uint32_t *)&fbDev->format) = FRAMEBUFFER_FORCE_FORMAT; +#endif + for (i = 0; i < mNumBuffers; i++) { buffers[i] = new NativeBuffer( diff --git a/libs/ui/PixelFormat.cpp b/libs/ui/PixelFormat.cpp index ee186c84de9c..6993dac6998a 100644 --- a/libs/ui/PixelFormat.cpp +++ b/libs/ui/PixelFormat.cpp @@ -15,13 +15,51 @@ */ #include <ui/PixelFormat.h> -#include <pixelflinger/format.h> #include <hardware/hardware.h> +// ---------------------------------------------------------------------------- namespace android { +// ---------------------------------------------------------------------------- static const int COMPONENT_YUV = 0xFF; +struct Info { + size_t size; + size_t bitsPerPixel; + struct { + uint8_t ah; + uint8_t al; + uint8_t rh; + uint8_t rl; + uint8_t gh; + uint8_t gl; + uint8_t bh; + uint8_t bl; + }; + uint8_t components; +}; + +static Info const sPixelFormatInfos[] = { + { 0, 0, { 0, 0, 0, 0, 0, 0, 0, 0 }, 0 }, + { 4, 32, {32,24, 8, 0, 16, 8, 24,16 }, PixelFormatInfo::RGBA }, + { 4, 24, { 0, 0, 8, 0, 16, 8, 24,16 }, PixelFormatInfo::RGB }, + { 3, 24, { 0, 0, 8, 0, 16, 8, 24,16 }, PixelFormatInfo::RGB }, + { 2, 16, { 0, 0, 16,11, 11, 5, 5, 0 }, PixelFormatInfo::RGB }, + { 4, 32, {32,24, 24,16, 16, 8, 8, 0 }, PixelFormatInfo::RGBA }, + { 2, 16, { 1, 0, 16,11, 11, 6, 6, 1 }, PixelFormatInfo::RGBA }, + { 2, 16, { 4, 0, 16,12, 12, 8, 8, 4 }, PixelFormatInfo::RGBA }, + { 1, 8, { 8, 0, 0, 0, 0, 0, 0, 0 }, PixelFormatInfo::ALPHA} +}; + +static const Info* gGetPixelFormatTable(size_t* numEntries) { + if (numEntries) { + *numEntries = sizeof(sPixelFormatInfos)/sizeof(Info); + } + return sPixelFormatInfos; +} + +// ---------------------------------------------------------------------------- + size_t PixelFormatInfo::getScanlineSize(unsigned int width) const { size_t size; @@ -77,27 +115,12 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) } size_t numEntries; - const GGLFormat *i = gglGetPixelFormatTable(&numEntries) + format; + const Info *i = gGetPixelFormatTable(&numEntries) + format; bool valid = uint32_t(format) < numEntries; if (!valid) { return BAD_INDEX; } - #define COMPONENT(name) \ - case GGL_##name: info->components = PixelFormatInfo::name; break; - - switch (i->components) { - COMPONENT(ALPHA) - COMPONENT(RGB) - COMPONENT(RGBA) - COMPONENT(LUMINANCE) - COMPONENT(LUMINANCE_ALPHA) - default: - return BAD_INDEX; - } - - #undef COMPONENT - info->format = format; info->bytesPerPixel = i->size; info->bitsPerPixel = i->bitsPerPixel; @@ -109,9 +132,12 @@ status_t getPixelFormatInfo(PixelFormat format, PixelFormatInfo* info) info->l_green = i->gl; info->h_blue = i->bh; info->l_blue = i->bl; + info->components = i->components; return NO_ERROR; } +// ---------------------------------------------------------------------------- }; // namespace android +// ---------------------------------------------------------------------------- diff --git a/libs/ui/tests/Android.mk b/libs/ui/tests/Android.mk index 700b60479a52..50cad369306c 100644 --- a/libs/ui/tests/Android.mk +++ b/libs/ui/tests/Android.mk @@ -2,47 +2,5 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) -# Build the unit tests. -test_src_files := \ - InputChannel_test.cpp \ - InputEvent_test.cpp \ - InputPublisherAndConsumer_test.cpp - -shared_libraries := \ - libcutils \ - libutils \ - libEGL \ - libbinder \ - libpixelflinger \ - libhardware \ - libhardware_legacy \ - libui \ - libstlport \ - libskia - -static_libraries := \ - libgtest \ - libgtest_main - -c_includes := \ - bionic \ - bionic/libstdc++/include \ - external/gtest/include \ - external/stlport/stlport \ - external/skia/include/core - -module_tags := eng tests - -$(foreach file,$(test_src_files), \ - $(eval include $(CLEAR_VARS)) \ - $(eval LOCAL_SHARED_LIBRARIES := $(shared_libraries)) \ - $(eval LOCAL_STATIC_LIBRARIES := $(static_libraries)) \ - $(eval LOCAL_C_INCLUDES := $(c_includes)) \ - $(eval LOCAL_SRC_FILES := $(file)) \ - $(eval LOCAL_MODULE := $(notdir $(file:%.cpp=%))) \ - $(eval LOCAL_MODULE_TAGS := $(module_tags)) \ - $(eval include $(BUILD_EXECUTABLE)) \ -) - # Build the manual test programs. include $(call all-makefiles-under, $(LOCAL_PATH)) diff --git a/libs/utils/Android.mk b/libs/utils/Android.mk index 24cf5048fa70..a96c8e604d2e 100644 --- a/libs/utils/Android.mk +++ b/libs/utils/Android.mk @@ -18,9 +18,6 @@ LOCAL_PATH:= $(call my-dir) # and once for the device. commonSources:= \ - Asset.cpp \ - AssetDir.cpp \ - AssetManager.cpp \ BasicHashtable.cpp \ BlobCache.cpp \ BufferedTextOutput.cpp \ @@ -29,14 +26,11 @@ commonSources:= \ FileMap.cpp \ Flattenable.cpp \ LinearTransform.cpp \ - ObbFile.cpp \ PropertyMap.cpp \ RefBase.cpp \ - ResourceTypes.cpp \ SharedBuffer.cpp \ Static.cpp \ StopWatch.cpp \ - StreamingZipInflater.cpp \ String8.cpp \ String16.cpp \ StringArray.cpp \ @@ -47,9 +41,6 @@ commonSources:= \ Tokenizer.cpp \ Unicode.cpp \ VectorImpl.cpp \ - ZipFileCRO.cpp \ - ZipFileRO.cpp \ - ZipUtils.cpp \ misc.cpp @@ -63,7 +54,6 @@ LOCAL_SRC_FILES:= $(commonSources) LOCAL_MODULE:= libutils LOCAL_CFLAGS += -DLIBUTILS_NATIVE=1 $(TOOL_CFLAGS) -LOCAL_C_INCLUDES += external/zlib ifeq ($(HOST_OS),windows) ifeq ($(strip $(USE_CYGWIN),),) @@ -79,7 +69,6 @@ endif include $(BUILD_HOST_STATIC_LIBRARY) - # For the device # ===================================================== include $(CLEAR_VARS) @@ -88,8 +77,6 @@ include $(CLEAR_VARS) # we have the common sources, plus some device-specific stuff LOCAL_SRC_FILES:= \ $(commonSources) \ - BackupData.cpp \ - BackupHelpers.cpp \ Looper.cpp ifeq ($(TARGET_OS),linux) @@ -97,14 +84,11 @@ LOCAL_LDLIBS += -lrt -ldl endif LOCAL_C_INCLUDES += \ - external/zlib \ - external/icu4c/common \ bionic/libc/private LOCAL_LDLIBS += -lpthread LOCAL_SHARED_LIBRARIES := \ - libz \ liblog \ libcutils \ libdl \ @@ -113,19 +97,6 @@ LOCAL_SHARED_LIBRARIES := \ LOCAL_MODULE:= libutils include $(BUILD_SHARED_LIBRARY) -ifeq ($(TARGET_OS),linux) -include $(CLEAR_VARS) -LOCAL_C_INCLUDES += \ - external/zlib \ - external/icu4c/common \ - bionic/libc/private -LOCAL_LDLIBS := -lrt -ldl -lpthread -LOCAL_MODULE := libutils -LOCAL_SRC_FILES := $(commonSources) BackupData.cpp BackupHelpers.cpp -include $(BUILD_STATIC_LIBRARY) -endif - - # Include subdirectory makefiles # ============================================================ diff --git a/libs/utils/tests/Android.mk b/libs/utils/tests/Android.mk index 58230f429ed0..a6811fcf8e65 100644 --- a/libs/utils/tests/Android.mk +++ b/libs/utils/tests/Android.mk @@ -7,10 +7,8 @@ test_src_files := \ BasicHashtable_test.cpp \ BlobCache_test.cpp \ Looper_test.cpp \ - ObbFile_test.cpp \ String8_test.cpp \ Unicode_test.cpp \ - ZipFileRO_test.cpp \ shared_libraries := \ libz \ diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java index eae03beae6fc..1c7f57750d02 100644 --- a/media/java/android/media/AudioService.java +++ b/media/java/android/media/AudioService.java @@ -591,7 +591,7 @@ public class AudioService extends IAudioService.Stub { // Post a persist volume msg sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, - SENDMSG_REPLACE, + SENDMSG_QUEUE, PERSIST_LAST_AUDIBLE, device, s, @@ -606,7 +606,7 @@ public class AudioService extends IAudioService.Stub { // to persist). Do not change volume if stream is muted. sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, - SENDMSG_NOOP, + SENDMSG_QUEUE, device, 0, streamState, @@ -746,7 +746,7 @@ public class AudioService extends IAudioService.Stub { // Post a persist volume msg sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, - SENDMSG_REPLACE, + SENDMSG_QUEUE, PERSIST_LAST_AUDIBLE, device, streamState, @@ -758,7 +758,7 @@ public class AudioService extends IAudioService.Stub { // to persist). sendMsg(mAudioHandler, MSG_SET_DEVICE_VOLUME, - SENDMSG_NOOP, + SENDMSG_QUEUE, device, 0, streamState, @@ -2208,7 +2208,7 @@ public class AudioService extends IAudioService.Stub { } sendMsg(mAudioHandler, MSG_SET_ALL_VOLUMES, - SENDMSG_NOOP, + SENDMSG_QUEUE, 0, 0, VolumeStreamState.this, 0); @@ -2252,7 +2252,7 @@ public class AudioService extends IAudioService.Stub { } sendMsg(mAudioHandler, MSG_SET_ALL_VOLUMES, - SENDMSG_NOOP, + SENDMSG_QUEUE, 0, 0, VolumeStreamState.this, 0); @@ -2350,7 +2350,7 @@ public class AudioService extends IAudioService.Stub { // Post a persist volume msg sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, - SENDMSG_REPLACE, + SENDMSG_QUEUE, PERSIST_CURRENT|PERSIST_LAST_AUDIBLE, device, streamState, diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java new file mode 100644 index 000000000000..7f496ca41cf0 --- /dev/null +++ b/media/java/android/media/MediaCodec.java @@ -0,0 +1,210 @@ +/* + * Copyright (C) 2012 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.media; + +import android.view.Surface; +import java.nio.ByteBuffer; +import java.util.Map; + +/** + * MediaCodec class can be used to access low-level media codec, i.e. + * encoder/decoder components. + * @hide +*/ +public class MediaCodec +{ + /** Per buffer metadata includes an offset and size specifying + the range of valid data in the associated codec buffer. + */ + public final static class BufferInfo { + public void set( + int offset, int size, long timeUs, int flags) { + mOffset = offset; + mSize = size; + mPresentationTimeUs = timeUs; + mFlags = flags; + } + + public int mOffset; + public int mSize; + public long mPresentationTimeUs; + public int mFlags; + }; + + public static int FLAG_SYNCFRAME = 1; + public static int FLAG_CODECCONFIG = 2; + public static int FLAG_EOS = 4; + + /** Instantiate a codec component by mime type. For decoder components + this is the mime type of media that this decoder should be able to + decoder, for encoder components it's the type of media this encoder + should encode _to_. + */ + public static MediaCodec CreateByType(String type, boolean encoder) { + return new MediaCodec(type, true /* nameIsType */, encoder); + } + + /** If you know the exact name of the component you want to instantiate + use this method to instantiate it. Use with caution. + */ + public static MediaCodec CreateByComponentName(String name) { + return new MediaCodec( + name, false /* nameIsType */, false /* unused */); + } + + private MediaCodec( + String name, boolean nameIsType, boolean encoder) { + native_setup(name, nameIsType, encoder); + } + + @Override + protected void finalize() { + native_finalize(); + } + + // Make sure you call this when you're done to free up any opened + // component instance instead of relying on the garbage collector + // to do this for you at some point in the future. + public native final void release(); + + public static int CONFIGURE_FLAG_ENCODE = 1; + + /** Configures a component. + * @param format A map of string/value pairs describing the input format + * (decoder) or the desired output format. + * + * Video formats have the following fields: + * "mime" - String + * "width" - Integer + * "height" - Integer + * optional "max-input-size" - Integer + * optional "csd-0", "csd-1" ... - ByteBuffer + * + * Audio formats have the following fields: + * "mime" - String + * "channel-count" - Integer + * "sample-rate" - Integer + * optional "max-input-size" - Integer + * optional "csd-0", "csd-1" ... - ByteBuffer + * + * If the format is used to configure an encoder, additional + * fields must be included: + * "bitrate" - Integer (in bits/sec) + * + * for video formats: + * "color-format" - Integer + * "frame-rate" - Integer or Float + * "i-frame-interval" - Integer + * optional "stride" - Integer, defaults to "width" + * optional "slice-height" - Integer, defaults to "height" + * + * @param surface Specify a surface on which to render the output of this + * decoder. + * @param flags Specify {@see #CONFIGURE_FLAG_ENCODE} to configure the + * component as an encoder. + */ + public void configure( + Map<String, Object> format, Surface surface, int flags) { + String[] keys = null; + Object[] values = null; + + if (format != null) { + keys = new String[format.size()]; + values = new Object[format.size()]; + + int i = 0; + for (Map.Entry<String, Object> entry: format.entrySet()) { + keys[i] = entry.getKey(); + values[i] = entry.getValue(); + ++i; + } + } + + native_configure(keys, values, surface, flags); + } + + private native final void native_configure( + String[] keys, Object[] values, Surface surface, int flags); + + /** After successfully configuring the component, call start. On return + * you can query the component for its input/output buffers. + */ + public native final void start(); + + public native final void stop(); + + /** Flush both input and output ports of the component, all indices + * previously returned in calls to dequeueInputBuffer and + * dequeueOutputBuffer become invalid. + */ + public native final void flush(); + + /** After filling a range of the input buffer at the specified index + * submit it to the component. + */ + public native final void queueInputBuffer( + int index, + int offset, int size, long presentationTimeUs, int flags); + + // Returns the index of an input buffer to be filled with valid data + // or -1 if no such buffer is currently available. + // This method will return immediately if timeoutUs == 0, wait indefinitely + // for the availability of an input buffer if timeoutUs < 0 or wait up + // to "timeoutUs" microseconds if timeoutUs > 0. + public native final int dequeueInputBuffer(long timeoutUs); + + // Returns the index of an output buffer that has been successfully + // decoded or one of the INFO_* constants below. + // The provided "info" will be filled with buffer meta data. + public static final int INFO_TRY_AGAIN_LATER = -1; + public static final int INFO_OUTPUT_FORMAT_CHANGED = -2; + public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3; + + /** Dequeue an output buffer, block at most "timeoutUs" microseconds. */ + public native final int dequeueOutputBuffer( + BufferInfo info, long timeoutUs); + + // If you are done with a buffer, use this call to return the buffer to + // the codec. If you previously specified a surface when configuring this + // video decoder you can optionally render the buffer. + public native final void releaseOutputBuffer(int index, boolean render); + + /** Call this after dequeueOutputBuffer signals a format change by returning + * {@see #INFO_OUTPUT_FORMAT_CHANGED} + */ + public native final Map<String, Object> getOutputFormat(); + + /** Call this after start() returns and whenever dequeueOutputBuffer + * signals an output buffer change by returning + * {@see #INFO_OUTPUT_BUFFERS_CHANGED} + */ + public native final ByteBuffer[] getBuffers(boolean input); + + private static native final void native_init(); + + private native final void native_setup( + String name, boolean nameIsType, boolean encoder); + + private native final void native_finalize(); + + static { + System.loadLibrary("media_jni"); + native_init(); + } + + private int mNativeContext; +} diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java new file mode 100644 index 000000000000..6a7f2f57f3dc --- /dev/null +++ b/media/java/android/media/MediaExtractor.java @@ -0,0 +1,78 @@ +/* + * Copyright (C) 2012 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.media; + +import java.nio.ByteBuffer; +import java.util.Map; + +/** + * MediaExtractor + * @hide +*/ +public class MediaExtractor +{ + public MediaExtractor(String path) { + native_setup(path); + } + + @Override + protected void finalize() { + native_finalize(); + } + + // Make sure you call this when you're done to free up any resources + // instead of relying on the garbage collector to do this for you at + // some point in the future. + public native final void release(); + + public native int countTracks(); + public native Map<String, Object> getTrackFormat(int index); + + // Subsequent calls to "readSampleData", "getSampleTrackIndex" and + // "getSampleTime" only retrieve information for the subset of tracks + // selected by the call below. + // Selecting the same track multiple times has no effect, the track + // is only selected once. + public native void selectTrack(int index); + + // All selected tracks seek near the requested time. The next sample + // returned for each selected track will be a sync sample. + public native void seekTo(long timeUs); + + public native boolean advance(); + + // Retrieve the current encoded sample and store it in the byte buffer + // starting at the given offset. + public native int readSampleData(ByteBuffer byteBuf, int offset); + + // Returns the track index the current sample originates from. + public native int getSampleTrackIndex(); + + // Returns the current sample's presentation time in microseconds. + public native long getSampleTime(); + + private static native final void native_init(); + private native final void native_setup(String path); + private native final void native_finalize(); + + static { + System.loadLibrary("media_jni"); + native_init(); + } + + private int mNativeContext; +} diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java index 85d99c153298..63196308364c 100644 --- a/media/java/android/media/MediaRecorder.java +++ b/media/java/android/media/MediaRecorder.java @@ -303,6 +303,8 @@ public class MediaRecorder /** * Uses the settings from a CamcorderProfile object for recording. This method should * be called after the video AND audio sources are set, and before setOutputFile(). + * If a time lapse CamcorderProfile is used, audio related source or recording + * parameters are ignored. * * @param profile the CamcorderProfile to use * @see android.media.CamcorderProfile @@ -315,8 +317,8 @@ public class MediaRecorder setVideoEncoder(profile.videoCodec); if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW && profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) { - // Enable time lapse. Also don't set audio for time lapse. - setParameter(String.format("time-lapse-enable=1")); + // Nothing needs to be done. Call to setCaptureRate() enables + // time lapse video recording. } else { setAudioEncodingBitRate(profile.audioBitRate); setAudioChannels(profile.audioChannels); @@ -327,7 +329,10 @@ public class MediaRecorder /** * Set video frame capture rate. This can be used to set a different video frame capture - * rate than the recorded video's playback rate. Currently this works only for time lapse mode. + * rate than the recorded video's playback rate. This method also sets the recording mode + * to time lapse. In time lapse video recording, only video is recorded. Audio related + * parameters are ignored when a time lapse recording session starts, if an application + * sets them. * * @param fps Rate at which frames should be captured in frames per second. * The fps can go as low as desired. However the fastest fps will be limited by the hardware. @@ -339,6 +344,9 @@ public class MediaRecorder * possible. */ public void setCaptureRate(double fps) { + // Make sure that time lapse is enabled when this method is called. + setParameter(String.format("time-lapse-enable=1")); + double timeBetweenFrameCapture = 1 / fps; int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture); setParameter(String.format("time-between-time-lapse-frame-capture=%d", diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java index 52d31c749e07..a08d6c37e1a4 100644 --- a/media/java/android/media/MediaScanner.java +++ b/media/java/android/media/MediaScanner.java @@ -62,6 +62,9 @@ import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Locale; +import libcore.io.ErrnoException; +import libcore.io.Libcore; + /** * Internal service helper that no-one should use directly. * @@ -348,20 +351,18 @@ public class MediaScanner private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options(); - private static class FileCacheEntry { + private static class FileEntry { long mRowId; String mPath; long mLastModified; int mFormat; - boolean mSeenInFileSystem; boolean mLastModifiedChanged; - FileCacheEntry(long rowId, String path, long lastModified, int format) { + FileEntry(long rowId, String path, long lastModified, int format) { mRowId = rowId; mPath = path; mLastModified = lastModified; mFormat = format; - mSeenInFileSystem = false; mLastModifiedChanged = false; } @@ -373,11 +374,7 @@ public class MediaScanner private MediaInserter mMediaInserter; - // hashes file path to FileCacheEntry. - // path should be lower case if mCaseInsensitivePaths is true - private LinkedHashMap<String, FileCacheEntry> mFileCache; - - private ArrayList<FileCacheEntry> mPlayLists; + private ArrayList<FileEntry> mPlayLists; private DrmManagerClient mDrmManagerClient = null; @@ -432,7 +429,7 @@ public class MediaScanner private int mWidth; private int mHeight; - public FileCacheEntry beginFile(String path, String mimeType, long lastModified, + public FileEntry beginFile(String path, String mimeType, long lastModified, long fileSize, boolean isDirectory, boolean noMedia) { mMimeType = mimeType; mFileType = 0; @@ -465,11 +462,7 @@ public class MediaScanner } } - String key = path; - if (mCaseInsensitivePaths) { - key = path.toLowerCase(); - } - FileCacheEntry entry = mFileCache.get(key); + FileEntry entry = makeEntryFor(path); // add some slack to avoid a rounding error long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0; boolean wasModified = delta > 1 || delta < -1; @@ -477,13 +470,11 @@ public class MediaScanner if (wasModified) { entry.mLastModified = lastModified; } else { - entry = new FileCacheEntry(0, path, lastModified, + entry = new FileEntry(0, path, lastModified, (isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0)); - mFileCache.put(key, entry); } entry.mLastModifiedChanged = true; } - entry.mSeenInFileSystem = true; if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) { mPlayLists.add(entry); @@ -525,7 +516,7 @@ public class MediaScanner Uri result = null; // long t1 = System.currentTimeMillis(); try { - FileCacheEntry entry = beginFile(path, mimeType, lastModified, + FileEntry entry = beginFile(path, mimeType, lastModified, fileSize, isDirectory, noMedia); // rescan for metadata if file was modified since last scan if (entry != null && (entry.mLastModifiedChanged || scanAlways)) { @@ -778,7 +769,7 @@ public class MediaScanner return map; } - private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications, + private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications, boolean alarms, boolean music, boolean podcasts) throws RemoteException { // update database @@ -1028,55 +1019,94 @@ public class MediaScanner String where = null; String[] selectionArgs = null; - if (mFileCache == null) { - mFileCache = new LinkedHashMap<String, FileCacheEntry>(); - } else { - mFileCache.clear(); - } if (mPlayLists == null) { - mPlayLists = new ArrayList<FileCacheEntry>(); + mPlayLists = new ArrayList<FileEntry>(); } else { mPlayLists.clear(); } if (filePath != null) { // query for only one file - where = Files.FileColumns.DATA + "=?"; - selectionArgs = new String[] { filePath }; + where = MediaStore.Files.FileColumns._ID + ">?" + + " AND " + Files.FileColumns.DATA + "=?"; + selectionArgs = new String[] { "", filePath }; + } else { + where = MediaStore.Files.FileColumns._ID + ">?"; + selectionArgs = new String[] { "" }; } + // Tell the provider to not delete the file. + // If the file is truly gone the delete is unnecessary, and we want to avoid + // accidentally deleting files that are really there (this may happen if the + // filesystem is mounted and unmounted while the scanner is running). + Uri.Builder builder = mFilesUri.buildUpon(); + builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); + MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build()); + // Build the list of files from the content provider try { if (prescanFiles) { - // First read existing files from the files table + // First read existing files from the files table. + // Because we'll be deleting entries for missing files as we go, + // we need to query the database in small batches, to avoid problems + // with CursorWindow positioning. + long lastId = Long.MIN_VALUE; + Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build(); + mWasEmptyPriorToScan = true; + + while (true) { + selectionArgs[0] = "" + lastId; + if (c != null) { + c.close(); + c = null; + } + c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION, + where, selectionArgs, MediaStore.Files.FileColumns._ID, null); + if (c == null) { + break; + } - c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, - where, selectionArgs, null, null); + int num = c.getCount(); - if (c != null) { - mWasEmptyPriorToScan = c.getCount() == 0; + if (num == 0) { + break; + } + mWasEmptyPriorToScan = false; while (c.moveToNext()) { long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); + lastId = rowId; // Only consider entries with absolute path names. // This allows storing URIs in the database without the // media scanner removing them. if (path != null && path.startsWith("/")) { - String key = path; - if (mCaseInsensitivePaths) { - key = path.toLowerCase(); + boolean exists = false; + try { + exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK); + } catch (ErrnoException e1) { + } + if (!exists && !MtpConstants.isAbstractObject(format)) { + // do not delete missing playlists, since they may have been + // modified by the user. + // The user can delete them in the media player instead. + // instead, clear the path and lastModified fields in the row + MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); + int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); + + if (!MediaFile.isPlayListFileType(fileType)) { + deleter.delete(rowId); + if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) { + deleter.flush(); + String parent = new File(path).getParent(); + mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null); + } + } } - - FileCacheEntry entry = new FileCacheEntry(rowId, path, - lastModified, format); - mFileCache.put(key, entry); } } - c.close(); - c = null; } } } @@ -1084,6 +1114,7 @@ public class MediaScanner if (c != null) { c.close(); } + deleter.flush(); } // compute original size of images @@ -1186,57 +1217,6 @@ public class MediaScanner } private void postscan(String[] directories) throws RemoteException { - Iterator<FileCacheEntry> iterator = mFileCache.values().iterator(); - - // Tell the provider to not delete the file. - // If the file is truly gone the delete is unnecessary, and we want to avoid - // accidentally deleting files that are really there (this may happen if the - // filesystem is mounted and unmounted while the scanner is running). - Uri.Builder builder = mFilesUri.buildUpon(); - builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false"); - MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build()); - - while (iterator.hasNext()) { - FileCacheEntry entry = iterator.next(); - String path = entry.mPath; - - // remove database entries for files that no longer exist. - boolean fileMissing = false; - - if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) { - if (inScanDirectory(path, directories)) { - // we didn't see this file in the scan directory. - fileMissing = true; - } else { - // the file actually a directory or other abstract object - // or is outside of our scan directory, - // so we need to check for file existence here. - File testFile = new File(path); - if (!testFile.exists()) { - fileMissing = true; - } - } - } - - if (fileMissing) { - // do not delete missing playlists, since they may have been modified by the user. - // the user can delete them in the media player instead. - // instead, clear the path and lastModified fields in the row - MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path); - int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType); - - if (!MediaFile.isPlayListFileType(fileType)) { - deleter.delete(entry.mRowId); - iterator.remove(); - if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) { - deleter.flush(); - File f = new File(path); - mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null); - } - } - } - } - deleter.flush(); // handle playlists last, after we know what media files are on the storage. if (mProcessPlaylists) { @@ -1248,7 +1228,6 @@ public class MediaScanner // allow GC to clean up mPlayLists = null; - mFileCache = null; mMediaProvider = null; } @@ -1422,11 +1401,7 @@ public class MediaScanner // build file cache so we can look up tracks in the playlist prescan(null, true); - String key = path; - if (mCaseInsensitivePaths) { - key = path.toLowerCase(); - } - FileCacheEntry entry = mFileCache.get(key); + FileEntry entry = makeEntryFor(path); if (entry != null) { processPlayList(entry); } @@ -1445,6 +1420,37 @@ public class MediaScanner } } + FileEntry makeEntryFor(String path) { + String key = path; + String where; + String[] selectionArgs; + if (mCaseInsensitivePaths) { + where = Files.FileColumns.DATA + " LIKE ?"; + selectionArgs = new String[] { path }; + } else { + where = Files.FileColumns.DATA + "=?"; + selectionArgs = new String[] { path }; + } + + Cursor c = null; + try { + c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, + where, selectionArgs, null, null); + if (c.moveToNext()) { + long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); + int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); + long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); + return new FileEntry(rowId, path, lastModified, format); + } + } catch (RemoteException e) { + } finally { + if (c != null) { + c.close(); + } + } + return null; + } + // returns the number of matching file/directory names, starting from the right private int matchPaths(String path1, String path2) { int result = 0; @@ -1495,26 +1501,37 @@ public class MediaScanner //FIXME - should we look for "../" within the path? // best matching MediaFile for the play list entry - FileCacheEntry bestMatch = null; + FileEntry bestMatch = null; // number of rightmost file/directory names for bestMatch int bestMatchLength = 0; - Iterator<FileCacheEntry> iterator = mFileCache.values().iterator(); - while (iterator.hasNext()) { - FileCacheEntry cacheEntry = iterator.next(); - String path = cacheEntry.mPath; + Cursor c = null; + try { + c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION, + null, null, null, null); + } catch (RemoteException e1) { + } - if (path.equalsIgnoreCase(entry)) { - bestMatch = cacheEntry; - break; // don't bother continuing search - } + if (c != null) { + while (c.moveToNext()) { + long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX); + String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX); + int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX); + long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX); + + if (path.equalsIgnoreCase(entry)) { + bestMatch = new FileEntry(rowId, path, lastModified, format); + break; // don't bother continuing search + } - int matchLength = matchPaths(path, entry); - if (matchLength > bestMatchLength) { - bestMatch = cacheEntry; - bestMatchLength = matchLength; + int matchLength = matchPaths(path, entry); + if (matchLength > bestMatchLength) { + bestMatch = new FileEntry(rowId, path, lastModified, format); + bestMatchLength = matchLength; + } } + c.close(); } if (bestMatch == null) { @@ -1524,7 +1541,7 @@ public class MediaScanner try { // check rowid is set. Rowid may be missing if it is inserted by bulkInsert(). if (bestMatch.mRowId == 0) { - Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION, + c = mMediaProvider.query(mAudioUri, ID_PROJECTION, MediaStore.Files.FileColumns.DATA + "=?", new String[] { bestMatch.mPath }, null, null); if (c != null) { @@ -1677,7 +1694,7 @@ public class MediaScanner } } - private void processPlayList(FileCacheEntry entry) throws RemoteException { + private void processPlayList(FileEntry entry) throws RemoteException { String path = entry.mPath; ContentValues values = new ContentValues(); int lastSlash = path.lastIndexOf('/'); @@ -1728,9 +1745,9 @@ public class MediaScanner } private void processPlayLists() throws RemoteException { - Iterator<FileCacheEntry> iterator = mPlayLists.iterator(); + Iterator<FileEntry> iterator = mPlayLists.iterator(); while (iterator.hasNext()) { - FileCacheEntry entry = iterator.next(); + FileEntry entry = iterator.next(); // only process playlist files if they are new or have been modified since the last scan if (entry.mLastModifiedChanged) { processPlayList(entry); diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java index bcf7b89b48a5..91d0addde5f8 100755 --- a/media/java/android/media/audiofx/Visualizer.java +++ b/media/java/android/media/audiofx/Visualizer.java @@ -84,6 +84,7 @@ public class Visualizer { // to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp private static final int NATIVE_EVENT_PCM_CAPTURE = 0; private static final int NATIVE_EVENT_FFT_CAPTURE = 1; + private static final int NATIVE_EVENT_SERVER_DIED = 2; // Error codes: /** @@ -147,6 +148,10 @@ public class Visualizer { * PCM and FFT capture listener registered by client */ private OnDataCaptureListener mCaptureListener = null; + /** + * Server Died listener registered by client + */ + private OnServerDiedListener mServerDiedListener = null; // accessed by native methods private int mNativeVisualizer; @@ -396,6 +401,9 @@ public class Visualizer { public interface OnDataCaptureListener { /** * Method called when a new waveform capture is available. + * <p>Data in the waveform buffer is valid only within the scope of the callback. + * Applications which needs access to the waveform data after returning from the callback + * should make a copy of the data instead of holding a reference. * @param visualizer Visualizer object on which the listener is registered. * @param waveform array of bytes containing the waveform representation. * @param samplingRate sampling rate of the audio visualized. @@ -404,6 +412,9 @@ public class Visualizer { /** * Method called when a new frequency capture is available. + * <p>Data in the fft buffer is valid only within the scope of the callback. + * Applications which needs access to the fft data after returning from the callback + * should make a copy of the data instead of holding a reference. * @param visualizer Visualizer object on which the listener is registered. * @param fft array of bytes containing the frequency representation. * @param samplingRate sampling rate of the audio visualized. @@ -452,6 +463,43 @@ public class Visualizer { } /** + * @hide + * + * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that + * the connection to the native media server has been broken and that the Visualizer object will + * need to be released and re-created. + * The client application can implement this interface and register the listener with the + * {@link #setServerDiedListener(OnServerDiedListener)} method. + */ + public interface OnServerDiedListener { + /** + * @hide + * + * Method called when the native media server has died. + * <p>If the native media server encounters a fatal error and needs to restart, the binder + * connection from the {@link #Visualizer} to the media server will be broken. Data capture + * callbacks will stop happening, and client initiated calls to the {@link #Visualizer} + * instance will fail with the error code {@link #DEAD_OBJECT}. To restore functionality, + * clients should {@link #release()} their old visualizer and create a new instance. + */ + void onServerDied(); + } + + /** + * @hide + * + * Registers an OnServerDiedListener interface. + * <p>Call this method with a null listener to stop receiving server death notifications. + * @return {@link #SUCCESS} in case of success, + */ + public int setServerDiedListener(OnServerDiedListener listener) { + synchronized (mListenerLock) { + mServerDiedListener = listener; + } + return SUCCESS; + } + + /** * Helper class to handle the forwarding of native events to the appropriate listeners */ private class NativeEventHandler extends Handler @@ -463,11 +511,7 @@ public class Visualizer { mVisualizer = v; } - @Override - public void handleMessage(Message msg) { - if (mVisualizer == null) { - return; - } + private void handleCaptureMessage(Message msg) { OnDataCaptureListener l = null; synchronized (mListenerLock) { l = mVisualizer.mCaptureListener; @@ -476,6 +520,7 @@ public class Visualizer { if (l != null) { byte[] data = (byte[])msg.obj; int samplingRate = msg.arg1; + switch(msg.what) { case NATIVE_EVENT_PCM_CAPTURE: l.onWaveFormDataCapture(mVisualizer, data, samplingRate); @@ -484,11 +529,41 @@ public class Visualizer { l.onFftDataCapture(mVisualizer, data, samplingRate); break; default: - Log.e(TAG,"Unknown native event: "+msg.what); + Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what); break; } } } + + private void handleServerDiedMessage(Message msg) { + OnServerDiedListener l = null; + synchronized (mListenerLock) { + l = mVisualizer.mServerDiedListener; + } + + if (l != null) + l.onServerDied(); + } + + @Override + public void handleMessage(Message msg) { + if (mVisualizer == null) { + return; + } + + switch(msg.what) { + case NATIVE_EVENT_PCM_CAPTURE: + case NATIVE_EVENT_FFT_CAPTURE: + handleCaptureMessage(msg); + break; + case NATIVE_EVENT_SERVER_DIED: + handleServerDiedMessage(msg); + break; + default: + Log.e(TAG,"Unknown native event: "+msg.what); + break; + } + } } //--------------------------------------------------------- diff --git a/media/jni/Android.mk b/media/jni/Android.mk index 23cc0e2f3228..070d2d93a8c2 100644 --- a/media/jni/Android.mk +++ b/media/jni/Android.mk @@ -2,6 +2,8 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ + android_media_MediaCodec.cpp \ + android_media_MediaExtractor.cpp \ android_media_MediaPlayer.cpp \ android_media_MediaRecorder.cpp \ android_media_MediaScanner.cpp \ @@ -25,6 +27,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libgui \ libstagefright \ + libstagefright_foundation \ libcamera_client \ libmtp \ libusbhost \ @@ -39,10 +42,12 @@ LOCAL_C_INCLUDES += \ external/tremor/Tremor \ frameworks/base/core/jni \ frameworks/base/media/libmedia \ + frameworks/base/media/libstagefright \ frameworks/base/media/libstagefright/codecs/amrnb/enc/src \ frameworks/base/media/libstagefright/codecs/amrnb/common \ frameworks/base/media/libstagefright/codecs/amrnb/common/include \ frameworks/base/media/mtp \ + frameworks/base/include/media/stagefright/openmax \ $(PV_INCLUDES) \ $(JNI_H_INCLUDE) \ $(call include-path-for, corecg graphics) diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp new file mode 100644 index 000000000000..43ca263a0301 --- /dev/null +++ b/media/jni/android_media_MediaCodec.cpp @@ -0,0 +1,550 @@ +/* + * Copyright 2012, 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_NDEBUG 0 +#define LOG_TAG "MediaCodec-JNI" +#include <utils/Log.h> + +#include "android_media_MediaCodec.h" + +#include "android_media_Utils.h" +#include "android_runtime/AndroidRuntime.h" +#include "android_runtime/android_view_Surface.h" +#include "jni.h" +#include "JNIHelp.h" + +#include <media/stagefright/MediaCodec.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MediaErrors.h> +#include <surfaceflinger/Surface.h> + +namespace android { + +// Keep these in sync with their equivalents in MediaCodec.java !!! +enum { + DEQUEUE_INFO_TRY_AGAIN_LATER = -1, + DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2, + DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3, +}; + +struct fields_t { + jfieldID context; +}; + +static fields_t gFields; + +//////////////////////////////////////////////////////////////////////////////// + +JMediaCodec::JMediaCodec( + JNIEnv *env, jobject thiz, + const char *name, bool nameIsType, bool encoder) + : mClass(NULL), + mObject(NULL) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); + + mLooper = new ALooper; + mLooper->setName("MediaCodec_looper"); + + mLooper->start( + false, // runOnCallingThread + false, // canCallJava + PRIORITY_DEFAULT); + + if (nameIsType) { + mCodec = MediaCodec::CreateByType(mLooper, name, encoder); + } else { + mCodec = MediaCodec::CreateByComponentName(mLooper, name); + } +} + +status_t JMediaCodec::initCheck() const { + return mCodec != NULL ? OK : NO_INIT; +} + +JMediaCodec::~JMediaCodec() { + mCodec->stop(); + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +status_t JMediaCodec::configure( + const sp<AMessage> &format, + const sp<ISurfaceTexture> &surfaceTexture, + int flags) { + sp<SurfaceTextureClient> client; + if (surfaceTexture != NULL) { + client = new SurfaceTextureClient(surfaceTexture); + } + return mCodec->configure(format, client, flags); +} + +status_t JMediaCodec::start() { + return mCodec->start(); +} + +status_t JMediaCodec::stop() { + return mCodec->stop(); +} + +status_t JMediaCodec::flush() { + return mCodec->flush(); +} + +status_t JMediaCodec::queueInputBuffer( + size_t index, + size_t offset, size_t size, int64_t timeUs, uint32_t flags) { + return mCodec->queueInputBuffer(index, offset, size, timeUs, flags); +} + +status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { + return mCodec->dequeueInputBuffer(index, timeoutUs); +} + +status_t JMediaCodec::dequeueOutputBuffer( + JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) { + size_t size, offset; + int64_t timeUs; + uint32_t flags; + status_t err; + if ((err = mCodec->dequeueOutputBuffer( + index, &size, &offset, &timeUs, &flags, timeoutUs)) != OK) { + return err; + } + + jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo"); + + jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V"); + env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags); + + return OK; +} + +status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) { + return render + ? mCodec->renderOutputBufferAndRelease(index) + : mCodec->releaseOutputBuffer(index); +} + +status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const { + sp<AMessage> msg; + status_t err; + if ((err = mCodec->getOutputFormat(&msg)) != OK) { + return err; + } + + return ConvertMessageToMap(env, msg, format); +} + +status_t JMediaCodec::getBuffers( + JNIEnv *env, bool input, jobjectArray *bufArray) const { + Vector<sp<ABuffer> > buffers; + + status_t err = + input + ? mCodec->getInputBuffers(&buffers) + : mCodec->getOutputBuffers(&buffers); + + if (err != OK) { + return err; + } + + jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer"); + + *bufArray = (jobjectArray)env->NewObjectArray( + buffers.size(), byteBufferClass, NULL); + + for (size_t i = 0; i < buffers.size(); ++i) { + const sp<ABuffer> &buffer = buffers.itemAt(i); + + jobject byteBuffer = + env->NewDirectByteBuffer( + buffer->base(), + buffer->capacity()); + + env->SetObjectArrayElement( + *bufArray, i, byteBuffer); + + env->DeleteLocalRef(byteBuffer); + byteBuffer = NULL; + } + + return OK; +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static sp<JMediaCodec> setMediaCodec( + JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) { + sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context); + if (codec != NULL) { + codec->incStrong(thiz); + } + if (old != NULL) { + old->decStrong(thiz); + } + env->SetIntField(thiz, gFields.context, (int)codec.get()); + + return old; +} + +static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) { + return (JMediaCodec *)env->GetIntField(thiz, gFields.context); +} + +static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) { + setMediaCodec(env, thiz, NULL); +} + +static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) { + switch (err) { + case OK: + return 0; + + case -EAGAIN: + return DEQUEUE_INFO_TRY_AGAIN_LATER; + + case INFO_FORMAT_CHANGED: + return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED; + + case INFO_OUTPUT_BUFFERS_CHANGED: + return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED; + + default: + { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + break; + } + } + + return 0; +} + +static void android_media_MediaCodec_native_configure( + JNIEnv *env, + jobject thiz, + jobjectArray keys, jobjectArray values, + jobject jsurface, + jint flags) { + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + sp<AMessage> format; + status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format); + + if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + sp<ISurfaceTexture> surfaceTexture; + if (jsurface != NULL) { + sp<Surface> surface(Surface_getSurface(env, jsurface)); + if (surface != NULL) { + surfaceTexture = surface->getSurfaceTexture(); + } else { + jniThrowException( + env, + "java/lang/IllegalArgumentException", + "The surface has been released"); + return; + } + } + + err = codec->configure(format, surfaceTexture, flags); + + throwExceptionAsNecessary(env, err); +} + +static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) { + ALOGV("android_media_MediaCodec_start"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = codec->start(); + + throwExceptionAsNecessary(env, err); +} + +static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) { + ALOGV("android_media_MediaCodec_stop"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = codec->stop(); + + throwExceptionAsNecessary(env, err); +} + +static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) { + ALOGV("android_media_MediaCodec_flush"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = codec->flush(); + + throwExceptionAsNecessary(env, err); +} + +static void android_media_MediaCodec_queueInputBuffer( + JNIEnv *env, + jobject thiz, + jint index, + jint offset, + jint size, + jlong timestampUs, + jint flags) { + ALOGV("android_media_MediaCodec_queueInputBuffer"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = codec->queueInputBuffer( + index, offset, size, timestampUs, flags); + + throwExceptionAsNecessary(env, err); +} + +static jint android_media_MediaCodec_dequeueInputBuffer( + JNIEnv *env, jobject thiz, jlong timeoutUs) { + ALOGV("android_media_MediaCodec_dequeueInputBuffer"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return -1; + } + + size_t index; + status_t err = codec->dequeueInputBuffer(&index, timeoutUs); + + if (err == OK) { + return index; + } + + return throwExceptionAsNecessary(env, err); +} + +static jint android_media_MediaCodec_dequeueOutputBuffer( + JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) { + ALOGV("android_media_MediaCodec_dequeueOutputBuffer"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + size_t index; + status_t err = codec->dequeueOutputBuffer( + env, bufferInfo, &index, timeoutUs); + + if (err == OK) { + return index; + } + + return throwExceptionAsNecessary(env, err); +} + +static void android_media_MediaCodec_releaseOutputBuffer( + JNIEnv *env, jobject thiz, jint index, jboolean render) { + ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = codec->releaseOutputBuffer(index, render); + + throwExceptionAsNecessary(env, err); +} + +static jobject android_media_MediaCodec_getOutputFormat( + JNIEnv *env, jobject thiz) { + ALOGV("android_media_MediaCodec_getOutputFormat"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + jobject format; + status_t err = codec->getOutputFormat(env, &format); + + if (err == OK) { + return format; + } + + throwExceptionAsNecessary(env, err); + + return NULL; +} + +static jobjectArray android_media_MediaCodec_getBuffers( + JNIEnv *env, jobject thiz, jboolean input) { + ALOGV("android_media_MediaCodec_getBuffers"); + + sp<JMediaCodec> codec = getMediaCodec(env, thiz); + + if (codec == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + jobjectArray buffers; + status_t err = codec->getBuffers(env, input, &buffers); + + if (err == OK) { + return buffers; + } + + throwExceptionAsNecessary(env, err); + + return NULL; +} + +static void android_media_MediaCodec_native_init(JNIEnv *env) { + jclass clazz = env->FindClass("android/media/MediaCodec"); + CHECK(clazz != NULL); + + gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); + CHECK(gFields.context != NULL); +} + +static void android_media_MediaCodec_native_setup( + JNIEnv *env, jobject thiz, + jstring name, jboolean nameIsType, jboolean encoder) { + if (name == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + const char *tmp = env->GetStringUTFChars(name, NULL); + + if (tmp == NULL) { + return; + } + + sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder); + + status_t err = codec->initCheck(); + + env->ReleaseStringUTFChars(name, tmp); + tmp = NULL; + + if (err != OK) { + jniThrowException( + env, + "java/io/IOException", + "Failed to allocate component instance"); + return; + } + + setMediaCodec(env,thiz, codec); +} + +static void android_media_MediaCodec_native_finalize( + JNIEnv *env, jobject thiz) { + android_media_MediaCodec_release(env, thiz); +} + +static JNINativeMethod gMethods[] = { + { "release", "()V", (void *)android_media_MediaCodec_release }, + + { "native_configure", + "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V", + (void *)android_media_MediaCodec_native_configure }, + + { "start", "()V", (void *)android_media_MediaCodec_start }, + { "stop", "()V", (void *)android_media_MediaCodec_stop }, + { "flush", "()V", (void *)android_media_MediaCodec_flush }, + + { "queueInputBuffer", "(IIIJI)V", + (void *)android_media_MediaCodec_queueInputBuffer }, + + { "dequeueInputBuffer", "(J)I", + (void *)android_media_MediaCodec_dequeueInputBuffer }, + + { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I", + (void *)android_media_MediaCodec_dequeueOutputBuffer }, + + { "releaseOutputBuffer", "(IZ)V", + (void *)android_media_MediaCodec_releaseOutputBuffer }, + + { "getOutputFormat", "()Ljava/util/Map;", + (void *)android_media_MediaCodec_getOutputFormat }, + + { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;", + (void *)android_media_MediaCodec_getBuffers }, + + { "native_init", "()V", (void *)android_media_MediaCodec_native_init }, + + { "native_setup", "(Ljava/lang/String;ZZ)V", + (void *)android_media_MediaCodec_native_setup }, + + { "native_finalize", "()V", + (void *)android_media_MediaCodec_native_finalize }, +}; + +int register_android_media_MediaCodec(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/media/MediaCodec", gMethods, NELEM(gMethods)); +} diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h new file mode 100644 index 000000000000..6b1257db74b1 --- /dev/null +++ b/media/jni/android_media_MediaCodec.h @@ -0,0 +1,81 @@ +/* + * Copyright 2012, 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_MEDIA_MEDIACODEC_H_ +#define _ANDROID_MEDIA_MEDIACODEC_H_ + +#include "jni.h" + +#include <media/stagefright/foundation/ABase.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +namespace android { + +struct ALooper; +struct AMessage; +struct ISurfaceTexture; +struct MediaCodec; + +struct JMediaCodec : public RefBase { + JMediaCodec( + JNIEnv *env, jobject thiz, + const char *name, bool nameIsType, bool encoder); + + status_t initCheck() const; + + status_t configure( + const sp<AMessage> &format, + const sp<ISurfaceTexture> &surfaceTexture, + int flags); + + status_t start(); + status_t stop(); + + status_t flush(); + + status_t queueInputBuffer( + size_t index, + size_t offset, size_t size, int64_t timeUs, uint32_t flags); + + status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs); + + status_t dequeueOutputBuffer( + JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs); + + status_t releaseOutputBuffer(size_t index, bool render); + + status_t getOutputFormat(JNIEnv *env, jobject *format) const; + + status_t getBuffers( + JNIEnv *env, bool input, jobjectArray *bufArray) const; + +protected: + virtual ~JMediaCodec(); + +private: + jclass mClass; + jweak mObject; + + sp<ALooper> mLooper; + sp<MediaCodec> mCodec; + + DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec); +}; + +} // namespace android + +#endif // _ANDROID_MEDIA_MEDIACODEC_H_ diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp new file mode 100644 index 000000000000..4757adfc6429 --- /dev/null +++ b/media/jni/android_media_MediaExtractor.cpp @@ -0,0 +1,400 @@ +/* + * Copyright 2012, 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_NDEBUG 0 +#define LOG_TAG "MediaExtractor-JNI" +#include <utils/Log.h> + +#include "android_media_MediaExtractor.h" + +#include "android_media_Utils.h" +#include "android_runtime/AndroidRuntime.h" +#include "jni.h" +#include "JNIHelp.h" + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/NuMediaExtractor.h> + +namespace android { + +struct fields_t { + jfieldID context; +}; + +static fields_t gFields; + +//////////////////////////////////////////////////////////////////////////////// + +JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz) + : mClass(NULL), + mObject(NULL) { + jclass clazz = env->GetObjectClass(thiz); + CHECK(clazz != NULL); + + mClass = (jclass)env->NewGlobalRef(clazz); + mObject = env->NewWeakGlobalRef(thiz); + + mImpl = new NuMediaExtractor; +} + +JMediaExtractor::~JMediaExtractor() { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->DeleteWeakGlobalRef(mObject); + mObject = NULL; + env->DeleteGlobalRef(mClass); + mClass = NULL; +} + +status_t JMediaExtractor::setDataSource(const char *path) { + return mImpl->setDataSource(path); +} + +size_t JMediaExtractor::countTracks() const { + return mImpl->countTracks(); +} + +status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const { + sp<AMessage> msg; + status_t err; + if ((err = mImpl->getTrackFormat(index, &msg)) != OK) { + return err; + } + + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + return ConvertMessageToMap(env, msg, format); +} + +status_t JMediaExtractor::selectTrack(size_t index) { + return mImpl->selectTrack(index); +} + +status_t JMediaExtractor::seekTo(int64_t timeUs) { + return mImpl->seekTo(timeUs); +} + +status_t JMediaExtractor::advance() { + return mImpl->advance(); +} + +status_t JMediaExtractor::readSampleData( + jobject byteBuf, size_t offset, size_t *sampleSize) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + void *dst = env->GetDirectBufferAddress(byteBuf); + + if (dst == NULL) { + // XXX if dst is NULL also fall back to "array()" + return INVALID_OPERATION; + } + + jlong dstSize = env->GetDirectBufferCapacity(byteBuf); + + if (dstSize < offset) { + return -ERANGE; + } + + sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset); + + status_t err = mImpl->readSampleData(buffer); + + if (err != OK) { + return err; + } + + *sampleSize = buffer->size(); + + return OK; +} + +status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { + return mImpl->getSampleTrackIndex(trackIndex); +} + +status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { + return mImpl->getSampleTime(sampleTimeUs); +} + +} // namespace android + +//////////////////////////////////////////////////////////////////////////////// + +using namespace android; + +static sp<JMediaExtractor> setMediaExtractor( + JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) { + sp<JMediaExtractor> old = + (JMediaExtractor *)env->GetIntField(thiz, gFields.context); + + if (extractor != NULL) { + extractor->incStrong(thiz); + } + if (old != NULL) { + old->decStrong(thiz); + } + env->SetIntField(thiz, gFields.context, (int)extractor.get()); + + return old; +} + +static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) { + return (JMediaExtractor *)env->GetIntField(thiz, gFields.context); +} + +static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) { + setMediaExtractor(env, thiz, NULL); +} + +static jint android_media_MediaExtractor_countTracks( + JNIEnv *env, jobject thiz) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + return extractor->countTracks(); +} + +static jobject android_media_MediaExtractor_getTrackFormat( + JNIEnv *env, jobject thiz, jint index) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return NULL; + } + + jobject format; + status_t err = extractor->getTrackFormat(index, &format); + + if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return NULL; + } + + return format; +} + +static void android_media_MediaExtractor_selectTrack( + JNIEnv *env, jobject thiz, jint index) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = extractor->selectTrack(index); + + if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } +} + +static void android_media_MediaExtractor_seekTo( + JNIEnv *env, jobject thiz, jlong timeUs) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return; + } + + status_t err = extractor->seekTo(timeUs); + + if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } +} + +static jboolean android_media_MediaExtractor_advance( + JNIEnv *env, jobject thiz) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return false; + } + + status_t err = extractor->advance(); + + if (err == ERROR_END_OF_STREAM) { + return false; + } else if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + return true; +} + +static jint android_media_MediaExtractor_readSampleData( + JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return -1; + } + + size_t sampleSize; + status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize); + + if (err == ERROR_END_OF_STREAM) { + return -1; + } else if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + return sampleSize; +} + +static jint android_media_MediaExtractor_getSampleTrackIndex( + JNIEnv *env, jobject thiz) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return -1; + } + + size_t trackIndex; + status_t err = extractor->getSampleTrackIndex(&trackIndex); + + if (err == ERROR_END_OF_STREAM) { + return -1; + } else if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + return trackIndex; +} + +static jlong android_media_MediaExtractor_getSampleTime( + JNIEnv *env, jobject thiz) { + sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz); + + if (extractor == NULL) { + jniThrowException(env, "java/lang/IllegalStateException", NULL); + return -1ll; + } + + int64_t sampleTimeUs; + status_t err = extractor->getSampleTime(&sampleTimeUs); + + if (err == ERROR_END_OF_STREAM) { + return -1ll; + } else if (err != OK) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return false; + } + + return sampleTimeUs; +} + +static void android_media_MediaExtractor_native_init(JNIEnv *env) { + jclass clazz = env->FindClass("android/media/MediaExtractor"); + CHECK(clazz != NULL); + + gFields.context = env->GetFieldID(clazz, "mNativeContext", "I"); + CHECK(gFields.context != NULL); + + DataSource::RegisterDefaultSniffers(); +} + +static void android_media_MediaExtractor_native_setup( + JNIEnv *env, jobject thiz, jstring path) { + sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz); + + if (path == NULL) { + jniThrowException(env, "java/lang/IllegalArgumentException", NULL); + return; + } + + const char *tmp = env->GetStringUTFChars(path, NULL); + + if (tmp == NULL) { + return; + } + + status_t err = extractor->setDataSource(tmp); + + env->ReleaseStringUTFChars(path, tmp); + tmp = NULL; + + if (err != OK) { + jniThrowException( + env, + "java/io/IOException", + "Failed to instantiate extractor."); + return; + } + + setMediaExtractor(env,thiz, extractor); +} + +static void android_media_MediaExtractor_native_finalize( + JNIEnv *env, jobject thiz) { + android_media_MediaExtractor_release(env, thiz); +} + +static JNINativeMethod gMethods[] = { + { "release", "()V", (void *)android_media_MediaExtractor_release }, + + { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks }, + + { "getTrackFormat", "(I)Ljava/util/Map;", + (void *)android_media_MediaExtractor_getTrackFormat }, + + { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack }, + + { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo }, + + { "advance", "()Z", (void *)android_media_MediaExtractor_advance }, + + { "readSampleData", "(Ljava/nio/ByteBuffer;I)I", + (void *)android_media_MediaExtractor_readSampleData }, + + { "getSampleTrackIndex", "()I", + (void *)android_media_MediaExtractor_getSampleTrackIndex }, + + { "getSampleTime", "()J", + (void *)android_media_MediaExtractor_getSampleTime }, + + { "native_init", "()V", (void *)android_media_MediaExtractor_native_init }, + + { "native_setup", "(Ljava/lang/String;)V", + (void *)android_media_MediaExtractor_native_setup }, + + { "native_finalize", "()V", + (void *)android_media_MediaExtractor_native_finalize }, +}; + +int register_android_media_MediaExtractor(JNIEnv *env) { + return AndroidRuntime::registerNativeMethods(env, + "android/media/MediaExtractor", gMethods, NELEM(gMethods)); +} diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h new file mode 100644 index 000000000000..70e58c6c1a26 --- /dev/null +++ b/media/jni/android_media_MediaExtractor.h @@ -0,0 +1,60 @@ +/* + * Copyright 2012, 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_MEDIA_MEDIAEXTRACTOR_H_ +#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <utils/Errors.h> +#include <utils/RefBase.h> + +#include "jni.h" + +namespace android { + +struct NuMediaExtractor; + +struct JMediaExtractor : public RefBase { + JMediaExtractor(JNIEnv *env, jobject thiz); + + status_t setDataSource(const char *path); + + size_t countTracks() const; + status_t getTrackFormat(size_t index, jobject *format) const; + + status_t selectTrack(size_t index); + + status_t seekTo(int64_t timeUs); + + status_t advance(); + status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize); + status_t getSampleTrackIndex(size_t *trackIndex); + status_t getSampleTime(int64_t *sampleTimeUs); + +protected: + virtual ~JMediaExtractor(); + +private: + jclass mClass; + jweak mObject; + sp<NuMediaExtractor> mImpl; + + DISALLOW_EVIL_CONSTRUCTORS(JMediaExtractor); +}; + +} // namespace android + +#endif // _ANDROID_MEDIA_MEDIAEXTRACTOR_H_ diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp index 8ff9dd3fdc02..199d56e469f9 100644 --- a/media/jni/android_media_MediaPlayer.cpp +++ b/media/jni/android_media_MediaPlayer.cpp @@ -810,6 +810,8 @@ static int register_android_media_MediaPlayer(JNIEnv *env) "android/media/MediaPlayer", gMethods, NELEM(gMethods)); } +extern int register_android_media_MediaCodec(JNIEnv *env); +extern int register_android_media_MediaExtractor(JNIEnv *env); extern int register_android_media_MediaMetadataRetriever(JNIEnv *env); extern int register_android_media_MediaRecorder(JNIEnv *env); extern int register_android_media_MediaScanner(JNIEnv *env); @@ -881,6 +883,16 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) goto bail; } + if (register_android_media_MediaCodec(env) < 0) { + ALOGE("ERROR: MediaCodec native registration failed"); + goto bail; + } + + if (register_android_media_MediaExtractor(env) < 0) { + ALOGE("ERROR: MediaCodec native registration failed"); + goto bail; + } + /* success -- return valid version number */ result = JNI_VERSION_1_4; diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp index 47963b101b7d..8b2321c31288 100644 --- a/media/jni/android_media_Utils.cpp +++ b/media/jni/android_media_Utils.cpp @@ -20,6 +20,10 @@ #include <utils/Log.h> #include "android_media_Utils.h" +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> + namespace android { bool ConvertKeyValueArraysToKeyedVector( @@ -71,5 +75,263 @@ bool ConvertKeyValueArraysToKeyedVector( return true; } +static jobject makeIntegerObject(JNIEnv *env, int32_t value) { + jclass clazz = env->FindClass("java/lang/Integer"); + CHECK(clazz != NULL); + + jmethodID integerConstructID = env->GetMethodID(clazz, "<init>", "(I)V"); + CHECK(integerConstructID != NULL); + + return env->NewObject(clazz, integerConstructID, value); +} + +static jobject makeFloatObject(JNIEnv *env, float value) { + jclass clazz = env->FindClass("java/lang/Float"); + CHECK(clazz != NULL); + + jmethodID floatConstructID = env->GetMethodID(clazz, "<init>", "(F)V"); + CHECK(floatConstructID != NULL); + + return env->NewObject(clazz, floatConstructID, value); +} + +static jobject makeByteBufferObject( + JNIEnv *env, const void *data, size_t size) { + jbyteArray byteArrayObj = env->NewByteArray(size); + env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data); + + jclass clazz = env->FindClass("java/nio/ByteBuffer"); + CHECK(clazz != NULL); + + jmethodID byteBufWrapID = + env->GetStaticMethodID(clazz, "wrap", "([B)Ljava/nio/ByteBuffer;"); + CHECK(byteBufWrapID != NULL); + + jobject byteBufObj = env->CallStaticObjectMethod( + clazz, byteBufWrapID, byteArrayObj); + + env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL; + + return byteBufObj; +} + +status_t ConvertMessageToMap( + JNIEnv *env, const sp<AMessage> &msg, jobject *map) { + jclass hashMapClazz = env->FindClass("java/util/HashMap"); + + if (hashMapClazz == NULL) { + return -EINVAL; + } + + jmethodID hashMapConstructID = + env->GetMethodID(hashMapClazz, "<init>", "()V"); + + if (hashMapConstructID == NULL) { + return -EINVAL; + } + + jmethodID hashMapPutID = + env->GetMethodID( + hashMapClazz, + "put", + "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;"); + + if (hashMapPutID == NULL) { + return -EINVAL; + } + + jobject hashMap = env->NewObject(hashMapClazz, hashMapConstructID); + + for (size_t i = 0; i < msg->countEntries(); ++i) { + AMessage::Type valueType; + const char *key = msg->getEntryNameAt(i, &valueType); + + jobject valueObj = NULL; + + switch (valueType) { + case AMessage::kTypeInt32: + { + int32_t val; + CHECK(msg->findInt32(key, &val)); + + valueObj = makeIntegerObject(env, val); + break; + } + + case AMessage::kTypeFloat: + { + float val; + CHECK(msg->findFloat(key, &val)); + + valueObj = makeFloatObject(env, val); + break; + } + + case AMessage::kTypeString: + { + AString val; + CHECK(msg->findString(key, &val)); + + valueObj = env->NewStringUTF(val.c_str()); + break; + } + + case AMessage::kTypeBuffer: + { + sp<ABuffer> buffer; + CHECK(msg->findBuffer(key, &buffer)); + + valueObj = makeByteBufferObject( + env, buffer->data(), buffer->size()); + break; + } + + default: + break; + } + + if (valueObj != NULL) { + jstring keyObj = env->NewStringUTF(key); + + jobject res = env->CallObjectMethod( + hashMap, hashMapPutID, keyObj, valueObj); + + env->DeleteLocalRef(keyObj); keyObj = NULL; + env->DeleteLocalRef(valueObj); valueObj = NULL; + } + } + + *map = hashMap; + + return OK; +} + +status_t ConvertKeyValueArraysToMessage( + JNIEnv *env, jobjectArray keys, jobjectArray values, + sp<AMessage> *out) { + jclass stringClass = env->FindClass("java/lang/String"); + CHECK(stringClass != NULL); + + jclass integerClass = env->FindClass("java/lang/Integer"); + CHECK(integerClass != NULL); + + jclass floatClass = env->FindClass("java/lang/Float"); + CHECK(floatClass != NULL); + + jclass byteBufClass = env->FindClass("java/nio/ByteBuffer"); + CHECK(byteBufClass != NULL); + + sp<AMessage> msg = new AMessage; + + jsize numEntries = 0; + + if (keys != NULL) { + if (values == NULL) { + return -EINVAL; + } + + numEntries = env->GetArrayLength(keys); + + if (numEntries != env->GetArrayLength(values)) { + return -EINVAL; + } + } else if (values != NULL) { + return -EINVAL; + } + + for (jsize i = 0; i < numEntries; ++i) { + jobject keyObj = env->GetObjectArrayElement(keys, i); + + if (!env->IsInstanceOf(keyObj, stringClass)) { + return -EINVAL; + } + + const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL); + + if (tmp == NULL) { + return -ENOMEM; + } + + AString key = tmp; + + env->ReleaseStringUTFChars((jstring)keyObj, tmp); + tmp = NULL; + + jobject valueObj = env->GetObjectArrayElement(values, i); + + if (env->IsInstanceOf(valueObj, stringClass)) { + const char *value = env->GetStringUTFChars((jstring)valueObj, NULL); + + if (value == NULL) { + return -ENOMEM; + } + + msg->setString(key.c_str(), value); + + env->ReleaseStringUTFChars((jstring)valueObj, value); + value = NULL; + } else if (env->IsInstanceOf(valueObj, integerClass)) { + jmethodID intValueID = + env->GetMethodID(integerClass, "intValue", "()I"); + CHECK(intValueID != NULL); + + jint value = env->CallIntMethod(valueObj, intValueID); + + msg->setInt32(key.c_str(), value); + } else if (env->IsInstanceOf(valueObj, floatClass)) { + jmethodID floatValueID = + env->GetMethodID(floatClass, "floatValue", "()F"); + CHECK(floatValueID != NULL); + + jfloat value = env->CallFloatMethod(valueObj, floatValueID); + + msg->setFloat(key.c_str(), value); + } else if (env->IsInstanceOf(valueObj, byteBufClass)) { + jmethodID positionID = + env->GetMethodID(byteBufClass, "position", "()I"); + CHECK(positionID != NULL); + + jmethodID limitID = + env->GetMethodID(byteBufClass, "limit", "()I"); + CHECK(limitID != NULL); + + jint position = env->CallIntMethod(valueObj, positionID); + jint limit = env->CallIntMethod(valueObj, limitID); + + sp<ABuffer> buffer = new ABuffer(limit - position); + + void *data = env->GetDirectBufferAddress(valueObj); + + if (data != NULL) { + memcpy(buffer->data(), + (const uint8_t *)data + position, + buffer->size()); + } else { + jmethodID arrayID = + env->GetMethodID(byteBufClass, "array", "()[B"); + CHECK(arrayID != NULL); + + jbyteArray byteArray = + (jbyteArray)env->CallObjectMethod(valueObj, arrayID); + CHECK(byteArray != NULL); + + env->GetByteArrayRegion( + byteArray, + position, + buffer->size(), + (jbyte *)buffer->data()); + + env->DeleteLocalRef(byteArray); byteArray = NULL; + } + + msg->setObject(key.c_str(), buffer); + } + } + + *out = msg; + + return OK; +} + } // namespace android diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h index a2c155adfc7a..635bceb2fddf 100644 --- a/media/jni/android_media_Utils.h +++ b/media/jni/android_media_Utils.h @@ -33,6 +33,14 @@ bool ConvertKeyValueArraysToKeyedVector( JNIEnv *env, jobjectArray keys, jobjectArray values, KeyedVector<String8, String8>* vector); +struct AMessage; +status_t ConvertMessageToMap( + JNIEnv *env, const sp<AMessage> &msg, jobject *map); + +status_t ConvertKeyValueArraysToMessage( + JNIEnv *env, jobjectArray keys, jobjectArray values, + sp<AMessage> *msg); + }; // namespace android #endif // _ANDROID_MEDIA_UTILS_H_ diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp index ecd4d076aaed..f015afbaf209 100644 --- a/media/jni/audioeffect/android_media_Visualizer.cpp +++ b/media/jni/audioeffect/android_media_Visualizer.cpp @@ -23,6 +23,7 @@ #include <nativehelper/jni.h> #include <nativehelper/JNIHelp.h> #include <android_runtime/AndroidRuntime.h> +#include <utils/threads.h> #include "media/Visualizer.h" using namespace android; @@ -38,6 +39,7 @@ using namespace android; #define NATIVE_EVENT_PCM_CAPTURE 0 #define NATIVE_EVENT_FFT_CAPTURE 1 +#define NATIVE_EVENT_SERVER_DIED 2 // ---------------------------------------------------------------------------- static const char* const kClassPathName = "android/media/audiofx/Visualizer"; @@ -54,6 +56,43 @@ static fields_t fields; struct visualizer_callback_cookie { jclass visualizer_class; // Visualizer class jobject visualizer_ref; // Visualizer object instance + + // Lazily allocated arrays used to hold callback data provided to java + // applications. These arrays are allocated during the first callback and + // reallocated when the size of the callback data changes. Allocating on + // demand and saving the arrays means that applications cannot safely hold a + // reference to the provided data (they need to make a copy if they want to + // hold onto outside of the callback scope), but it avoids GC thrash caused + // by constantly allocating and releasing arrays to hold callback data. + Mutex callback_data_lock; + jbyteArray waveform_data; + jbyteArray fft_data; + + visualizer_callback_cookie() { + waveform_data = NULL; + fft_data = NULL; + } + + ~visualizer_callback_cookie() { + cleanupBuffers(); + } + + void cleanupBuffers() { + AutoMutex lock(&callback_data_lock); + if (waveform_data || fft_data) { + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + if (waveform_data) { + env->DeleteGlobalRef(waveform_data); + waveform_data = NULL; + } + + if (fft_data) { + env->DeleteGlobalRef(fft_data); + fft_data = NULL; + } + } + } }; // ---------------------------------------------------------------------------- @@ -66,7 +105,6 @@ class visualizerJniStorage { ~visualizerJniStorage() { } - }; @@ -93,6 +131,26 @@ static jint translateError(int code) { // ---------------------------------------------------------------------------- +static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) { + if (NULL != *array) { + uint32_t len = env->GetArrayLength(*array); + if (len == size) + return; + + env->DeleteGlobalRef(*array); + *array = NULL; + } + + jbyteArray localRef = env->NewByteArray(size); + if (NULL != localRef) { + // Promote to global ref. + *array = (jbyteArray)env->NewGlobalRef(localRef); + + // Release our (now pointless) local ref. + env->DeleteLocalRef(localRef); + } +} + static void captureCallback(void* user, uint32_t waveformSize, uint8_t *waveform, @@ -106,6 +164,7 @@ static void captureCallback(void* user, visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user; JNIEnv *env = AndroidRuntime::getJNIEnv(); + AutoMutex lock(&callbackInfo->callback_data_lock); ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p", callbackInfo, @@ -118,7 +177,11 @@ static void captureCallback(void* user, } if (waveformSize != 0 && waveform != NULL) { - jbyteArray jArray = env->NewByteArray(waveformSize); + jbyteArray jArray; + + ensureArraySize(env, &callbackInfo->waveform_data, waveformSize); + jArray = callbackInfo->waveform_data; + if (jArray != NULL) { jbyte *nArray = env->GetByteArrayElements(jArray, NULL); memcpy(nArray, waveform, waveformSize); @@ -131,12 +194,15 @@ static void captureCallback(void* user, samplingrate, 0, jArray); - env->DeleteLocalRef(jArray); } } if (fftSize != 0 && fft != NULL) { - jbyteArray jArray = env->NewByteArray(fftSize); + jbyteArray jArray; + + ensureArraySize(env, &callbackInfo->fft_data, fftSize); + jArray = callbackInfo->fft_data; + if (jArray != NULL) { jbyte *nArray = env->GetByteArrayElements(jArray, NULL); memcpy(nArray, fft, fftSize); @@ -149,7 +215,6 @@ static void captureCallback(void* user, samplingrate, 0, jArray); - env->DeleteLocalRef(jArray); } } @@ -220,6 +285,23 @@ android_media_visualizer_native_init(JNIEnv *env) } +static void android_media_visualizer_effect_callback(int32_t event, + void *user, + void *info) { + if ((event == AudioEffect::EVENT_ERROR) && + (*((status_t*)info) == DEAD_OBJECT)) { + visualizerJniStorage* lpJniStorage = (visualizerJniStorage*)user; + visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData; + JNIEnv *env = AndroidRuntime::getJNIEnv(); + + env->CallStaticVoidMethod( + callbackInfo->visualizer_class, + fields.midPostNativeEvent, + callbackInfo->visualizer_ref, + NATIVE_EVENT_SERVER_DIED, + 0, 0, 0); + } +} static jint android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this, @@ -255,8 +337,8 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th // create the native Visualizer object lpVisualizer = new Visualizer(0, - NULL, - NULL, + android_media_visualizer_effect_callback, + lpJniStorage, sessionId); if (lpVisualizer == NULL) { ALOGE("Error creating Visualizer"); @@ -345,7 +427,17 @@ android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean e return VISUALIZER_ERROR_NO_INIT; } - return translateError(lpVisualizer->setEnabled(enabled)); + jint retVal = translateError(lpVisualizer->setEnabled(enabled)); + + if (!enabled) { + visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField( + thiz, fields.fidJniData); + + if (NULL != lpJniStorage) + lpJniStorage->mCallbackData.cleanupBuffers(); + } + + return retVal; } static jboolean diff --git a/media/libaah_rtp/Android.mk b/media/libaah_rtp/Android.mk new file mode 100644 index 000000000000..54fd9ec10ebd --- /dev/null +++ b/media/libaah_rtp/Android.mk @@ -0,0 +1,40 @@ +LOCAL_PATH:= $(call my-dir) +# +# libaah_rtp +# + +include $(CLEAR_VARS) + +LOCAL_MODULE := libaah_rtp +LOCAL_MODULE_TAGS := optional + +LOCAL_SRC_FILES := \ + aah_decoder_pump.cpp \ + aah_rx_player.cpp \ + aah_rx_player_core.cpp \ + aah_rx_player_ring_buffer.cpp \ + aah_rx_player_substream.cpp \ + aah_tx_packet.cpp \ + aah_tx_player.cpp \ + aah_tx_sender.cpp \ + pipe_event.cpp + +LOCAL_C_INCLUDES := \ + frameworks/base/include \ + frameworks/base/include/media/stagefright/openmax \ + frameworks/base/media \ + frameworks/base/media/libstagefright + +LOCAL_SHARED_LIBRARIES := \ + libcommon_time_client \ + libbinder \ + libmedia \ + libstagefright \ + libstagefright_foundation \ + libutils + +LOCAL_LDLIBS := \ + -lpthread + +include $(BUILD_SHARED_LIBRARY) + diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp new file mode 100644 index 000000000000..72fe43baf5d9 --- /dev/null +++ b/media/libaah_rtp/aah_decoder_pump.cpp @@ -0,0 +1,520 @@ +/* + * 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 "LibAAH_RTP" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <poll.h> +#include <pthread.h> + +#include <common_time/cc_helper.h> +#include <media/AudioSystem.h> +#include <media/AudioTrack.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> +#include <media/stagefright/Utils.h> +#include <utils/Timers.h> +#include <utils/threads.h> + +#include "aah_decoder_pump.h" + +namespace android { + +static const long long kLongDecodeErrorThreshold = 1000000ll; +static const uint32_t kMaxLongErrorsBeforeFatal = 3; +static const uint32_t kMaxErrorsBeforeFatal = 60; + +AAH_DecoderPump::AAH_DecoderPump(OMXClient& omx) + : omx_(omx) + , thread_status_(OK) + , renderer_(NULL) + , last_queued_pts_valid_(false) + , last_queued_pts_(0) + , last_ts_transform_valid_(false) + , last_volume_(0xFF) { + thread_ = new ThreadWrapper(this); +} + +AAH_DecoderPump::~AAH_DecoderPump() { + shutdown(); +} + +status_t AAH_DecoderPump::initCheck() { + if (thread_ == NULL) { + ALOGE("Failed to allocate thread"); + return NO_MEMORY; + } + + return OK; +} + +status_t AAH_DecoderPump::queueForDecode(MediaBuffer* buf) { + if (NULL == buf) { + return BAD_VALUE; + } + + if (OK != thread_status_) { + return thread_status_; + } + + { // Explicit scope for AutoMutex pattern. + AutoMutex lock(&thread_lock_); + in_queue_.push_back(buf); + } + + thread_cond_.signal(); + + return OK; +} + +void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) { + Mutex::Autolock lock(&render_lock_); + sp<MetaData> meta; + int64_t ts; + status_t res; + + // Fetch the metadata and make sure the sample has a timestamp. We + // cannot render samples which are missing PTSs. + meta = decoded_sample->meta_data(); + if ((meta == NULL) || (!meta->findInt64(kKeyTime, &ts))) { + ALOGV("Decoded sample missing timestamp, cannot render."); + CHECK(false); + } else { + // If we currently are not holding on to a renderer, go ahead and + // make one now. + if (NULL == renderer_) { + renderer_ = new TimedAudioTrack(); + if (NULL != renderer_) { + int frameCount; + AudioTrack::getMinFrameCount(&frameCount, + AUDIO_STREAM_DEFAULT, + static_cast<int>(format_sample_rate_)); + int ch_format = (format_channels_ == 1) + ? AUDIO_CHANNEL_OUT_MONO + : AUDIO_CHANNEL_OUT_STEREO; + + res = renderer_->set(AUDIO_STREAM_DEFAULT, + format_sample_rate_, + AUDIO_FORMAT_PCM_16_BIT, + ch_format, + frameCount); + if (res != OK) { + ALOGE("Failed to setup audio renderer. (res = %d)", res); + delete renderer_; + renderer_ = NULL; + } else { + CHECK(last_ts_transform_valid_); + + res = renderer_->setMediaTimeTransform( + last_ts_transform_, TimedAudioTrack::COMMON_TIME); + if (res != NO_ERROR) { + ALOGE("Failed to set media time transform on AudioTrack" + " (res = %d)", res); + delete renderer_; + renderer_ = NULL; + } else { + float volume = static_cast<float>(last_volume_) + / 255.0f; + if (renderer_->setVolume(volume, volume) != OK) { + ALOGW("%s: setVolume failed", __FUNCTION__); + } + + renderer_->start(); + } + } + } else { + ALOGE("Failed to allocate AudioTrack to use as a renderer."); + } + } + + if (NULL != renderer_) { + uint8_t* decoded_data = + reinterpret_cast<uint8_t*>(decoded_sample->data()); + uint32_t decoded_amt = decoded_sample->range_length(); + decoded_data += decoded_sample->range_offset(); + + sp<IMemory> pcm_payload; + res = renderer_->allocateTimedBuffer(decoded_amt, &pcm_payload); + if (res != OK) { + ALOGE("Failed to allocate %d byte audio track buffer." + " (res = %d)", decoded_amt, res); + } else { + memcpy(pcm_payload->pointer(), decoded_data, decoded_amt); + + res = renderer_->queueTimedBuffer(pcm_payload, ts); + if (res != OK) { + ALOGE("Failed to queue %d byte audio track buffer with media" + " PTS %lld. (res = %d)", decoded_amt, ts, res); + } else { + last_queued_pts_valid_ = true; + last_queued_pts_ = ts; + } + } + + } else { + ALOGE("No renderer, dropping audio payload."); + } + } +} + +void AAH_DecoderPump::stopAndCleanupRenderer() { + if (NULL == renderer_) { + return; + } + + renderer_->stop(); + delete renderer_; + renderer_ = NULL; +} + +void AAH_DecoderPump::setRenderTSTransform(const LinearTransform& trans) { + Mutex::Autolock lock(&render_lock_); + + if (last_ts_transform_valid_ && !memcmp(&trans, + &last_ts_transform_, + sizeof(trans))) { + return; + } + + last_ts_transform_ = trans; + last_ts_transform_valid_ = true; + + if (NULL != renderer_) { + status_t res = renderer_->setMediaTimeTransform( + last_ts_transform_, TimedAudioTrack::COMMON_TIME); + if (res != NO_ERROR) { + ALOGE("Failed to set media time transform on AudioTrack" + " (res = %d)", res); + } + } +} + +void AAH_DecoderPump::setRenderVolume(uint8_t volume) { + Mutex::Autolock lock(&render_lock_); + + if (volume == last_volume_) { + return; + } + + last_volume_ = volume; + if (renderer_ != NULL) { + float volume = static_cast<float>(last_volume_) / 255.0f; + if (renderer_->setVolume(volume, volume) != OK) { + ALOGW("%s: setVolume failed", __FUNCTION__); + } + } +} + +// isAboutToUnderflow is something of a hack used to figure out when it might be +// time to give up on trying to fill in a gap in the RTP sequence and simply +// move on with a discontinuity. If we had perfect knowledge of when we were +// going to underflow, it would not be a hack, but unfortunately we do not. +// Right now, we just take the PTS of the last sample queued, and check to see +// if its presentation time is within kAboutToUnderflowThreshold from now. If +// it is, then we say that we are about to underflow. This decision is based on +// two (possibly invalid) assumptions. +// +// 1) The transmitter is leading the clock by more than +// kAboutToUnderflowThreshold. +// 2) The delta between the PTS of the last sample queued and the next sample +// is less than the transmitter's clock lead amount. +// +// Right now, the default transmitter lead time is 1 second, which is a pretty +// large number and greater than the 50mSec that kAboutToUnderflowThreshold is +// currently set to. This should satisfy assumption #1 for now, but changes to +// the transmitter clock lead time could effect this. +// +// For non-sparse streams with a homogeneous sample rate (the vast majority of +// streams in the world), the delta between any two adjacent PTSs will always be +// the homogeneous sample period. It is very uncommon to see a sample period +// greater than the 1 second clock lead we are currently using, and you +// certainly will not see it in an MP3 file which should satisfy assumption #2. +// Sparse audio streams (where no audio is transmitted for long periods of +// silence) and extremely low framerate video stream (like an MPEG-2 slideshow +// or the video stream for a pay TV audio channel) are examples of streams which +// might violate assumption #2. +bool AAH_DecoderPump::isAboutToUnderflow(int64_t threshold) { + Mutex::Autolock lock(&render_lock_); + + // If we have never queued anything to the decoder, we really don't know if + // we are going to underflow or not. + if (!last_queued_pts_valid_ || !last_ts_transform_valid_) { + return false; + } + + // Don't have access to Common Time? If so, then things are Very Bad + // elsewhere in the system; it pretty much does not matter what we do here. + // Since we cannot really tell if we are about to underflow or not, its + // probably best to assume that we are not and proceed accordingly. + int64_t tt_now; + if (OK != cc_helper_.getCommonTime(&tt_now)) { + return false; + } + + // Transform from media time to common time. + int64_t last_queued_pts_tt; + if (!last_ts_transform_.doForwardTransform(last_queued_pts_, + &last_queued_pts_tt)) { + return false; + } + + // Check to see if we are underflowing. + return ((tt_now + threshold - last_queued_pts_tt) > 0); +} + +void* AAH_DecoderPump::workThread() { + // No need to lock when accessing decoder_ from the thread. The + // implementation of init and shutdown ensure that other threads never touch + // decoder_ while the work thread is running. + CHECK(decoder_ != NULL); + CHECK(format_ != NULL); + + // Start the decoder and note its result code. If something goes horribly + // wrong, callers of queueForDecode and getOutput will be able to detect + // that the thread encountered a fatal error and shut down by examining + // thread_status_. + thread_status_ = decoder_->start(format_.get()); + if (OK != thread_status_) { + ALOGE("AAH_DecoderPump's work thread failed to start decoder (res = %d)", + thread_status_); + return NULL; + } + + DurationTimer decode_timer; + uint32_t consecutive_long_errors = 0; + uint32_t consecutive_errors = 0; + + while (!thread_->exitPending()) { + status_t res; + MediaBuffer* bufOut = NULL; + + decode_timer.start(); + res = decoder_->read(&bufOut); + decode_timer.stop(); + + if (res == INFO_FORMAT_CHANGED) { + // Format has changed. Destroy our current renderer so that a new + // one can be created during queueToRenderer with the proper format. + // + // TODO : In order to transition seamlessly, we should change this + // to put the old renderer in a queue to play out completely before + // we destroy it. We can still create a new renderer, the timed + // nature of the renderer should ensure a seamless splice. + stopAndCleanupRenderer(); + res = OK; + } + + // Try to be a little nuanced in our handling of actual decode errors. + // Errors could happen because of minor stream corruption or because of + // transient resource limitations. In these cases, we would rather drop + // a little bit of output and ride out the unpleasantness then throw up + // our hands and abort everything. + // + // OTOH - When things are really bad (like we have a non-transient + // resource or bookkeeping issue, or the stream being fed to us is just + // complete and total garbage) we really want to terminate playback and + // raise an error condition all the way up to the application level so + // they can deal with it. + // + // Unfortunately, the error codes returned by the decoder can be a + // little non-specific. For example, if an OMXCodec times out + // attempting to obtain an output buffer, the error we get back is a + // generic -1. Try to distinguish between this resource timeout error + // and ES corruption error by timing how long the decode operation + // takes. Maintain accounting for both errors and "long errors". If we + // get more than a certain number consecutive errors of either type, + // consider it fatal and shutdown (which will cause the error to + // propagate all of the way up to the application level). The threshold + // for "long errors" is deliberately much lower than that of normal + // decode errors, both because of how long they take to happen and + // because they generally indicate resource limitation errors which are + // unlikely to go away in pathologically bad cases (in contrast to + // stream corruption errors which might happen 20 times in a row and + // then be suddenly OK again) + if (res != OK) { + consecutive_errors++; + if (decode_timer.durationUsecs() >= kLongDecodeErrorThreshold) + consecutive_long_errors++; + + CHECK(NULL == bufOut); + + ALOGW("%s: Failed to decode data (res = %d)", + __PRETTY_FUNCTION__, res); + + if ((consecutive_errors >= kMaxErrorsBeforeFatal) || + (consecutive_long_errors >= kMaxLongErrorsBeforeFatal)) { + ALOGE("%s: Maximum decode error threshold has been reached." + " There have been %d consecutive decode errors, and %d" + " consecutive decode operations which resulted in errors" + " and took more than %lld uSec to process. The last" + " decode operation took %lld uSec.", + __PRETTY_FUNCTION__, + consecutive_errors, consecutive_long_errors, + kLongDecodeErrorThreshold, decode_timer.durationUsecs()); + thread_status_ = res; + break; + } + + continue; + } + + if (NULL == bufOut) { + ALOGW("%s: Successful decode, but no buffer produced", + __PRETTY_FUNCTION__); + continue; + } + + // Successful decode (with actual output produced). Clear the error + // counters. + consecutive_errors = 0; + consecutive_long_errors = 0; + + queueToRenderer(bufOut); + bufOut->release(); + } + + decoder_->stop(); + stopAndCleanupRenderer(); + + return NULL; +} + +status_t AAH_DecoderPump::init(const sp<MetaData>& params) { + Mutex::Autolock lock(&init_lock_); + + if (decoder_ != NULL) { + // already inited + return OK; + } + + if (params == NULL) { + return BAD_VALUE; + } + + if (!params->findInt32(kKeyChannelCount, &format_channels_)) { + return BAD_VALUE; + } + + if (!params->findInt32(kKeySampleRate, &format_sample_rate_)) { + return BAD_VALUE; + } + + CHECK(OK == thread_status_); + CHECK(decoder_ == NULL); + + status_t ret_val = UNKNOWN_ERROR; + + // Cache the format and attempt to create the decoder. + format_ = params; + decoder_ = OMXCodec::Create( + omx_.interface(), // IOMX Handle + format_, // Metadata for substream (indicates codec) + false, // Make a decoder, not an encoder + sp<MediaSource>(this)); // We will be the source for this codec. + + if (decoder_ == NULL) { + ALOGE("Failed to allocate decoder in %s", __PRETTY_FUNCTION__); + goto bailout; + } + + // Fire up the pump thread. It will take care of starting and stopping the + // decoder. + ret_val = thread_->run("aah_decode_pump", ANDROID_PRIORITY_AUDIO); + if (OK != ret_val) { + ALOGE("Failed to start work thread in %s (res = %d)", + __PRETTY_FUNCTION__, ret_val); + goto bailout; + } + +bailout: + if (OK != ret_val) { + decoder_ = NULL; + format_ = NULL; + } + + return OK; +} + +status_t AAH_DecoderPump::shutdown() { + Mutex::Autolock lock(&init_lock_); + return shutdown_l(); +} + +status_t AAH_DecoderPump::shutdown_l() { + thread_->requestExit(); + thread_cond_.signal(); + thread_->requestExitAndWait(); + + for (MBQueue::iterator iter = in_queue_.begin(); + iter != in_queue_.end(); + ++iter) { + (*iter)->release(); + } + in_queue_.clear(); + + last_queued_pts_valid_ = false; + last_ts_transform_valid_ = false; + last_volume_ = 0xFF; + thread_status_ = OK; + + decoder_ = NULL; + format_ = NULL; + + return OK; +} + +status_t AAH_DecoderPump::read(MediaBuffer **buffer, + const ReadOptions *options) { + if (!buffer) { + return BAD_VALUE; + } + + *buffer = NULL; + + // While its not time to shut down, and we have no data to process, wait. + AutoMutex lock(&thread_lock_); + while (!thread_->exitPending() && in_queue_.empty()) + thread_cond_.wait(thread_lock_); + + // At this point, if its not time to shutdown then we must have something to + // process. Go ahead and pop the front of the queue for processing. + if (!thread_->exitPending()) { + CHECK(!in_queue_.empty()); + + *buffer = *(in_queue_.begin()); + in_queue_.erase(in_queue_.begin()); + } + + // If we managed to get a buffer, then everything must be OK. If not, then + // we must be shutting down. + return (NULL == *buffer) ? INVALID_OPERATION : OK; +} + +AAH_DecoderPump::ThreadWrapper::ThreadWrapper(AAH_DecoderPump* owner) + : Thread(false /* canCallJava*/ ) + , owner_(owner) { +} + +bool AAH_DecoderPump::ThreadWrapper::threadLoop() { + CHECK(NULL != owner_); + owner_->workThread(); + return false; +} + +} // namespace android diff --git a/media/libaah_rtp/aah_decoder_pump.h b/media/libaah_rtp/aah_decoder_pump.h new file mode 100644 index 000000000000..f5a6529396be --- /dev/null +++ b/media/libaah_rtp/aah_decoder_pump.h @@ -0,0 +1,107 @@ +/* + * 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 __DECODER_PUMP_H__ +#define __DECODER_PUMP_H__ + +#include <pthread.h> + +#include <common_time/cc_helper.h> +#include <media/stagefright/MediaSource.h> +#include <utils/LinearTransform.h> +#include <utils/List.h> +#include <utils/threads.h> + +namespace android { + +class MetaData; +class OMXClient; +class TimedAudioTrack; + +class AAH_DecoderPump : public MediaSource { + public: + explicit AAH_DecoderPump(OMXClient& omx); + status_t initCheck(); + + status_t queueForDecode(MediaBuffer* buf); + + status_t init(const sp<MetaData>& params); + status_t shutdown(); + + void setRenderTSTransform(const LinearTransform& trans); + void setRenderVolume(uint8_t volume); + bool isAboutToUnderflow(int64_t threshold); + bool getStatus() const { return thread_status_; } + + // MediaSource methods + virtual status_t start(MetaData *params) { return OK; } + virtual sp<MetaData> getFormat() { return format_; } + virtual status_t stop() { return OK; } + virtual status_t read(MediaBuffer **buffer, + const ReadOptions *options); + + protected: + virtual ~AAH_DecoderPump(); + + private: + class ThreadWrapper : public Thread { + public: + friend class AAH_DecoderPump; + explicit ThreadWrapper(AAH_DecoderPump* owner); + + private: + virtual bool threadLoop(); + AAH_DecoderPump* owner_; + + DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper); + }; + + void* workThread(); + virtual status_t shutdown_l(); + void queueToRenderer(MediaBuffer* decoded_sample); + void stopAndCleanupRenderer(); + + sp<MetaData> format_; + int32_t format_channels_; + int32_t format_sample_rate_; + + sp<MediaSource> decoder_; + OMXClient& omx_; + Mutex init_lock_; + + sp<ThreadWrapper> thread_; + Condition thread_cond_; + Mutex thread_lock_; + status_t thread_status_; + + Mutex render_lock_; + TimedAudioTrack* renderer_; + bool last_queued_pts_valid_; + int64_t last_queued_pts_; + bool last_ts_transform_valid_; + LinearTransform last_ts_transform_; + uint8_t last_volume_; + CCHelper cc_helper_; + + // protected by the thread_lock_ + typedef List<MediaBuffer*> MBQueue; + MBQueue in_queue_; + + DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPump); +}; + +} // namespace android +#endif // __DECODER_PUMP_H__ diff --git a/media/libaah_rtp/aah_rx_player.cpp b/media/libaah_rtp/aah_rx_player.cpp new file mode 100644 index 000000000000..9dd79fd2dd83 --- /dev/null +++ b/media/libaah_rtp/aah_rx_player.cpp @@ -0,0 +1,288 @@ +/* + * 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 "LibAAH_RTP" +//#define LOG_NDEBUG 0 + +#include <binder/IServiceManager.h> +#include <media/MediaPlayerInterface.h> +#include <utils/Log.h> + +#include "aah_rx_player.h" + +namespace android { + +const uint32_t AAH_RXPlayer::kRTPRingBufferSize = 1 << 10; + +sp<MediaPlayerBase> createAAH_RXPlayer() { + sp<MediaPlayerBase> ret = new AAH_RXPlayer(); + return ret; +} + +AAH_RXPlayer::AAH_RXPlayer() + : ring_buffer_(kRTPRingBufferSize) + , substreams_(NULL) { + thread_wrapper_ = new ThreadWrapper(*this); + + is_playing_ = false; + multicast_joined_ = false; + transmitter_known_ = false; + current_epoch_known_ = false; + data_source_set_ = false; + sock_fd_ = -1; + + substreams_.setCapacity(4); + + memset(&listen_addr_, 0, sizeof(listen_addr_)); + memset(&transmitter_addr_, 0, sizeof(transmitter_addr_)); + + fetchAudioFlinger(); +} + +AAH_RXPlayer::~AAH_RXPlayer() { + reset_l(); + CHECK(substreams_.size() == 0); + omx_.disconnect(); +} + +status_t AAH_RXPlayer::initCheck() { + if (thread_wrapper_ == NULL) { + ALOGE("Failed to allocate thread wrapper!"); + return NO_MEMORY; + } + + if (!ring_buffer_.initCheck()) { + ALOGE("Failed to allocate reassembly ring buffer!"); + return NO_MEMORY; + } + + // Check for the presense of the common time service by attempting to query + // for CommonTime's frequency. If we get an error back, we cannot talk to + // the service at all and should abort now. + status_t res; + uint64_t freq; + res = cc_helper_.getCommonFreq(&freq); + if (OK != res) { + ALOGE("Failed to connect to common time service!"); + return res; + } + + return omx_.connect(); +} + +status_t AAH_RXPlayer::setDataSource( + const char *url, + const KeyedVector<String8, String8> *headers) { + AutoMutex api_lock(&api_lock_); + uint32_t a, b, c, d; + uint16_t port; + + if (data_source_set_) { + return INVALID_OPERATION; + } + + if (NULL == url) { + return BAD_VALUE; + } + + if (5 != sscanf(url, "%*[^:/]://%u.%u.%u.%u:%hu", &a, &b, &c, &d, &port)) { + ALOGE("Failed to parse URL \"%s\"", url); + return BAD_VALUE; + } + + if ((a > 255) || (b > 255) || (c > 255) || (d > 255) || (port == 0)) { + ALOGE("Bad multicast address \"%s\"", url); + return BAD_VALUE; + } + + ALOGI("setDataSource :: %u.%u.%u.%u:%hu", a, b, c, d, port); + + a = (a << 24) | (b << 16) | (c << 8) | d; + + memset(&listen_addr_, 0, sizeof(listen_addr_)); + listen_addr_.sin_family = AF_INET; + listen_addr_.sin_port = htons(port); + listen_addr_.sin_addr.s_addr = htonl(a); + data_source_set_ = true; + + return OK; +} + +status_t AAH_RXPlayer::setDataSource(int fd, int64_t offset, int64_t length) { + return INVALID_OPERATION; +} + +status_t AAH_RXPlayer::setVideoSurface(const sp<Surface>& surface) { + return OK; +} + +status_t AAH_RXPlayer::setVideoSurfaceTexture( + const sp<ISurfaceTexture>& surfaceTexture) { + return OK; +} + +status_t AAH_RXPlayer::prepare() { + return OK; +} + +status_t AAH_RXPlayer::prepareAsync() { + sendEvent(MEDIA_PREPARED); + return OK; +} + +status_t AAH_RXPlayer::start() { + AutoMutex api_lock(&api_lock_); + + if (is_playing_) { + return OK; + } + + status_t res = startWorkThread(); + is_playing_ = (res == OK); + return res; +} + +status_t AAH_RXPlayer::stop() { + return pause(); +} + +status_t AAH_RXPlayer::pause() { + AutoMutex api_lock(&api_lock_); + stopWorkThread(); + CHECK(sock_fd_ < 0); + is_playing_ = false; + return OK; +} + +bool AAH_RXPlayer::isPlaying() { + AutoMutex api_lock(&api_lock_); + return is_playing_; +} + +status_t AAH_RXPlayer::seekTo(int msec) { + sendEvent(MEDIA_SEEK_COMPLETE); + return OK; +} + +status_t AAH_RXPlayer::getCurrentPosition(int *msec) { + if (NULL != msec) { + *msec = 0; + } + return OK; +} + +status_t AAH_RXPlayer::getDuration(int *msec) { + if (NULL != msec) { + *msec = 1; + } + return OK; +} + +status_t AAH_RXPlayer::reset() { + AutoMutex api_lock(&api_lock_); + reset_l(); + return OK; +} + +void AAH_RXPlayer::reset_l() { + stopWorkThread(); + CHECK(sock_fd_ < 0); + CHECK(!multicast_joined_); + is_playing_ = false; + data_source_set_ = false; + transmitter_known_ = false; + memset(&listen_addr_, 0, sizeof(listen_addr_)); +} + +status_t AAH_RXPlayer::setLooping(int loop) { + return OK; +} + +player_type AAH_RXPlayer::playerType() { + return AAH_RX_PLAYER; +} + +status_t AAH_RXPlayer::setParameter(int key, const Parcel &request) { + return ERROR_UNSUPPORTED; +} + +status_t AAH_RXPlayer::getParameter(int key, Parcel *reply) { + return ERROR_UNSUPPORTED; +} + +status_t AAH_RXPlayer::invoke(const Parcel& request, Parcel *reply) { + if (!reply) { + return BAD_VALUE; + } + + int32_t magic; + status_t err = request.readInt32(&magic); + if (err != OK) { + reply->writeInt32(err); + return OK; + } + + if (magic != 0x12345) { + reply->writeInt32(BAD_VALUE); + return OK; + } + + int32_t methodID; + err = request.readInt32(&methodID); + if (err != OK) { + reply->writeInt32(err); + return OK; + } + + switch (methodID) { + // Get Volume + case INVOKE_GET_MASTER_VOLUME: { + if (audio_flinger_ != NULL) { + reply->writeInt32(OK); + reply->writeFloat(audio_flinger_->masterVolume()); + } else { + reply->writeInt32(UNKNOWN_ERROR); + } + } break; + + // Set Volume + case INVOKE_SET_MASTER_VOLUME: { + float targetVol = request.readFloat(); + reply->writeInt32(audio_flinger_->setMasterVolume(targetVol)); + } break; + + default: return BAD_VALUE; + } + + return OK; +} + +void AAH_RXPlayer::fetchAudioFlinger() { + if (audio_flinger_ == NULL) { + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder; + binder = sm->getService(String16("media.audio_flinger")); + + if (binder == NULL) { + ALOGW("AAH_RXPlayer failed to fetch handle to audio flinger." + " Master volume control will not be possible."); + } + + audio_flinger_ = interface_cast<IAudioFlinger>(binder); + } +} + +} // namespace android diff --git a/media/libaah_rtp/aah_rx_player.h b/media/libaah_rtp/aah_rx_player.h new file mode 100644 index 000000000000..7a1b6e3a7efa --- /dev/null +++ b/media/libaah_rtp/aah_rx_player.h @@ -0,0 +1,313 @@ +/* + * 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 __AAH_RX_PLAYER_H__ +#define __AAH_RX_PLAYER_H__ + +#include <common_time/cc_helper.h> +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXClient.h> +#include <netinet/in.h> +#include <utils/KeyedVector.h> +#include <utils/LinearTransform.h> +#include <utils/threads.h> + +#include "aah_decoder_pump.h" +#include "pipe_event.h" + +namespace android { + +class AAH_RXPlayer : public MediaPlayerInterface { + public: + AAH_RXPlayer(); + + virtual status_t initCheck(); + virtual status_t setDataSource(const char *url, + const KeyedVector<String8, String8>* + headers); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setVideoSurface(const sp<Surface>& surface); + virtual status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& + surfaceTexture); + virtual status_t prepare(); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t pause(); + virtual bool isPlaying(); + virtual status_t seekTo(int msec); + virtual status_t getCurrentPosition(int *msec); + virtual status_t getDuration(int *msec); + virtual status_t reset(); + virtual status_t setLooping(int loop); + virtual player_type playerType(); + virtual status_t setParameter(int key, const Parcel &request); + virtual status_t getParameter(int key, Parcel *reply); + virtual status_t invoke(const Parcel& request, Parcel *reply); + + protected: + virtual ~AAH_RXPlayer(); + + private: + class ThreadWrapper : public Thread { + public: + friend class AAH_RXPlayer; + explicit ThreadWrapper(AAH_RXPlayer& player) + : Thread(false /* canCallJava */ ) + , player_(player) { } + + virtual bool threadLoop() { return player_.threadLoop(); } + + private: + AAH_RXPlayer& player_; + + DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper); + }; + +#pragma pack(push, 1) + // PacketBuffers are structures used by the RX ring buffer. The ring buffer + // is a ring of pointers to PacketBuffer structures which act as variable + // length byte arrays and hold the contents of received UDP packets. Rather + // than make this a structure which hold a length and a pointer to another + // allocated structure (which would require two allocations), this struct + // uses a structure overlay pattern where allocation for the byte array + // consists of allocating (arrayLen + sizeof(ssize_t)) bytes of data from + // whatever pool/heap the packet buffer pulls from, and then overlaying the + // packed PacketBuffer structure on top of the allocation. The one-byte + // array at the end of the structure serves as an offset to the the data + // portion of the allocation; packet buffers are never allocated on the + // stack or using the new operator. Instead, the static allocate-byte-array + // and destroy methods handle the allocate and overlay pattern. They also + // allow for a potential future optimization where instead of just + // allocating blocks from the process global heap and overlaying, the + // allocator is replaced with a different implementation (private heap, + // free-list, circular buffer, etc) which reduces potential heap + // fragmentation issues which might arise from the frequent allocation and + // destruction of the received UDP traffic. + struct PacketBuffer { + ssize_t length_; + uint8_t data_[1]; + + // TODO : consider changing this to be some form of ring buffer or free + // pool system instead of just using the heap in order to avoid heap + // fragmentation. + static PacketBuffer* allocate(ssize_t length); + static void destroy(PacketBuffer* pb); + + private: + // Force people to use allocate/destroy instead of new/delete. + PacketBuffer() { } + ~PacketBuffer() { } + }; + + struct RetransRequest { + uint32_t magic_; + uint32_t mcast_ip_; + uint16_t mcast_port_; + uint16_t start_seq_; + uint16_t end_seq_; + }; +#pragma pack(pop) + + enum GapStatus { + kGS_NoGap = 0, + kGS_NormalGap, + kGS_FastStartGap, + }; + + struct SeqNoGap { + uint16_t start_seq_; + uint16_t end_seq_; + }; + + class RXRingBuffer { + public: + explicit RXRingBuffer(uint32_t capacity); + ~RXRingBuffer(); + + bool initCheck() const { return (ring_ != NULL); } + void reset(); + + // Push a packet buffer with a given sequence number into the ring + // buffer. pushBuffer will always consume the buffer pushed to it, + // either destroying it because it was a duplicate or overflow, or + // holding on to it in the ring. Callers should not hold any references + // to PacketBuffers after they have been pushed to the ring. Returns + // false in the case of a serious error (such as ring overflow). + // Callers should consider resetting the pipeline entirely in the event + // of a serious error. + bool pushBuffer(PacketBuffer* buf, uint16_t seq); + + // Fetch the next buffer in the RTP sequence. Returns NULL if there is + // no buffer to fetch. If a non-NULL PacketBuffer is returned, + // is_discon will be set to indicate whether or not this PacketBuffer is + // discontiuous with any previously returned packet buffers. Packet + // buffers returned by fetchBuffer are the caller's responsibility; they + // must be certain to destroy the buffers when they are done. + PacketBuffer* fetchBuffer(bool* is_discon); + + // Returns true and fills out the gap structure if the read pointer of + // the ring buffer is currently pointing to a gap which would stall a + // fetchBuffer operation. Returns false if the read pointer is not + // pointing to a gap in the sequence currently. + GapStatus fetchCurrentGap(SeqNoGap* gap); + + // Causes the read pointer to skip over any portion of a gap indicated + // by nak. If nak is NULL, any gap currently blocking the read pointer + // will be completely skipped. If any portion of a gap is skipped, the + // next successful read from fetch buffer will indicate a discontinuity. + void processNAK(const SeqNoGap* nak = NULL); + + // Compute the number of milliseconds until the inactivity timer for + // this RTP stream. Returns -1 if there is no active timeout, or 0 if + // the system has already timed out. + int computeInactivityTimeout(); + + private: + Mutex lock_; + PacketBuffer** ring_; + uint32_t capacity_; + uint32_t rd_; + uint32_t wr_; + + uint16_t rd_seq_; + bool rd_seq_known_; + bool waiting_for_fast_start_; + bool fetched_first_packet_; + + uint64_t rtp_activity_timeout_; + bool rtp_activity_timeout_valid_; + + DISALLOW_EVIL_CONSTRUCTORS(RXRingBuffer); + }; + + class Substream : public virtual RefBase { + public: + Substream(uint32_t ssrc, OMXClient& omx); + + void cleanupBufferInProgress(); + void shutdown(); + void processPayloadStart(uint8_t* buf, + uint32_t amt, + int32_t ts_lower); + void processPayloadCont (uint8_t* buf, + uint32_t amt); + void processTSTransform(const LinearTransform& trans); + + bool isAboutToUnderflow(); + uint32_t getSSRC() const { return ssrc_; } + uint16_t getProgramID() const { return (ssrc_ >> 5) & 0x1F; } + status_t getStatus() const { return status_; } + + protected: + virtual ~Substream() { + shutdown(); + } + + private: + void cleanupDecoder(); + bool shouldAbort(const char* log_tag); + void processCompletedBuffer(); + bool setupSubstreamType(uint8_t substream_type, + uint8_t codec_type); + + uint32_t ssrc_; + bool waiting_for_rap_; + status_t status_; + + bool substream_details_known_; + uint8_t substream_type_; + uint8_t codec_type_; + sp<MetaData> substream_meta_; + + MediaBuffer* buffer_in_progress_; + uint32_t expected_buffer_size_; + uint32_t buffer_filled_; + + sp<AAH_DecoderPump> decoder_; + + static int64_t kAboutToUnderflowThreshold; + + DISALLOW_EVIL_CONSTRUCTORS(Substream); + }; + + typedef DefaultKeyedVector< uint32_t, sp<Substream> > SubstreamVec; + + status_t startWorkThread(); + void stopWorkThread(); + virtual bool threadLoop(); + bool setupSocket(); + void cleanupSocket(); + void resetPipeline(); + void reset_l(); + bool processRX(PacketBuffer* pb); + void processRingBuffer(); + void processCommandPacket(PacketBuffer* pb); + bool processGaps(); + int computeNextGapRetransmitTimeout(); + void fetchAudioFlinger(); + + PipeEvent wakeup_work_thread_evt_; + sp<ThreadWrapper> thread_wrapper_; + Mutex api_lock_; + bool is_playing_; + bool data_source_set_; + + struct sockaddr_in listen_addr_; + int sock_fd_; + bool multicast_joined_; + + struct sockaddr_in transmitter_addr_; + bool transmitter_known_; + + uint32_t current_epoch_; + bool current_epoch_known_; + + SeqNoGap current_gap_; + GapStatus current_gap_status_; + uint64_t next_retrans_req_time_; + + RXRingBuffer ring_buffer_; + SubstreamVec substreams_; + OMXClient omx_; + CCHelper cc_helper_; + + // Connection to audio flinger used to hack a path to setMasterVolume. + sp<IAudioFlinger> audio_flinger_; + + static const uint32_t kRTPRingBufferSize; + static const uint32_t kRetransRequestMagic; + static const uint32_t kFastStartRequestMagic; + static const uint32_t kRetransNAKMagic; + static const uint32_t kGapRerequestTimeoutUSec; + static const uint32_t kFastStartTimeoutUSec; + static const uint32_t kRTPActivityTimeoutUSec; + + static const uint32_t INVOKE_GET_MASTER_VOLUME = 3; + static const uint32_t INVOKE_SET_MASTER_VOLUME = 4; + + static uint64_t monotonicUSecNow(); + + DISALLOW_EVIL_CONSTRUCTORS(AAH_RXPlayer); +}; + +} // namespace android + +#endif // __AAH_RX_PLAYER_H__ diff --git a/media/libaah_rtp/aah_rx_player_core.cpp b/media/libaah_rtp/aah_rx_player_core.cpp new file mode 100644 index 000000000000..d2b33866cdeb --- /dev/null +++ b/media/libaah_rtp/aah_rx_player_core.cpp @@ -0,0 +1,807 @@ +/* + * 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 "LibAAH_RTP" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include <fcntl.h> +#include <poll.h> +#include <sys/socket.h> +#include <time.h> +#include <utils/misc.h> + +#include <media/stagefright/Utils.h> + +#include "aah_rx_player.h" +#include "aah_tx_packet.h" + +namespace android { + +const uint32_t AAH_RXPlayer::kRetransRequestMagic = + FOURCC('T','r','e','q'); +const uint32_t AAH_RXPlayer::kRetransNAKMagic = + FOURCC('T','n','a','k'); +const uint32_t AAH_RXPlayer::kFastStartRequestMagic = + FOURCC('T','f','s','t'); +const uint32_t AAH_RXPlayer::kGapRerequestTimeoutUSec = 75000; +const uint32_t AAH_RXPlayer::kFastStartTimeoutUSec = 800000; +const uint32_t AAH_RXPlayer::kRTPActivityTimeoutUSec = 10000000; + +static inline int16_t fetchInt16(uint8_t* data) { + return static_cast<int16_t>(U16_AT(data)); +} + +static inline int32_t fetchInt32(uint8_t* data) { + return static_cast<int32_t>(U32_AT(data)); +} + +static inline int64_t fetchInt64(uint8_t* data) { + return static_cast<int64_t>(U64_AT(data)); +} + +uint64_t AAH_RXPlayer::monotonicUSecNow() { + struct timespec now; + int res = clock_gettime(CLOCK_MONOTONIC, &now); + CHECK(res >= 0); + + uint64_t ret = static_cast<uint64_t>(now.tv_sec) * 1000000; + ret += now.tv_nsec / 1000; + + return ret; +} + +status_t AAH_RXPlayer::startWorkThread() { + status_t res; + stopWorkThread(); + res = thread_wrapper_->run("TRX_Player", PRIORITY_AUDIO); + + if (res != OK) { + ALOGE("Failed to start work thread (res = %d)", res); + } + + return res; +} + +void AAH_RXPlayer::stopWorkThread() { + thread_wrapper_->requestExit(); // set the exit pending flag + wakeup_work_thread_evt_.setEvent(); + + status_t res; + res = thread_wrapper_->requestExitAndWait(); // block until thread exit. + if (res != OK) { + ALOGE("Failed to stop work thread (res = %d)", res); + } + + wakeup_work_thread_evt_.clearPendingEvents(); +} + +void AAH_RXPlayer::cleanupSocket() { + if (sock_fd_ >= 0) { + if (multicast_joined_) { + int res; + struct ip_mreq mreq; + mreq.imr_multiaddr = listen_addr_.sin_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + res = setsockopt(sock_fd_, + IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (res < 0) { + ALOGW("Failed to leave multicast group. (%d, %d)", res, errno); + } + multicast_joined_ = false; + } + + close(sock_fd_); + sock_fd_ = -1; + } + + resetPipeline(); +} + +void AAH_RXPlayer::resetPipeline() { + ring_buffer_.reset(); + + // Explicitly shudown all of the active substreams, then call clear out the + // collection. Failure to clear out a substream can result in its decoder + // holding a reference to itself and therefor not going away when the + // collection is cleared. + for (size_t i = 0; i < substreams_.size(); ++i) + substreams_.valueAt(i)->shutdown(); + + substreams_.clear(); + + current_gap_status_ = kGS_NoGap; +} + +bool AAH_RXPlayer::setupSocket() { + long flags; + int res, buf_size; + socklen_t opt_size; + + cleanupSocket(); + CHECK(sock_fd_ < 0); + + // Make the socket + sock_fd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sock_fd_ < 0) { + ALOGE("Failed to create listen socket (errno %d)", errno); + goto bailout; + } + + // Set non-blocking operation + flags = fcntl(sock_fd_, F_GETFL); + res = fcntl(sock_fd_, F_SETFL, flags | O_NONBLOCK); + if (res < 0) { + ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)", + sock_fd_, errno); + goto bailout; + } + + // Bind to our port + struct sockaddr_in bind_addr; + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + bind_addr.sin_addr.s_addr = INADDR_ANY; + bind_addr.sin_port = listen_addr_.sin_port; + res = bind(sock_fd_, + reinterpret_cast<const sockaddr*>(&bind_addr), + sizeof(bind_addr)); + if (res < 0) { + uint32_t a = ntohl(bind_addr.sin_addr.s_addr); + uint16_t p = ntohs(bind_addr.sin_port); + ALOGE("Failed to bind socket (%d) to %d.%d.%d.%d:%hd. (errno %d)", + sock_fd_, + (a >> 24) & 0xFF, + (a >> 16) & 0xFF, + (a >> 8) & 0xFF, + (a ) & 0xFF, + p, + errno); + + goto bailout; + } + + buf_size = 1 << 16; // 64k + res = setsockopt(sock_fd_, + SOL_SOCKET, SO_RCVBUF, + &buf_size, sizeof(buf_size)); + if (res < 0) { + ALOGW("Failed to increase socket buffer size to %d. (errno %d)", + buf_size, errno); + } + + buf_size = 0; + opt_size = sizeof(buf_size); + res = getsockopt(sock_fd_, + SOL_SOCKET, SO_RCVBUF, + &buf_size, &opt_size); + if (res < 0) { + ALOGW("Failed to fetch socket buffer size. (errno %d)", errno); + } else { + ALOGI("RX socket buffer size is now %d bytes", buf_size); + } + + if (listen_addr_.sin_addr.s_addr) { + // Join the multicast group and we should be good to go. + struct ip_mreq mreq; + mreq.imr_multiaddr = listen_addr_.sin_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + res = setsockopt(sock_fd_, + IPPROTO_IP, + IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (res < 0) { + ALOGE("Failed to join multicast group. (errno %d)", errno); + goto bailout; + } + multicast_joined_ = true; + } + + return true; + +bailout: + cleanupSocket(); + return false; +} + +bool AAH_RXPlayer::threadLoop() { + struct pollfd poll_fds[2]; + bool process_more_right_now = false; + + if (!setupSocket()) { + sendEvent(MEDIA_ERROR); + goto bailout; + } + + while (!thread_wrapper_->exitPending()) { + // Step 1: Wait until there is something to do. + int gap_timeout = computeNextGapRetransmitTimeout(); + int ring_timeout = ring_buffer_.computeInactivityTimeout(); + int timeout = -1; + + if (!ring_timeout) { + ALOGW("RTP inactivity timeout reached, resetting pipeline."); + resetPipeline(); + timeout = gap_timeout; + } else { + if (gap_timeout < 0) { + timeout = ring_timeout; + } else if (ring_timeout < 0) { + timeout = gap_timeout; + } else { + timeout = (gap_timeout < ring_timeout) ? gap_timeout + : ring_timeout; + } + } + + if ((0 != timeout) && (!process_more_right_now)) { + // Set up the events to wait on. Start with the wakeup pipe. + memset(&poll_fds, 0, sizeof(poll_fds)); + poll_fds[0].fd = wakeup_work_thread_evt_.getWakeupHandle(); + poll_fds[0].events = POLLIN; + + // Add the RX socket. + poll_fds[1].fd = sock_fd_; + poll_fds[1].events = POLLIN; + + // Wait for something interesing to happen. + int poll_res = poll(poll_fds, NELEM(poll_fds), timeout); + if (poll_res < 0) { + ALOGE("Fatal error (%d,%d) while waiting on events", + poll_res, errno); + sendEvent(MEDIA_ERROR); + goto bailout; + } + } + + if (thread_wrapper_->exitPending()) { + break; + } + + wakeup_work_thread_evt_.clearPendingEvents(); + process_more_right_now = false; + + // Step 2: Do we have data waiting in the socket? If so, drain the + // socket moving valid RTP information into the ring buffer to be + // processed. + if (poll_fds[1].revents) { + struct sockaddr_in from; + socklen_t from_len; + + ssize_t res = 0; + while (!thread_wrapper_->exitPending()) { + // Check the size of any pending packet. + res = recv(sock_fd_, NULL, 0, MSG_PEEK | MSG_TRUNC); + + // Error? + if (res < 0) { + // If the error is anything other than would block, + // something has gone very wrong. + if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) { + ALOGE("Fatal socket error during recvfrom (%d, %d)", + (int)res, errno); + goto bailout; + } + + // Socket is out of data, just break out of processing and + // wait for more. + break; + } + + // Allocate a payload. + PacketBuffer* pb = PacketBuffer::allocate(res); + if (NULL == pb) { + ALOGE("Fatal error, failed to allocate packet buffer of" + " length %u", static_cast<uint32_t>(res)); + goto bailout; + } + + // Fetch the data. + from_len = sizeof(from); + res = recvfrom(sock_fd_, pb->data_, pb->length_, 0, + reinterpret_cast<struct sockaddr*>(&from), + &from_len); + if (res != pb->length_) { + ALOGE("Fatal error, fetched packet length (%d) does not" + " match peeked packet length (%u). This should never" + " happen. (errno = %d)", + static_cast<int>(res), + static_cast<uint32_t>(pb->length_), + errno); + } + + bool drop_packet = false; + if (transmitter_known_) { + if (from.sin_addr.s_addr != + transmitter_addr_.sin_addr.s_addr) { + uint32_t a = ntohl(from.sin_addr.s_addr); + uint16_t p = ntohs(from.sin_port); + ALOGV("Dropping packet from unknown transmitter" + " %u.%u.%u.%u:%hu", + ((a >> 24) & 0xFF), + ((a >> 16) & 0xFF), + ((a >> 8) & 0xFF), + ( a & 0xFF), + p); + + drop_packet = true; + } else { + transmitter_addr_.sin_port = from.sin_port; + } + } else { + memcpy(&transmitter_addr_, &from, sizeof(from)); + transmitter_known_ = true; + } + + if (!drop_packet) { + bool serious_error = !processRX(pb); + + if (serious_error) { + // Something went "seriously wrong". Currently, the + // only trigger for this should be a ring buffer + // overflow. The current failsafe behavior for when + // something goes seriously wrong is to just reset the + // pipeline. The system should behave as if this + // AAH_RXPlayer was just set up for the first time. + ALOGE("Something just went seriously wrong with the" + " pipeline. Resetting."); + resetPipeline(); + } + } else { + PacketBuffer::destroy(pb); + } + } + } + + // Step 3: Process any data we mave have accumulated in the ring buffer + // so far. + if (!thread_wrapper_->exitPending()) { + processRingBuffer(); + } + + // Step 4: At this point in time, the ring buffer should either be + // empty, or stalled in front of a gap caused by some dropped packets. + // Check on the current gap situation and deal with it in an appropriate + // fashion. If processGaps returns true, it means that it has given up + // on a gap and that we should try to process some more data + // immediately. + if (!thread_wrapper_->exitPending()) { + process_more_right_now = processGaps(); + } + + // Step 5: Check for fatal errors. If any of our substreams has + // encountered a fatal, unrecoverable, error, then propagate the error + // up to user level and shut down. + for (size_t i = 0; i < substreams_.size(); ++i) { + status_t status; + CHECK(substreams_.valueAt(i) != NULL); + + status = substreams_.valueAt(i)->getStatus(); + if (OK != status) { + ALOGE("Substream index %d has encountered an unrecoverable" + " error (%d). Signalling application level and shutting" + " down.", i, status); + sendEvent(MEDIA_ERROR); + goto bailout; + } + } + } + +bailout: + cleanupSocket(); + return false; +} + +bool AAH_RXPlayer::processRX(PacketBuffer* pb) { + CHECK(NULL != pb); + + uint8_t* data = pb->data_; + ssize_t amt = pb->length_; + uint32_t nak_magic; + uint16_t seq_no; + uint32_t epoch; + + // Every packet either starts with an RTP header which is at least 12 bytes + // long or is a retry NAK which is 14 bytes long. If there are fewer than + // 12 bytes here, this cannot be a proper RTP packet. + if (amt < 12) { + ALOGV("Dropping packet, too short to contain RTP header (%u bytes)", + static_cast<uint32_t>(amt)); + goto drop_packet; + } + + // Check to see if this is the special case of a NAK packet. + nak_magic = ntohl(*(reinterpret_cast<uint32_t*>(data))); + if (nak_magic == kRetransNAKMagic) { + // Looks like a NAK packet; make sure its long enough. + + if (amt < static_cast<ssize_t>(sizeof(RetransRequest))) { + ALOGV("Dropping packet, too short to contain NAK payload (%u bytes)", + static_cast<uint32_t>(amt)); + goto drop_packet; + } + + SeqNoGap gap; + RetransRequest* rtr = reinterpret_cast<RetransRequest*>(data); + gap.start_seq_ = ntohs(rtr->start_seq_); + gap.end_seq_ = ntohs(rtr->end_seq_); + + ALOGV("Process NAK for gap at [%hu, %hu]", gap.start_seq_, gap.end_seq_); + ring_buffer_.processNAK(&gap); + + return true; + } + + // According to the TRTP spec, version should be 2, padding should be 0, + // extension should be 0 and CSRCCnt should be 0. If any of these tests + // fail, we chuck the packet. + if (data[0] != 0x80) { + ALOGV("Dropping packet, bad V/P/X/CSRCCnt field (0x%02x)", + data[0]); + goto drop_packet; + } + + // Check the payload type. For TRTP, it should always be 100. + if ((data[1] & 0x7F) != 100) { + ALOGV("Dropping packet, bad payload type. (%u)", + data[1] & 0x7F); + goto drop_packet; + } + + // Check whether the transmitter has begun a new epoch. + epoch = (U32_AT(data + 8) >> 10) & 0x3FFFFF; + if (current_epoch_known_) { + if (epoch != current_epoch_) { + ALOGV("%s: new epoch %u", __PRETTY_FUNCTION__, epoch); + current_epoch_ = epoch; + resetPipeline(); + } + } else { + current_epoch_ = epoch; + current_epoch_known_ = true; + } + + // Extract the sequence number and hand the packet off to the ring buffer + // for dropped packet detection and later processing. + seq_no = U16_AT(data + 2); + return ring_buffer_.pushBuffer(pb, seq_no); + +drop_packet: + PacketBuffer::destroy(pb); + return true; +} + +void AAH_RXPlayer::processRingBuffer() { + PacketBuffer* pb; + bool is_discon; + sp<Substream> substream; + LinearTransform trans; + bool foundTrans = false; + + while (NULL != (pb = ring_buffer_.fetchBuffer(&is_discon))) { + if (is_discon) { + // Abort all partially assembled payloads. + for (size_t i = 0; i < substreams_.size(); ++i) { + CHECK(substreams_.valueAt(i) != NULL); + substreams_.valueAt(i)->cleanupBufferInProgress(); + } + } + + uint8_t* data = pb->data_; + ssize_t amt = pb->length_; + + // Should not have any non-RTP packets in the ring buffer. RTP packets + // must be at least 12 bytes long. + CHECK(amt >= 12); + + // Extract the marker bit and the SSRC field. + bool marker = (data[1] & 0x80) != 0; + uint32_t ssrc = U32_AT(data + 8); + + // Is this the start of a new TRTP payload? If so, the marker bit + // should be set and there are some things we should be checking for. + if (marker) { + // TRTP headers need to have at least a byte for version, a byte for + // payload type and flags, and 4 bytes for length. + if (amt < 18) { + ALOGV("Dropping packet, too short to contain TRTP header" + " (%u bytes)", static_cast<uint32_t>(amt)); + goto process_next_packet; + } + + // Check the TRTP version and extract the payload type/flags. + uint8_t trtp_version = data[12]; + uint8_t payload_type = (data[13] >> 4) & 0xF; + uint8_t trtp_flags = data[13] & 0xF; + + if (1 != trtp_version) { + ALOGV("Dropping packet, bad trtp version %hhu", trtp_version); + goto process_next_packet; + } + + // Is there a timestamp transformation present on this packet? If + // so, extract it and pass it to the appropriate substreams. + if (trtp_flags & 0x02) { + ssize_t offset = 18 + ((trtp_flags & 0x01) ? 4 : 0); + if (amt < (offset + 24)) { + ALOGV("Dropping packet, too short to contain TRTP Timestamp" + " Transformation (%u bytes)", + static_cast<uint32_t>(amt)); + goto process_next_packet; + } + + trans.a_zero = fetchInt64(data + offset); + trans.b_zero = fetchInt64(data + offset + 16); + trans.a_to_b_numer = static_cast<int32_t>( + fetchInt32 (data + offset + 8)); + trans.a_to_b_denom = U32_AT(data + offset + 12); + foundTrans = true; + + uint32_t program_id = (ssrc >> 5) & 0x1F; + for (size_t i = 0; i < substreams_.size(); ++i) { + sp<Substream> iter = substreams_.valueAt(i); + CHECK(iter != NULL); + + if (iter->getProgramID() == program_id) { + iter->processTSTransform(trans); + } + } + } + + // Is this a command packet? If so, its not necessarily associate + // with one particular substream. Just give it to the command + // packet handler and then move on. + if (4 == payload_type) { + processCommandPacket(pb); + goto process_next_packet; + } + } + + // If we got to here, then we are a normal packet. Find (or allocate) + // the substream we belong to and send the packet off to be processed. + substream = substreams_.valueFor(ssrc); + if (substream == NULL) { + substream = new Substream(ssrc, omx_); + if (substream == NULL) { + ALOGE("Failed to allocate substream for SSRC 0x%08x", ssrc); + goto process_next_packet; + } + substreams_.add(ssrc, substream); + + if (foundTrans) { + substream->processTSTransform(trans); + } + } + + CHECK(substream != NULL); + + if (marker) { + // Start of a new TRTP payload for this substream. Extract the + // lower 32 bits of the timestamp and hand the buffer to the + // substream for processing. + uint32_t ts_lower = U32_AT(data + 4); + substream->processPayloadStart(data + 12, amt - 12, ts_lower); + } else { + // Continuation of an existing TRTP payload. Just hand it off to + // the substream for processing. + substream->processPayloadCont(data + 12, amt - 12); + } + +process_next_packet: + PacketBuffer::destroy(pb); + } // end of main processing while loop. +} + +void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) { + CHECK(NULL != pb); + + uint8_t* data = pb->data_; + ssize_t amt = pb->length_; + + // verify that this packet meets the minimum length of a command packet + if (amt < 20) { + return; + } + + uint8_t trtp_version = data[12]; + uint8_t trtp_flags = data[13] & 0xF; + + if (1 != trtp_version) { + ALOGV("Dropping packet, bad trtp version %hhu", trtp_version); + return; + } + + // calculate the start of the command payload + ssize_t offset = 18; + if (trtp_flags & 0x01) { + // timestamp is present (4 bytes) + offset += 4; + } + if (trtp_flags & 0x02) { + // transform is present (24 bytes) + offset += 24; + } + + // the packet must contain 2 bytes of command payload beyond the TRTP header + if (amt < offset + 2) { + return; + } + + uint16_t command_id = U16_AT(data + offset); + + switch (command_id) { + case TRTPControlPacket::kCommandNop: + break; + + case TRTPControlPacket::kCommandEOS: + case TRTPControlPacket::kCommandFlush: { + uint16_t program_id = (U32_AT(data + 8) >> 5) & 0x1F; + ALOGI("*** %s flushing program_id=%d", + __PRETTY_FUNCTION__, program_id); + + Vector<uint32_t> substreams_to_remove; + for (size_t i = 0; i < substreams_.size(); ++i) { + sp<Substream> iter = substreams_.valueAt(i); + if (iter->getProgramID() == program_id) { + iter->shutdown(); + substreams_to_remove.add(iter->getSSRC()); + } + } + + for (size_t i = 0; i < substreams_to_remove.size(); ++i) { + substreams_.removeItem(substreams_to_remove[i]); + } + } break; + } +} + +bool AAH_RXPlayer::processGaps() { + // Deal with the current gap situation. Specifically... + // + // 1) If a new gap has shown up, send a retransmit request to the + // transmitter. + // 2) If a gap we were working on has had a packet in the middle or at + // the end filled in, send another retransmit request for the begining + // portion of the gap. TRTP was designed for LANs where packet + // re-ordering is very unlikely; so see the middle or end of a gap + // filled in before the begining is an almost certain indication that + // a retransmission packet was also dropped. + // 3) If we have been working on a gap for a while and it still has not + // been filled in, send another retransmit request. + // 4) If the are no more gaps in the ring, clear the current_gap_status_ + // flag to indicate that all is well again. + + // Start by fetching the active gap status. + SeqNoGap gap; + bool send_retransmit_request = false; + bool ret_val = false; + GapStatus gap_status; + if (kGS_NoGap != (gap_status = ring_buffer_.fetchCurrentGap(&gap))) { + // Note: checking for a change in the end sequence number should cover + // moving on to an entirely new gap for case #1 as well as resending the + // begining of a gap range for case #2. + send_retransmit_request = (kGS_NoGap == current_gap_status_) || + (current_gap_.end_seq_ != gap.end_seq_); + + // If this is the same gap we have been working on, and it has timed + // out, then check to see if our substreams are about to underflow. If + // so, instead of sending another retransmit request, just give up on + // this gap and move on. + if (!send_retransmit_request && + (kGS_NoGap != current_gap_status_) && + (0 == computeNextGapRetransmitTimeout())) { + + // If out current gap is the fast-start gap, don't bother to skip it + // because substreams look like the are about to underflow. + if ((kGS_FastStartGap != gap_status) || + (current_gap_.end_seq_ != gap.end_seq_)) { + for (size_t i = 0; i < substreams_.size(); ++i) { + if (substreams_.valueAt(i)->isAboutToUnderflow()) { + ALOGV("About to underflow, giving up on gap [%hu, %hu]", + gap.start_seq_, gap.end_seq_); + ring_buffer_.processNAK(); + current_gap_status_ = kGS_NoGap; + return true; + } + } + } + + // Looks like no one is about to underflow. Just go ahead and send + // the request. + send_retransmit_request = true; + } + } else { + current_gap_status_ = kGS_NoGap; + } + + if (send_retransmit_request) { + // If we have been working on a fast start, and it is still not filled + // in, even after the extended retransmit time out, give up and skip it. + // The system should fall back into its normal slow-start behavior. + if ((kGS_FastStartGap == current_gap_status_) && + (current_gap_.end_seq_ == gap.end_seq_)) { + ALOGV("Fast start is taking forever; giving up."); + ring_buffer_.processNAK(); + current_gap_status_ = kGS_NoGap; + return true; + } + + // Send the request. + RetransRequest req; + uint32_t magic = (kGS_FastStartGap == gap_status) + ? kFastStartRequestMagic + : kRetransRequestMagic; + req.magic_ = htonl(magic); + req.mcast_ip_ = listen_addr_.sin_addr.s_addr; + req.mcast_port_ = listen_addr_.sin_port; + req.start_seq_ = htons(gap.start_seq_); + req.end_seq_ = htons(gap.end_seq_); + + { + uint32_t a = ntohl(transmitter_addr_.sin_addr.s_addr); + uint16_t p = ntohs(transmitter_addr_.sin_port); + ALOGV("Sending to transmitter %u.%u.%u.%u:%hu", + ((a >> 24) & 0xFF), + ((a >> 16) & 0xFF), + ((a >> 8) & 0xFF), + ( a & 0xFF), + p); + } + + int res = sendto(sock_fd_, &req, sizeof(req), 0, + reinterpret_cast<struct sockaddr*>(&transmitter_addr_), + sizeof(transmitter_addr_)); + if (res < 0) { + ALOGE("Error when sending retransmit request (%d)", errno); + } else { + ALOGV("%s request for range [%hu, %hu] sent", + (kGS_FastStartGap == gap_status) ? "Fast Start" : "Retransmit", + gap.start_seq_, gap.end_seq_); + } + + // Update the current gap info. + current_gap_ = gap; + current_gap_status_ = gap_status; + next_retrans_req_time_ = monotonicUSecNow() + + ((kGS_FastStartGap == current_gap_status_) + ? kFastStartTimeoutUSec + : kGapRerequestTimeoutUSec); + } + + return false; +} + +// Compute when its time to send the next gap retransmission in milliseconds. +// Returns < 0 for an infinite timeout (no gap) and 0 if its time to retransmit +// right now. +int AAH_RXPlayer::computeNextGapRetransmitTimeout() { + if (kGS_NoGap == current_gap_status_) { + return -1; + } + + int64_t timeout_delta = next_retrans_req_time_ - monotonicUSecNow(); + + timeout_delta /= 1000; + if (timeout_delta <= 0) { + return 0; + } + + return static_cast<uint32_t>(timeout_delta); +} + +} // namespace android diff --git a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp new file mode 100644 index 000000000000..0d8b31f5441d --- /dev/null +++ b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp @@ -0,0 +1,366 @@ +/* + * 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 "LibAAH_RTP" +//#define LOG_NDEBUG 0 +#include <utils/Log.h> + +#include "aah_rx_player.h" + +namespace android { + +AAH_RXPlayer::RXRingBuffer::RXRingBuffer(uint32_t capacity) { + capacity_ = capacity; + rd_ = wr_ = 0; + ring_ = new PacketBuffer*[capacity]; + memset(ring_, 0, sizeof(PacketBuffer*) * capacity); + reset(); +} + +AAH_RXPlayer::RXRingBuffer::~RXRingBuffer() { + reset(); + delete[] ring_; +} + +void AAH_RXPlayer::RXRingBuffer::reset() { + AutoMutex lock(&lock_); + + if (NULL != ring_) { + while (rd_ != wr_) { + CHECK(rd_ < capacity_); + if (NULL != ring_[rd_]) { + PacketBuffer::destroy(ring_[rd_]); + ring_[rd_] = NULL; + } + rd_ = (rd_ + 1) % capacity_; + } + } + + rd_ = wr_ = 0; + rd_seq_known_ = false; + waiting_for_fast_start_ = true; + fetched_first_packet_ = false; + rtp_activity_timeout_valid_ = false; +} + +bool AAH_RXPlayer::RXRingBuffer::pushBuffer(PacketBuffer* buf, + uint16_t seq) { + AutoMutex lock(&lock_); + CHECK(NULL != ring_); + CHECK(NULL != buf); + + rtp_activity_timeout_valid_ = true; + rtp_activity_timeout_ = monotonicUSecNow() + kRTPActivityTimeoutUSec; + + // If the ring buffer is totally reset (we have never received a single + // payload) then we don't know the rd sequence number and this should be + // simple. We just store the payload, advance the wr pointer and record the + // initial sequence number. + if (!rd_seq_known_) { + CHECK(rd_ == wr_); + CHECK(NULL == ring_[wr_]); + CHECK(wr_ < capacity_); + + ring_[wr_] = buf; + wr_ = (wr_ + 1) % capacity_; + rd_seq_ = seq; + rd_seq_known_ = true; + return true; + } + + // Compute the seqence number of this payload and of the write pointer, + // normalized around the read pointer. IOW - transform the payload seq no + // and the wr pointer seq no into a space where the rd pointer seq no is + // zero. This will define 4 cases we can consider... + // + // 1) norm_seq == norm_wr_seq + // This payload is contiguous with the last. All is good. + // + // 2) ((norm_seq < norm_wr_seq) && (norm_seq >= norm_rd_seq) + // aka ((norm_seq < norm_wr_seq) && (norm_seq >= 0) + // This payload is in the past, in the unprocessed region of the ring + // buffer. It is probably a retransmit intended to fill in a dropped + // payload; it may be a duplicate. + // + // 3) ((norm_seq - norm_wr_seq) & 0x8000) != 0 + // This payload is in the past compared to the write pointer (or so very + // far in the future that it has wrapped the seq no space), but not in + // the unprocessed region of the ring buffer. This could be a duplicate + // retransmit; we just drop these payloads unless we are waiting for our + // first fast start packet. If we are waiting for fast start, than this + // packet is probably the first packet of the fast start retransmission. + // If it will fit in the buffer, back up the read pointer to its position + // and clear the fast start flag, otherwise just drop it. + // + // 4) ((norm_seq - norm_wr_seq) & 0x8000) == 0 + // This payload which is ahead of the next write pointer. This indicates + // that we have missed some payloads and need to request a retransmit. + // If norm_seq >= (capacity - 1), then the gap is so large that it would + // overflow the ring buffer and we should probably start to panic. + + uint16_t norm_wr_seq = ((wr_ + capacity_ - rd_) % capacity_); + uint16_t norm_seq = seq - rd_seq_; + + // Check for overflow first. + if ((!(norm_seq & 0x8000)) && (norm_seq >= (capacity_ - 1))) { + ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu], seq = %hu", + capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq); + PacketBuffer::destroy(buf); + return false; + } + + // Check for case #1 + if (norm_seq == norm_wr_seq) { + CHECK(wr_ < capacity_); + CHECK(NULL == ring_[wr_]); + + ring_[wr_] = buf; + wr_ = (wr_ + 1) % capacity_; + + CHECK(wr_ != rd_); + return true; + } + + // Check case #2 + uint32_t ring_pos = (rd_ + norm_seq) % capacity_; + if ((norm_seq < norm_wr_seq) && (!(norm_seq & 0x8000))) { + // Do we already have a payload for this slot? If so, then this looks + // like a duplicate retransmit. Just ignore it. + if (NULL != ring_[ring_pos]) { + ALOGD("RXed duplicate retransmit, seq = %hu", seq); + PacketBuffer::destroy(buf); + } else { + // Looks like we were missing this payload. Go ahead and store it. + ring_[ring_pos] = buf; + } + + return true; + } + + // Check case #3 + if ((norm_seq - norm_wr_seq) & 0x8000) { + if (!waiting_for_fast_start_) { + ALOGD("RXed duplicate retransmit from before rd pointer, seq = %hu", + seq); + PacketBuffer::destroy(buf); + } else { + // Looks like a fast start fill-in. Go ahead and store it, assuming + // that we can fit it in the buffer. + uint32_t implied_ring_size = static_cast<uint32_t>(norm_wr_seq) + + (rd_seq_ - seq); + + if (implied_ring_size >= (capacity_ - 1)) { + ALOGD("RXed what looks like a fast start packet (seq = %hu)," + " but packet is too far in the past to fit into the ring" + " buffer. Dropping.", seq); + PacketBuffer::destroy(buf); + } else { + ring_pos = (rd_ + capacity_ + seq - rd_seq_) % capacity_; + rd_seq_ = seq; + rd_ = ring_pos; + waiting_for_fast_start_ = false; + + CHECK(ring_pos < capacity_); + CHECK(NULL == ring_[ring_pos]); + ring_[ring_pos] = buf; + } + + } + return true; + } + + // Must be in case #4 with no overflow. This packet fits in the current + // ring buffer, but is discontiuguous. Advance the write pointer leaving a + // gap behind. + uint32_t gap_len = (ring_pos + capacity_ - wr_) % capacity_; + ALOGD("Drop detected; %u packets, seq_range [%hu, %hu]", + gap_len, + rd_seq_ + norm_wr_seq, + rd_seq_ + norm_wr_seq + gap_len - 1); + + CHECK(NULL == ring_[ring_pos]); + ring_[ring_pos] = buf; + wr_ = (ring_pos + 1) % capacity_; + CHECK(wr_ != rd_); + + return true; +} + +AAH_RXPlayer::PacketBuffer* +AAH_RXPlayer::RXRingBuffer::fetchBuffer(bool* is_discon) { + AutoMutex lock(&lock_); + CHECK(NULL != ring_); + CHECK(NULL != is_discon); + + // If the read seqence number is not known, then this ring buffer has not + // received a packet since being reset and there cannot be any packets to + // return. If we are still waiting for the first fast start packet to show + // up, we don't want to let any buffer be consumed yet because we expect to + // see a packet before the initial read sequence number show up shortly. + if (!rd_seq_known_ || waiting_for_fast_start_) { + *is_discon = false; + return NULL; + } + + PacketBuffer* ret = NULL; + *is_discon = !fetched_first_packet_; + + while ((rd_ != wr_) && (NULL == ret)) { + CHECK(rd_ < capacity_); + + // If we hit a gap, stall and do not advance the read pointer. Let the + // higher level code deal with requesting retries and/or deciding to + // skip the current gap. + ret = ring_[rd_]; + if (NULL == ret) { + break; + } + + ring_[rd_] = NULL; + rd_ = (rd_ + 1) % capacity_; + ++rd_seq_; + } + + if (NULL != ret) { + fetched_first_packet_ = true; + } + + return ret; +} + +AAH_RXPlayer::GapStatus +AAH_RXPlayer::RXRingBuffer::fetchCurrentGap(SeqNoGap* gap) { + AutoMutex lock(&lock_); + CHECK(NULL != ring_); + CHECK(NULL != gap); + + // If the read seqence number is not known, then this ring buffer has not + // received a packet since being reset and there cannot be any gaps. + if (!rd_seq_known_) { + return kGS_NoGap; + } + + // If we are waiting for fast start, then the current gap is a fast start + // gap and it includes all packets before the read sequence number. + if (waiting_for_fast_start_) { + gap->start_seq_ = + gap->end_seq_ = rd_seq_ - 1; + return kGS_FastStartGap; + } + + // If rd == wr, then the buffer is empty and there cannot be any gaps. + if (rd_ == wr_) { + return kGS_NoGap; + } + + // If rd_ is currently pointing at an unprocessed packet, then there is no + // current gap. + CHECK(rd_ < capacity_); + if (NULL != ring_[rd_]) { + return kGS_NoGap; + } + + // Looks like there must be a gap here. The start of the gap is the current + // rd sequence number, all we need to do now is determine its length in + // order to compute the end sequence number. + gap->start_seq_ = rd_seq_; + uint16_t end = rd_seq_; + uint32_t tmp = (rd_ + 1) % capacity_; + while ((tmp != wr_) && (NULL == ring_[tmp])) { + ++end; + tmp = (tmp + 1) % capacity_; + } + gap->end_seq_ = end; + + return kGS_NormalGap; +} + +void AAH_RXPlayer::RXRingBuffer::processNAK(const SeqNoGap* nak) { + AutoMutex lock(&lock_); + CHECK(NULL != ring_); + + // If we were waiting for our first fast start fill-in packet, and we + // received a NAK, then apparantly we are not getting our fast start. Just + // clear the waiting flag and go back to normal behavior. + if (waiting_for_fast_start_) { + waiting_for_fast_start_ = false; + } + + // If we have not received a packet since last reset, or there is no data in + // the ring, then there is nothing to skip. + if ((!rd_seq_known_) || (rd_ == wr_)) { + return; + } + + // If rd_ is currently pointing at an unprocessed packet, then there is no + // gap to skip. + CHECK(rd_ < capacity_); + if (NULL != ring_[rd_]) { + return; + } + + // Looks like there must be a gap here. Advance rd until we have passed + // over the portion of it indicated by nak (or all of the gap if nak is + // NULL). Then reset fetched_first_packet_ so that the next read will show + // up as being discontiguous. + uint16_t seq_after_gap = (NULL == nak) ? 0 : nak->end_seq_ + 1; + while ((rd_ != wr_) && + (NULL == ring_[rd_]) && + ((NULL == nak) || (seq_after_gap != rd_seq_))) { + rd_ = (rd_ + 1) % capacity_; + ++rd_seq_; + } + fetched_first_packet_ = false; +} + +int AAH_RXPlayer::RXRingBuffer::computeInactivityTimeout() { + AutoMutex lock(&lock_); + + if (!rtp_activity_timeout_valid_) { + return -1; + } + + uint64_t now = monotonicUSecNow(); + if (rtp_activity_timeout_ <= now) { + return 0; + } + + return (rtp_activity_timeout_ - now) / 1000; +} + +AAH_RXPlayer::PacketBuffer* +AAH_RXPlayer::PacketBuffer::allocate(ssize_t length) { + if (length <= 0) { + return NULL; + } + + uint32_t alloc_len = sizeof(PacketBuffer) + length; + PacketBuffer* ret = reinterpret_cast<PacketBuffer*>( + new uint8_t[alloc_len]); + + if (NULL != ret) { + ret->length_ = length; + } + + return ret; +} + +void AAH_RXPlayer::PacketBuffer::destroy(PacketBuffer* pb) { + uint8_t* kill_me = reinterpret_cast<uint8_t*>(pb); + delete[] kill_me; +} + +} // namespace android diff --git a/media/libaah_rtp/aah_rx_player_substream.cpp b/media/libaah_rtp/aah_rx_player_substream.cpp new file mode 100644 index 000000000000..1e4c784629a2 --- /dev/null +++ b/media/libaah_rtp/aah_rx_player_substream.cpp @@ -0,0 +1,498 @@ +/* + * 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 "LibAAH_RTP" +//#define LOG_NDEBUG 0 + +#include <utils/Log.h> + +#include <include/avc_utils.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/OMXCodec.h> +#include <media/stagefright/Utils.h> + +#include "aah_rx_player.h" + +namespace android { + +int64_t AAH_RXPlayer::Substream::kAboutToUnderflowThreshold = + 50ull * 1000; + +AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) { + ssrc_ = ssrc; + substream_details_known_ = false; + buffer_in_progress_ = NULL; + status_ = OK; + + decoder_ = new AAH_DecoderPump(omx); + if (decoder_ == NULL) { + ALOGE("%s failed to allocate decoder pump!", __PRETTY_FUNCTION__); + } + if (OK != decoder_->initCheck()) { + ALOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__); + } + + // cleanupBufferInProgress will reset most of the internal state variables. + // Just need to make sure that buffer_in_progress_ is NULL before calling. + cleanupBufferInProgress(); +} + + +void AAH_RXPlayer::Substream::shutdown() { + substream_meta_ = NULL; + status_ = OK; + cleanupBufferInProgress(); + cleanupDecoder(); +} + +void AAH_RXPlayer::Substream::cleanupBufferInProgress() { + if (NULL != buffer_in_progress_) { + buffer_in_progress_->release(); + buffer_in_progress_ = NULL; + } + + expected_buffer_size_ = 0; + buffer_filled_ = 0; + waiting_for_rap_ = true; +} + +void AAH_RXPlayer::Substream::cleanupDecoder() { + if (decoder_ != NULL) { + decoder_->shutdown(); + } +} + +bool AAH_RXPlayer::Substream::shouldAbort(const char* log_tag) { + // If we have already encountered a fatal error, do nothing. We are just + // waiting for our owner to shut us down now. + if (OK != status_) { + ALOGV("Skipping %s, substream has encountered fatal error (%d).", + log_tag, status_); + return true; + } + + return false; +} + +void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf, + uint32_t amt, + int32_t ts_lower) { + uint32_t min_length = 6; + + if (shouldAbort(__PRETTY_FUNCTION__)) { + return; + } + + // Do we have a buffer in progress already? If so, abort the buffer. In + // theory, this should never happen. If there were a discontinutity in the + // stream, the discon in the seq_nos at the RTP level should have already + // triggered a cleanup of the buffer in progress. To see a problem at this + // level is an indication either of a bug in the transmitter, or some form + // of terrible corruption/tampering on the wire. + if (NULL != buffer_in_progress_) { + ALOGE("processPayloadStart is aborting payload already in progress."); + cleanupBufferInProgress(); + } + + // Parse enough of the header to know where we stand. Since this is a + // payload start, it should begin with a TRTP header which has to be at + // least 6 bytes long. + if (amt < min_length) { + ALOGV("Discarding payload too short to contain TRTP header (len = %u)", + amt); + return; + } + + // Check the TRTP version number. + if (0x01 != buf[0]) { + ALOGV("Unexpected TRTP version (%u) in header. Expected %u.", + buf[0], 1); + return; + } + + // Extract the substream type field and make sure its one we understand (and + // one that does not conflict with any previously received substream type. + uint8_t header_type = (buf[1] >> 4) & 0xF; + switch (header_type) { + case 0x01: + // Audio, yay! Just break. We understand audio payloads. + break; + case 0x02: + ALOGV("RXed packet with unhandled TRTP header type (Video)."); + return; + case 0x03: + ALOGV("RXed packet with unhandled TRTP header type (Subpicture)."); + return; + case 0x04: + ALOGV("RXed packet with unhandled TRTP header type (Control)."); + return; + default: + ALOGV("RXed packet with unhandled TRTP header type (%u).", + header_type); + return; + } + + if (substream_details_known_ && (header_type != substream_type_)) { + ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does not" + " match previously received header type (%u)", + ssrc_, header_type, substream_type_); + return; + } + + // Check the flags to see if there is another 32 bits of timestamp present. + uint32_t trtp_header_len = 6; + bool ts_valid = buf[1] & 0x1; + if (ts_valid) { + min_length += 4; + trtp_header_len += 4; + if (amt < min_length) { + ALOGV("Discarding payload too short to contain TRTP timestamp" + " (len = %u)", amt); + return; + } + } + + // Extract the TRTP length field and sanity check it. + uint32_t trtp_len; + trtp_len = (static_cast<uint32_t>(buf[2]) << 24) | + (static_cast<uint32_t>(buf[3]) << 16) | + (static_cast<uint32_t>(buf[4]) << 8) | + static_cast<uint32_t>(buf[5]); + if (trtp_len < min_length) { + ALOGV("TRTP length (%u) is too short to be valid. Must be at least %u" + " bytes.", trtp_len, min_length); + return; + } + + // Extract the rest of the timestamp field if valid. + int64_t ts = 0; + uint32_t parse_offset = 6; + if (ts_valid) { + ts = (static_cast<int64_t>(buf[parse_offset ]) << 56) | + (static_cast<int64_t>(buf[parse_offset + 1]) << 48) | + (static_cast<int64_t>(buf[parse_offset + 2]) << 40) | + (static_cast<int64_t>(buf[parse_offset + 3]) << 32); + ts |= ts_lower; + parse_offset += 4; + } + + // Check the flags to see if there is another 24 bytes of timestamp + // transformation present. + if (buf[1] & 0x2) { + min_length += 24; + parse_offset += 24; + trtp_header_len += 24; + if (amt < min_length) { + ALOGV("Discarding payload too short to contain TRTP timestamp" + " transformation (len = %u)", amt); + return; + } + } + + // TODO : break the parsing into individual parsers for the different + // payload types (audio, video, etc). + // + // At this point in time, we know that this is audio. Go ahead and parse + // the basic header, check the codec type, and find the payload portion of + // the packet. + min_length += 3; + if (trtp_len < min_length) { + ALOGV("TRTP length (%u) is too short to be a valid audio payload. Must" + " be at least %u bytes.", trtp_len, min_length); + return; + } + + if (amt < min_length) { + ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain" + " entire TRTP header. TRTP does not currently support fragmenting" + " TRTP headers across RTP payloads", amt); + return; + } + + uint8_t codec_type = buf[parse_offset ]; + uint8_t flags = buf[parse_offset + 1]; + uint8_t volume = buf[parse_offset + 2]; + parse_offset += 3; + trtp_header_len += 3; + + if (!setupSubstreamType(header_type, codec_type)) { + return; + } + + if (decoder_ != NULL) { + decoder_->setRenderVolume(volume); + } + + // TODO : move all of the constant flag and offset definitions for TRTP up + // into some sort of common header file. + if (waiting_for_rap_ && !(flags & 0x08)) { + ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP."); + return; + } + + if (flags & 0x10) { + ALOGV("Dropping TRTP Audio Payload with aux codec data present (only" + " handle MP3 right now, and it has no aux data)"); + return; + } + + // OK - everything left is just payload. Compute the payload size, start + // the buffer in progress and pack as much payload as we can into it. If + // the payload is finished once we are done, go ahead and send the payload + // to the decoder. + expected_buffer_size_ = trtp_len - trtp_header_len; + if (!expected_buffer_size_) { + ALOGV("Dropping TRTP Audio Payload with 0 Access Unit length"); + return; + } + + CHECK(amt >= trtp_header_len); + uint32_t todo = amt - trtp_header_len; + if (expected_buffer_size_ < todo) { + ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;" + " dropping payload.", todo, expected_buffer_size_); + return; + } + + buffer_filled_ = 0; + buffer_in_progress_ = new MediaBuffer(expected_buffer_size_); + if ((NULL == buffer_in_progress_) || + (NULL == buffer_in_progress_->data())) { + ALOGV("Failed to allocate MediaBuffer of length %u", + expected_buffer_size_); + cleanupBufferInProgress(); + return; + } + + sp<MetaData> meta = buffer_in_progress_->meta_data(); + if (meta == NULL) { + ALOGV("Missing metadata structure in allocated MediaBuffer; dropping" + " payload"); + cleanupBufferInProgress(); + return; + } + + // TODO : set this based on the codec type indicated in the TRTP stream. + // Right now, we only support MP3, so the choice is obvious. + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + if (ts_valid) { + meta->setInt64(kKeyTime, ts); + } + + if (amt > 0) { + uint8_t* tgt = + reinterpret_cast<uint8_t*>(buffer_in_progress_->data()); + memcpy(tgt + buffer_filled_, buf + trtp_header_len, todo); + buffer_filled_ += amt; + } + + if (buffer_filled_ >= expected_buffer_size_) { + processCompletedBuffer(); + } +} + +void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf, + uint32_t amt) { + if (shouldAbort(__PRETTY_FUNCTION__)) { + return; + } + + if (NULL == buffer_in_progress_) { + ALOGV("TRTP Receiver skipping payload continuation; no buffer currently" + " in progress."); + return; + } + + CHECK(buffer_filled_ < expected_buffer_size_); + uint32_t buffer_left = expected_buffer_size_ - buffer_filled_; + if (amt > buffer_left) { + ALOGV("Extra data (%u > %u) present in continued TRTP Audio Payload;" + " dropping payload.", amt, buffer_left); + cleanupBufferInProgress(); + return; + } + + if (amt > 0) { + uint8_t* tgt = + reinterpret_cast<uint8_t*>(buffer_in_progress_->data()); + memcpy(tgt + buffer_filled_, buf, amt); + buffer_filled_ += amt; + } + + if (buffer_filled_ >= expected_buffer_size_) { + processCompletedBuffer(); + } +} + +void AAH_RXPlayer::Substream::processCompletedBuffer() { + const uint8_t* buffer_data = NULL; + int sample_rate; + int channel_count; + size_t frame_size; + status_t res; + + CHECK(NULL != buffer_in_progress_); + + if (decoder_ == NULL) { + ALOGV("Dropping complete buffer, no decoder pump allocated"); + goto bailout; + } + + buffer_data = reinterpret_cast<const uint8_t*>(buffer_in_progress_->data()); + if (buffer_in_progress_->size() < 4) { + ALOGV("MP3 payload too short to contain header, dropping payload."); + goto bailout; + } + + // Extract the channel count and the sample rate from the MP3 header. The + // stagefright MP3 requires that these be delivered before decoing can + // begin. + if (!GetMPEGAudioFrameSize(U32_AT(buffer_data), + &frame_size, + &sample_rate, + &channel_count, + NULL, + NULL)) { + ALOGV("Failed to parse MP3 header in payload, droping payload."); + goto bailout; + } + + + // Make sure that our substream metadata is set up properly. If there has + // been a format change, be sure to reset the underlying decoder. In + // stagefright, it seems like the only way to do this is to destroy and + // recreate the decoder. + if (substream_meta_ == NULL) { + substream_meta_ = new MetaData(); + + if (substream_meta_ == NULL) { + ALOGE("Failed to allocate MetaData structure for substream"); + goto bailout; + } + + substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG); + substream_meta_->setInt32 (kKeyChannelCount, channel_count); + substream_meta_->setInt32 (kKeySampleRate, sample_rate); + } else { + int32_t prev_sample_rate; + int32_t prev_channel_count; + substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate); + substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count); + + if ((prev_channel_count != channel_count) || + (prev_sample_rate != sample_rate)) { + ALOGW("Format change detected, forcing decoder reset."); + cleanupDecoder(); + + substream_meta_->setInt32(kKeyChannelCount, channel_count); + substream_meta_->setInt32(kKeySampleRate, sample_rate); + } + } + + // If our decoder has not be set up, do so now. + res = decoder_->init(substream_meta_); + if (OK != res) { + ALOGE("Failed to init decoder (res = %d)", res); + cleanupDecoder(); + substream_meta_ = NULL; + goto bailout; + } + + // Queue the payload for decode. + res = decoder_->queueForDecode(buffer_in_progress_); + + if (res != OK) { + ALOGD("Failed to queue payload for decode, resetting decoder pump!" + " (res = %d)", res); + status_ = res; + cleanupDecoder(); + cleanupBufferInProgress(); + } + + // NULL out buffer_in_progress before calling the cleanup helper. + // + // MediaBuffers use something of a hybrid ref-counting pattern which prevent + // the AAH_DecoderPump's input queue from adding their own reference to the + // MediaBuffer. MediaBuffers start life with a reference count of 0, as + // well as an observer which starts as NULL. Before being given an + // observer, the ref count cannot be allowed to become non-zero as it will + // cause calls to release() to assert. Basically, before a MediaBuffer has + // an observer, they behave like non-ref counted obects where release() + // serves the roll of delete. After a MediaBuffer has an observer, they + // become more like ref counted objects where add ref and release can be + // used, and when the ref count hits zero, the MediaBuffer is handed off to + // the observer. + // + // Given all of this, when we give the buffer to the decoder pump to wait in + // the to-be-processed queue, the decoder cannot add a ref to the buffer as + // it would in a traditional ref counting system. Instead it needs to + // "steal" the non-existent ref. In the case of queue failure, we need to + // make certain to release this non-existent reference so that the buffer is + // cleaned up during the cleanupBufferInProgress helper. In the case of a + // successful queue operation, we need to make certain that the + // cleanupBufferInProgress helper does not release the buffer since it needs + // to remain alive in the queue. We acomplish this by NULLing out the + // buffer pointer before calling the cleanup helper. + buffer_in_progress_ = NULL; + +bailout: + cleanupBufferInProgress(); +} + + +void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) { + if (decoder_ != NULL) { + decoder_->setRenderTSTransform(trans); + } +} + +bool AAH_RXPlayer::Substream::isAboutToUnderflow() { + if (decoder_ == NULL) { + return false; + } + + return decoder_->isAboutToUnderflow(kAboutToUnderflowThreshold); +} + +bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type, + uint8_t codec_type) { + // Sanity check the codec type. Right now we only support MP3. Also check + // for conflicts with previously delivered codec types. + if (substream_details_known_ && (codec_type != codec_type_)) { + ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does not" + " match previously received codec type (%u)", + ssrc_, codec_type, codec_type_); + return false; + } + + if (codec_type != 0x03) { + ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported codec" + " type (%u)", ssrc_, codec_type); + return false; + } + + if (!substream_details_known_) { + substream_type_ = substream_type; + codec_type_ = codec_type; + substream_details_known_ = true; + } + + return true; +} + +} // namespace android diff --git a/media/libaah_rtp/aah_tx_packet.cpp b/media/libaah_rtp/aah_tx_packet.cpp new file mode 100644 index 000000000000..3f6e0e9520d6 --- /dev/null +++ b/media/libaah_rtp/aah_tx_packet.cpp @@ -0,0 +1,331 @@ +/* + * 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 "LibAAH_RTP" +#include <utils/Log.h> + +#include <arpa/inet.h> +#include <string.h> + +#include <media/stagefright/foundation/ADebug.h> + +#include "aah_tx_packet.h" + +namespace android { + +const int TRTPPacket::kRTPHeaderLen; +const uint32_t TRTPPacket::kTRTPEpochMask; + +TRTPPacket::~TRTPPacket() { + delete mPacket; +} + +/*** TRTP packet properties ***/ + +void TRTPPacket::setSeqNumber(uint16_t val) { + mSeqNumber = val; + + if (mIsPacked) { + const int kTRTPSeqNumberOffset = 2; + uint16_t* buf = reinterpret_cast<uint16_t*>( + mPacket + kTRTPSeqNumberOffset); + *buf = htons(mSeqNumber); + } +} + +uint16_t TRTPPacket::getSeqNumber() const { + return mSeqNumber; +} + +void TRTPPacket::setPTS(int64_t val) { + CHECK(!mIsPacked); + mPTS = val; + mPTSValid = true; +} + +int64_t TRTPPacket::getPTS() const { + return mPTS; +} + +void TRTPPacket::setEpoch(uint32_t val) { + mEpoch = val; + + if (mIsPacked) { + const int kTRTPEpochOffset = 8; + uint32_t* buf = reinterpret_cast<uint32_t*>( + mPacket + kTRTPEpochOffset); + uint32_t val = ntohl(*buf); + val &= ~(kTRTPEpochMask << kTRTPEpochShift); + val |= (mEpoch & kTRTPEpochMask) << kTRTPEpochShift; + *buf = htonl(val); + } +} + +void TRTPPacket::setProgramID(uint16_t val) { + CHECK(!mIsPacked); + mProgramID = val; +} + +void TRTPPacket::setSubstreamID(uint16_t val) { + CHECK(!mIsPacked); + mSubstreamID = val; +} + + +void TRTPPacket::setClockTransform(const LinearTransform& trans) { + CHECK(!mIsPacked); + mClockTranform = trans; + mClockTranformValid = true; +} + +uint8_t* TRTPPacket::getPacket() const { + CHECK(mIsPacked); + return mPacket; +} + +int TRTPPacket::getPacketLen() const { + CHECK(mIsPacked); + return mPacketLen; +} + +void TRTPPacket::setExpireTime(nsecs_t val) { + CHECK(!mIsPacked); + mExpireTime = val; +} + +nsecs_t TRTPPacket::getExpireTime() const { + return mExpireTime; +} + +/*** TRTP audio packet properties ***/ + +void TRTPAudioPacket::setCodecType(TRTPAudioCodecType val) { + CHECK(!mIsPacked); + mCodecType = val; +} + +void TRTPAudioPacket::setRandomAccessPoint(bool val) { + CHECK(!mIsPacked); + mRandomAccessPoint = val; +} + +void TRTPAudioPacket::setDropable(bool val) { + CHECK(!mIsPacked); + mDropable = val; +} + +void TRTPAudioPacket::setDiscontinuity(bool val) { + CHECK(!mIsPacked); + mDiscontinuity = val; +} + +void TRTPAudioPacket::setEndOfStream(bool val) { + CHECK(!mIsPacked); + mEndOfStream = val; +} + +void TRTPAudioPacket::setVolume(uint8_t val) { + CHECK(!mIsPacked); + mVolume = val; +} + +void TRTPAudioPacket::setAccessUnitData(void* data, int len) { + CHECK(!mIsPacked); + mAccessUnitData = data; + mAccessUnitLen = len; +} + +/*** TRTP control packet properties ***/ + +void TRTPControlPacket::setCommandID(TRTPCommandID val) { + CHECK(!mIsPacked); + mCommandID = val; +} + +/*** TRTP packet serializers ***/ + +void TRTPPacket::writeU8(uint8_t*& buf, uint8_t val) { + *buf = val; + buf++; +} + +void TRTPPacket::writeU16(uint8_t*& buf, uint16_t val) { + *reinterpret_cast<uint16_t*>(buf) = htons(val); + buf += 2; +} + +void TRTPPacket::writeU32(uint8_t*& buf, uint32_t val) { + *reinterpret_cast<uint32_t*>(buf) = htonl(val); + buf += 4; +} + +void TRTPPacket::writeU64(uint8_t*& buf, uint64_t val) { + buf[0] = static_cast<uint8_t>(val >> 56); + buf[1] = static_cast<uint8_t>(val >> 48); + buf[2] = static_cast<uint8_t>(val >> 40); + buf[3] = static_cast<uint8_t>(val >> 32); + buf[4] = static_cast<uint8_t>(val >> 24); + buf[5] = static_cast<uint8_t>(val >> 16); + buf[6] = static_cast<uint8_t>(val >> 8); + buf[7] = static_cast<uint8_t>(val); + buf += 8; +} + +void TRTPPacket::writeTRTPHeader(uint8_t*& buf, + bool isFirstFragment, + int totalPacketLen) { + // RTP header + writeU8(buf, + ((mVersion & 0x03) << 6) | + (static_cast<int>(mPadding) << 5) | + (static_cast<int>(mExtension) << 4) | + (mCsrcCount & 0x0F)); + writeU8(buf, + (static_cast<int>(isFirstFragment) << 7) | + (mPayloadType & 0x7F)); + writeU16(buf, mSeqNumber); + if (isFirstFragment && mPTSValid) { + writeU32(buf, mPTS & 0xFFFFFFFF); + } else { + writeU32(buf, 0); + } + writeU32(buf, + ((mEpoch & kTRTPEpochMask) << kTRTPEpochShift) | + ((mProgramID & 0x1F) << 5) | + (mSubstreamID & 0x1F)); + + // TRTP header + writeU8(buf, mTRTPVersion); + writeU8(buf, + ((mTRTPHeaderType & 0x0F) << 4) | + (mClockTranformValid ? 0x02 : 0x00) | + (mPTSValid ? 0x01 : 0x00)); + writeU32(buf, totalPacketLen - kRTPHeaderLen); + if (mPTSValid) { + writeU32(buf, mPTS >> 32); + } + + if (mClockTranformValid) { + writeU64(buf, mClockTranform.a_zero); + writeU32(buf, mClockTranform.a_to_b_numer); + writeU32(buf, mClockTranform.a_to_b_denom); + writeU64(buf, mClockTranform.b_zero); + } +} + +bool TRTPAudioPacket::pack() { + if (mIsPacked) { + return false; + } + + int packetLen = kRTPHeaderLen + + mAccessUnitLen + + TRTPHeaderLen(); + + // TODO : support multiple fragments + const int kMaxUDPPayloadLen = 65507; + if (packetLen > kMaxUDPPayloadLen) { + return false; + } + + mPacket = new uint8_t[packetLen]; + if (!mPacket) { + return false; + } + + mPacketLen = packetLen; + + uint8_t* cur = mPacket; + + writeTRTPHeader(cur, true, packetLen); + writeU8(cur, mCodecType); + writeU8(cur, + (static_cast<int>(mRandomAccessPoint) << 3) | + (static_cast<int>(mDropable) << 2) | + (static_cast<int>(mDiscontinuity) << 1) | + (static_cast<int>(mEndOfStream))); + writeU8(cur, mVolume); + + memcpy(cur, mAccessUnitData, mAccessUnitLen); + + mIsPacked = true; + return true; +} + +int TRTPPacket::TRTPHeaderLen() const { + // 6 bytes for version, payload type, flags and length. An additional 4 if + // there are upper timestamp bits present and another 24 if there is a clock + // transformation present. + return 6 + + (mClockTranformValid ? 24 : 0) + + (mPTSValid ? 4 : 0); +} + +int TRTPAudioPacket::TRTPHeaderLen() const { + // TRTPPacket::TRTPHeaderLen() for the base TRTPHeader. 3 bytes for audio's + // codec type, flags and volume field. Another 5 bytes if the codec type is + // PCM and we are sending sample rate/channel count. as well as however long + // the aux data (if present) is. + + int pcmParamLength; + switch(mCodecType) { + case kCodecPCMBigEndian: + case kCodecPCMLittleEndian: + pcmParamLength = 5; + break; + + default: + pcmParamLength = 0; + break; + } + + + // TODO : properly compute aux data length. Currently, nothing + // uses aux data, so its length is always 0. + int auxDataLength = 0; + return TRTPPacket::TRTPHeaderLen() + + 3 + + auxDataLength + + pcmParamLength; +} + +bool TRTPControlPacket::pack() { + if (mIsPacked) { + return false; + } + + // command packets contain a 2-byte command ID + int packetLen = kRTPHeaderLen + + TRTPHeaderLen() + + 2; + + mPacket = new uint8_t[packetLen]; + if (!mPacket) { + return false; + } + + mPacketLen = packetLen; + + uint8_t* cur = mPacket; + + writeTRTPHeader(cur, true, packetLen); + writeU16(cur, mCommandID); + + mIsPacked = true; + return true; +} + +} // namespace android diff --git a/media/libaah_rtp/aah_tx_packet.h b/media/libaah_rtp/aah_tx_packet.h new file mode 100644 index 000000000000..833803ea1bef --- /dev/null +++ b/media/libaah_rtp/aah_tx_packet.h @@ -0,0 +1,191 @@ +/* + * 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 __AAH_TX_PACKET_H__ +#define __AAH_TX_PACKET_H__ + +#include <media/stagefright/foundation/ABase.h> +#include <utils/LinearTransform.h> +#include <utils/RefBase.h> +#include <utils/Timers.h> + +namespace android { + +class TRTPPacket : public RefBase { + protected: + enum TRTPHeaderType { + kHeaderTypeAudio = 1, + kHeaderTypeVideo = 2, + kHeaderTypeSubpicture = 3, + kHeaderTypeControl = 4, + }; + + TRTPPacket(TRTPHeaderType headerType) + : mIsPacked(false) + , mVersion(2) + , mPadding(false) + , mExtension(false) + , mCsrcCount(0) + , mPayloadType(100) + , mSeqNumber(0) + , mPTSValid(false) + , mPTS(0) + , mEpoch(0) + , mProgramID(0) + , mSubstreamID(0) + , mClockTranformValid(false) + , mTRTPVersion(1) + , mTRTPLength(0) + , mTRTPHeaderType(headerType) + , mPacket(NULL) + , mPacketLen(0) { } + + public: + virtual ~TRTPPacket(); + + void setSeqNumber(uint16_t val); + uint16_t getSeqNumber() const; + + void setPTS(int64_t val); + int64_t getPTS() const; + + void setEpoch(uint32_t val); + void setProgramID(uint16_t val); + void setSubstreamID(uint16_t val); + void setClockTransform(const LinearTransform& trans); + + uint8_t* getPacket() const; + int getPacketLen() const; + + void setExpireTime(nsecs_t val); + nsecs_t getExpireTime() const; + + virtual bool pack() = 0; + + // mask for the number of bits in a TRTP epoch + static const uint32_t kTRTPEpochMask = (1 << 22) - 1; + static const int kTRTPEpochShift = 10; + + protected: + static const int kRTPHeaderLen = 12; + virtual int TRTPHeaderLen() const; + + void writeTRTPHeader(uint8_t*& buf, + bool isFirstFragment, + int totalPacketLen); + + void writeU8(uint8_t*& buf, uint8_t val); + void writeU16(uint8_t*& buf, uint16_t val); + void writeU32(uint8_t*& buf, uint32_t val); + void writeU64(uint8_t*& buf, uint64_t val); + + bool mIsPacked; + + uint8_t mVersion; + bool mPadding; + bool mExtension; + uint8_t mCsrcCount; + uint8_t mPayloadType; + uint16_t mSeqNumber; + bool mPTSValid; + int64_t mPTS; + uint32_t mEpoch; + uint16_t mProgramID; + uint16_t mSubstreamID; + LinearTransform mClockTranform; + bool mClockTranformValid; + uint8_t mTRTPVersion; + uint32_t mTRTPLength; + TRTPHeaderType mTRTPHeaderType; + + uint8_t* mPacket; + int mPacketLen; + + nsecs_t mExpireTime; + + DISALLOW_EVIL_CONSTRUCTORS(TRTPPacket); +}; + +class TRTPAudioPacket : public TRTPPacket { + public: + TRTPAudioPacket() + : TRTPPacket(kHeaderTypeAudio) + , mCodecType(kCodecInvalid) + , mRandomAccessPoint(false) + , mDropable(false) + , mDiscontinuity(false) + , mEndOfStream(false) + , mVolume(0) + , mAccessUnitData(NULL) { } + + enum TRTPAudioCodecType { + kCodecInvalid = 0, + kCodecPCMBigEndian = 1, + kCodecPCMLittleEndian = 2, + kCodecMPEG1Audio = 3, + }; + + void setCodecType(TRTPAudioCodecType val); + void setRandomAccessPoint(bool val); + void setDropable(bool val); + void setDiscontinuity(bool val); + void setEndOfStream(bool val); + void setVolume(uint8_t val); + void setAccessUnitData(void* data, int len); + + virtual bool pack(); + + protected: + virtual int TRTPHeaderLen() const; + + private: + TRTPAudioCodecType mCodecType; + bool mRandomAccessPoint; + bool mDropable; + bool mDiscontinuity; + bool mEndOfStream; + uint8_t mVolume; + void* mAccessUnitData; + int mAccessUnitLen; + + DISALLOW_EVIL_CONSTRUCTORS(TRTPAudioPacket); +}; + +class TRTPControlPacket : public TRTPPacket { + public: + TRTPControlPacket() + : TRTPPacket(kHeaderTypeControl) + , mCommandID(kCommandNop) {} + + enum TRTPCommandID { + kCommandNop = 1, + kCommandFlush = 2, + kCommandEOS = 3, + }; + + void setCommandID(TRTPCommandID val); + + virtual bool pack(); + + private: + TRTPCommandID mCommandID; + + DISALLOW_EVIL_CONSTRUCTORS(TRTPControlPacket); +}; + +} // namespace android + +#endif // __AAH_TX_PLAYER_H__ diff --git a/media/libaah_rtp/aah_tx_player.cpp b/media/libaah_rtp/aah_tx_player.cpp new file mode 100644 index 000000000000..a79a9891ef03 --- /dev/null +++ b/media/libaah_rtp/aah_tx_player.cpp @@ -0,0 +1,1139 @@ +/* + * 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 "LibAAH_RTP" +#include <utils/Log.h> + +#define __STDC_FORMAT_MACROS +#include <inttypes.h> +#include <netdb.h> +#include <netinet/ip.h> + +#include <common_time/cc_helper.h> +#include <media/IMediaPlayer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/FileSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MetaData.h> +#include <utils/Timers.h> + +#include "aah_tx_packet.h" +#include "aah_tx_player.h" + +namespace android { + +static int64_t kLowWaterMarkUs = 2000000ll; // 2secs +static int64_t kHighWaterMarkUs = 10000000ll; // 10secs +static const size_t kLowWaterMarkBytes = 40000; +static const size_t kHighWaterMarkBytes = 200000; + +// When we start up, how much lead time should we put on the first access unit? +static const int64_t kAAHStartupLeadTimeUs = 300000LL; + +// How much time do we attempt to lead the clock by in steady state? +static const int64_t kAAHBufferTimeUs = 1000000LL; + +// how long do we keep data in our retransmit buffer after sending it. +const int64_t AAH_TXPlayer::kAAHRetryKeepAroundTimeNs = + kAAHBufferTimeUs * 1100; + +sp<MediaPlayerBase> createAAH_TXPlayer() { + sp<MediaPlayerBase> ret = new AAH_TXPlayer(); + return ret; +} + +template <typename T> static T clamp(T val, T min, T max) { + if (val < min) { + return min; + } else if (val > max) { + return max; + } else { + return val; + } +} + +struct AAH_TXEvent : public TimedEventQueue::Event { + AAH_TXEvent(AAH_TXPlayer *player, + void (AAH_TXPlayer::*method)()) : mPlayer(player) + , mMethod(method) {} + + protected: + virtual ~AAH_TXEvent() {} + + virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) { + (mPlayer->*mMethod)(); + } + + private: + AAH_TXPlayer *mPlayer; + void (AAH_TXPlayer::*mMethod)(); + + AAH_TXEvent(const AAH_TXEvent &); + AAH_TXEvent& operator=(const AAH_TXEvent &); +}; + +AAH_TXPlayer::AAH_TXPlayer() + : mQueueStarted(false) + , mFlags(0) + , mExtractorFlags(0) { + DataSource::RegisterDefaultSniffers(); + + mBufferingEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onBufferingUpdate); + mBufferingEventPending = false; + + mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio); + mPumpAudioEventPending = false; + + reset_l(); +} + +AAH_TXPlayer::~AAH_TXPlayer() { + if (mQueueStarted) { + mQueue.stop(); + } + + reset_l(); +} + +void AAH_TXPlayer::cancelPlayerEvents(bool keepBufferingGoing) { + if (!keepBufferingGoing) { + mQueue.cancelEvent(mBufferingEvent->eventID()); + mBufferingEventPending = false; + + mQueue.cancelEvent(mPumpAudioEvent->eventID()); + mPumpAudioEventPending = false; + } +} + +status_t AAH_TXPlayer::initCheck() { + // Check for the presense of the common time service by attempting to query + // for CommonTime's frequency. If we get an error back, we cannot talk to + // the service at all and should abort now. + status_t res; + uint64_t freq; + res = mCCHelper.getCommonFreq(&freq); + if (OK != res) { + ALOGE("Failed to connect to common time service! (res %d)", res); + return res; + } + + return OK; +} + +status_t AAH_TXPlayer::setDataSource( + const char *url, + const KeyedVector<String8, String8> *headers) { + Mutex::Autolock autoLock(mLock); + return setDataSource_l(url, headers); +} + +status_t AAH_TXPlayer::setDataSource_l( + const char *url, + const KeyedVector<String8, String8> *headers) { + reset_l(); + + // the URL must consist of "aahTX://" followed by the real URL of + // the data source + const char *kAAHPrefix = "aahTX://"; + if (strncasecmp(url, kAAHPrefix, strlen(kAAHPrefix))) { + return INVALID_OPERATION; + } + + mUri.setTo(url + strlen(kAAHPrefix)); + + if (headers) { + mUriHeaders = *headers; + + ssize_t index = mUriHeaders.indexOfKey(String8("x-hide-urls-from-log")); + if (index >= 0) { + // Browser is in "incognito" mode, suppress logging URLs. + + // This isn't something that should be passed to the server. + mUriHeaders.removeItemsAt(index); + + mFlags |= INCOGNITO; + } + } + + // The URL may optionally contain a "#" character followed by a Skyjam + // cookie. Ideally the cookie header should just be passed in the headers + // argument, but the Java API for supplying headers is apparently not yet + // exposed in the SDK used by application developers. + const char kSkyjamCookieDelimiter = '#'; + char* skyjamCookie = strrchr(mUri.string(), kSkyjamCookieDelimiter); + if (skyjamCookie) { + skyjamCookie++; + mUriHeaders.add(String8("Cookie"), String8(skyjamCookie)); + mUri = String8(mUri.string(), skyjamCookie - mUri.string()); + } + + return OK; +} + +status_t AAH_TXPlayer::setDataSource(int fd, int64_t offset, int64_t length) { + Mutex::Autolock autoLock(mLock); + + reset_l(); + + sp<DataSource> dataSource = new FileSource(dup(fd), offset, length); + + status_t err = dataSource->initCheck(); + + if (err != OK) { + return err; + } + + mFileSource = dataSource; + + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + + if (extractor == NULL) { + return UNKNOWN_ERROR; + } + + return setDataSource_l(extractor); +} + +status_t AAH_TXPlayer::setVideoSurface(const sp<Surface>& surface) { + return OK; +} + +status_t AAH_TXPlayer::setVideoSurfaceTexture( + const sp<ISurfaceTexture>& surfaceTexture) { + return OK; +} + +status_t AAH_TXPlayer::prepare() { + return INVALID_OPERATION; +} + +status_t AAH_TXPlayer::prepareAsync() { + Mutex::Autolock autoLock(mLock); + + return prepareAsync_l(); +} + +status_t AAH_TXPlayer::prepareAsync_l() { + if (mFlags & PREPARING) { + return UNKNOWN_ERROR; // async prepare already pending + } + + mAAH_Sender = AAH_TXSender::GetInstance(); + if (mAAH_Sender == NULL) { + return NO_MEMORY; + } + + if (!mQueueStarted) { + mQueue.start(); + mQueueStarted = true; + } + + mFlags |= PREPARING; + mAsyncPrepareEvent = new AAH_TXEvent( + this, &AAH_TXPlayer::onPrepareAsyncEvent); + + mQueue.postEvent(mAsyncPrepareEvent); + + return OK; +} + +status_t AAH_TXPlayer::finishSetDataSource_l() { + sp<DataSource> dataSource; + + if (!strncasecmp("http://", mUri.string(), 7) || + !strncasecmp("https://", mUri.string(), 8)) { + + mConnectingDataSource = HTTPBase::Create( + (mFlags & INCOGNITO) + ? HTTPBase::kFlagIncognito + : 0); + + mLock.unlock(); + status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders); + mLock.lock(); + + if (err != OK) { + mConnectingDataSource.clear(); + + ALOGI("mConnectingDataSource->connect() returned %d", err); + return err; + } + + mCachedSource = new NuCachedSource2(mConnectingDataSource); + mConnectingDataSource.clear(); + + dataSource = mCachedSource; + + // We're going to prefill the cache before trying to instantiate + // the extractor below, as the latter is an operation that otherwise + // could block on the datasource for a significant amount of time. + // During that time we'd be unable to abort the preparation phase + // without this prefill. + + mLock.unlock(); + + for (;;) { + status_t finalStatus; + size_t cachedDataRemaining = + mCachedSource->approxDataRemaining(&finalStatus); + + if (finalStatus != OK || + cachedDataRemaining >= kHighWaterMarkBytes || + (mFlags & PREPARE_CANCELLED)) { + break; + } + + usleep(200000); + } + + mLock.lock(); + + if (mFlags & PREPARE_CANCELLED) { + ALOGI("Prepare cancelled while waiting for initial cache fill."); + return UNKNOWN_ERROR; + } + } else { + dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders); + } + + if (dataSource == NULL) { + return UNKNOWN_ERROR; + } + + sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource); + + if (extractor == NULL) { + return UNKNOWN_ERROR; + } + + return setDataSource_l(extractor); +} + +status_t AAH_TXPlayer::setDataSource_l(const sp<MediaExtractor> &extractor) { + // Attempt to approximate overall stream bitrate by summing all + // tracks' individual bitrates, if not all of them advertise bitrate, + // we have to fail. + + int64_t totalBitRate = 0; + + for (size_t i = 0; i < extractor->countTracks(); ++i) { + sp<MetaData> meta = extractor->getTrackMetaData(i); + + int32_t bitrate; + if (!meta->findInt32(kKeyBitRate, &bitrate)) { + totalBitRate = -1; + break; + } + + totalBitRate += bitrate; + } + + mBitrate = totalBitRate; + + ALOGV("mBitrate = %lld bits/sec", mBitrate); + + bool haveAudio = false; + for (size_t i = 0; i < extractor->countTracks(); ++i) { + sp<MetaData> meta = extractor->getTrackMetaData(i); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strncasecmp(mime, "audio/", 6)) { + mAudioSource = extractor->getTrack(i); + CHECK(mAudioSource != NULL); + haveAudio = true; + break; + } + } + + if (!haveAudio) { + return UNKNOWN_ERROR; + } + + mExtractorFlags = extractor->flags(); + + return OK; +} + +void AAH_TXPlayer::abortPrepare(status_t err) { + CHECK(err != OK); + + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err); + + mPrepareResult = err; + mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED); + mPreparedCondition.broadcast(); +} + +void AAH_TXPlayer::onPrepareAsyncEvent() { + Mutex::Autolock autoLock(mLock); + + if (mFlags & PREPARE_CANCELLED) { + ALOGI("prepare was cancelled before doing anything"); + abortPrepare(UNKNOWN_ERROR); + return; + } + + if (mUri.size() > 0) { + status_t err = finishSetDataSource_l(); + + if (err != OK) { + abortPrepare(err); + return; + } + } + + mAudioSource->getFormat()->findInt64(kKeyDuration, &mDurationUs); + + status_t err = mAudioSource->start(); + if (err != OK) { + ALOGI("failed to start audio source, err=%d", err); + abortPrepare(err); + return; + } + + mFlags |= PREPARING_CONNECTED; + + if (mCachedSource != NULL) { + postBufferingEvent_l(); + } else { + finishAsyncPrepare_l(); + } +} + +void AAH_TXPlayer::finishAsyncPrepare_l() { + notifyListener_l(MEDIA_PREPARED); + + mPrepareResult = OK; + mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED); + mFlags |= PREPARED; + mPreparedCondition.broadcast(); +} + +status_t AAH_TXPlayer::start() { + Mutex::Autolock autoLock(mLock); + + mFlags &= ~CACHE_UNDERRUN; + + return play_l(); +} + +status_t AAH_TXPlayer::play_l() { + if (mFlags & PLAYING) { + return OK; + } + + if (!(mFlags & PREPARED)) { + return INVALID_OPERATION; + } + + { + Mutex::Autolock lock(mEndpointLock); + if (!mEndpointValid) { + return INVALID_OPERATION; + } + if (!mEndpointRegistered) { + mProgramID = mAAH_Sender->registerEndpoint(mEndpoint); + mEndpointRegistered = true; + } + } + + mFlags |= PLAYING; + + updateClockTransform_l(false); + + postPumpAudioEvent_l(-1); + + return OK; +} + +status_t AAH_TXPlayer::stop() { + status_t ret = pause(); + sendEOS_l(); + return ret; +} + +status_t AAH_TXPlayer::pause() { + Mutex::Autolock autoLock(mLock); + + mFlags &= ~CACHE_UNDERRUN; + + return pause_l(); +} + +status_t AAH_TXPlayer::pause_l(bool doClockUpdate) { + if (!(mFlags & PLAYING)) { + return OK; + } + + cancelPlayerEvents(true /* keepBufferingGoing */); + + mFlags &= ~PLAYING; + + if (doClockUpdate) { + updateClockTransform_l(true); + } + + return OK; +} + +void AAH_TXPlayer::updateClockTransform_l(bool pause) { + // record the new pause status so that onPumpAudio knows what rate to apply + // when it initializes the transform + mPlayRateIsPaused = pause; + + // if we haven't yet established a valid clock transform, then we can't + // do anything here + if (!mCurrentClockTransformValid) { + return; + } + + // sample the current common time + int64_t commonTimeNow; + if (OK != mCCHelper.getCommonTime(&commonTimeNow)) { + ALOGE("updateClockTransform_l get common time failed"); + mCurrentClockTransformValid = false; + return; + } + + // convert the current common time to media time using the old + // transform + int64_t mediaTimeNow; + if (!mCurrentClockTransform.doReverseTransform( + commonTimeNow, &mediaTimeNow)) { + ALOGE("updateClockTransform_l reverse transform failed"); + mCurrentClockTransformValid = false; + return; + } + + // calculate a new transform that preserves the old transform's + // result for the current time + mCurrentClockTransform.a_zero = mediaTimeNow; + mCurrentClockTransform.b_zero = commonTimeNow; + mCurrentClockTransform.a_to_b_numer = 1; + mCurrentClockTransform.a_to_b_denom = pause ? 0 : 1; + + // send a packet announcing the new transform + sp<TRTPControlPacket> packet = new TRTPControlPacket(); + packet->setClockTransform(mCurrentClockTransform); + packet->setCommandID(TRTPControlPacket::kCommandNop); + queuePacketToSender_l(packet); +} + +void AAH_TXPlayer::sendEOS_l() { + sp<TRTPControlPacket> packet = new TRTPControlPacket(); + packet->setCommandID(TRTPControlPacket::kCommandEOS); + queuePacketToSender_l(packet); +} + +bool AAH_TXPlayer::isPlaying() { + return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN); +} + +status_t AAH_TXPlayer::seekTo(int msec) { + if (mExtractorFlags & MediaExtractor::CAN_SEEK) { + Mutex::Autolock autoLock(mLock); + return seekTo_l(static_cast<int64_t>(msec) * 1000); + } + + notifyListener_l(MEDIA_SEEK_COMPLETE); + return OK; +} + +status_t AAH_TXPlayer::seekTo_l(int64_t timeUs) { + mIsSeeking = true; + mSeekTimeUs = timeUs; + + mCurrentClockTransformValid = false; + mLastQueuedMediaTimePTSValid = false; + + // send a flush command packet + sp<TRTPControlPacket> packet = new TRTPControlPacket(); + packet->setCommandID(TRTPControlPacket::kCommandFlush); + queuePacketToSender_l(packet); + + return OK; +} + +status_t AAH_TXPlayer::getCurrentPosition(int *msec) { + if (!msec) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mLock); + + int position; + + if (mIsSeeking) { + position = mSeekTimeUs / 1000; + } else if (mCurrentClockTransformValid) { + // sample the current common time + int64_t commonTimeNow; + if (OK != mCCHelper.getCommonTime(&commonTimeNow)) { + ALOGE("getCurrentPosition get common time failed"); + return INVALID_OPERATION; + } + + int64_t mediaTimeNow; + if (!mCurrentClockTransform.doReverseTransform(commonTimeNow, + &mediaTimeNow)) { + ALOGE("getCurrentPosition reverse transform failed"); + return INVALID_OPERATION; + } + + position = static_cast<int>(mediaTimeNow / 1000); + } else { + position = 0; + } + + int duration; + if (getDuration_l(&duration) == OK) { + *msec = clamp(position, 0, duration); + } else { + *msec = (position >= 0) ? position : 0; + } + + return OK; +} + +status_t AAH_TXPlayer::getDuration(int* msec) { + if (!msec) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mLock); + + return getDuration_l(msec); +} + +status_t AAH_TXPlayer::getDuration_l(int* msec) { + if (mDurationUs < 0) { + return UNKNOWN_ERROR; + } + + *msec = (mDurationUs + 500) / 1000; + + return OK; +} + +status_t AAH_TXPlayer::reset() { + Mutex::Autolock autoLock(mLock); + reset_l(); + return OK; +} + +void AAH_TXPlayer::reset_l() { + if (mFlags & PREPARING) { + mFlags |= PREPARE_CANCELLED; + if (mConnectingDataSource != NULL) { + ALOGI("interrupting the connection process"); + mConnectingDataSource->disconnect(); + } + + if (mFlags & PREPARING_CONNECTED) { + // We are basically done preparing, we're just buffering + // enough data to start playback, we can safely interrupt that. + finishAsyncPrepare_l(); + } + } + + while (mFlags & PREPARING) { + mPreparedCondition.wait(mLock); + } + + cancelPlayerEvents(); + + sendEOS_l(); + + mCachedSource.clear(); + + if (mAudioSource != NULL) { + mAudioSource->stop(); + } + mAudioSource.clear(); + + mFlags = 0; + mExtractorFlags = 0; + + mDurationUs = -1; + mIsSeeking = false; + mSeekTimeUs = 0; + + mUri.setTo(""); + mUriHeaders.clear(); + + mFileSource.clear(); + + mBitrate = -1; + + { + Mutex::Autolock lock(mEndpointLock); + if (mAAH_Sender != NULL && mEndpointRegistered) { + mAAH_Sender->unregisterEndpoint(mEndpoint); + } + mEndpointRegistered = false; + mEndpointValid = false; + } + + mProgramID = 0; + + mAAH_Sender.clear(); + mLastQueuedMediaTimePTSValid = false; + mCurrentClockTransformValid = false; + mPlayRateIsPaused = false; + + mTRTPVolume = 255; +} + +status_t AAH_TXPlayer::setLooping(int loop) { + return OK; +} + +player_type AAH_TXPlayer::playerType() { + return AAH_TX_PLAYER; +} + +status_t AAH_TXPlayer::setParameter(int key, const Parcel &request) { + return ERROR_UNSUPPORTED; +} + +status_t AAH_TXPlayer::getParameter(int key, Parcel *reply) { + return ERROR_UNSUPPORTED; +} + +status_t AAH_TXPlayer::invoke(const Parcel& request, Parcel *reply) { + if (!reply) { + return BAD_VALUE; + } + + int32_t methodID; + status_t err = request.readInt32(&methodID); + if (err != android::OK) { + return err; + } + + switch (methodID) { + case kInvokeSetAAHDstIPPort: + case kInvokeSetAAHConfigBlob: { + if (mEndpointValid) { + return INVALID_OPERATION; + } + + String8 addr; + uint16_t port; + + if (methodID == kInvokeSetAAHDstIPPort) { + addr = String8(request.readString16()); + + int32_t port32; + err = request.readInt32(&port32); + if (err != android::OK) { + return err; + } + port = static_cast<uint16_t>(port32); + } else { + String8 blob(request.readString16()); + + char addr_buf[101]; + if (sscanf(blob.string(), "V1:%100s %" SCNu16, + addr_buf, &port) != 2) { + return BAD_VALUE; + } + if (addr.setTo(addr_buf) != OK) { + return NO_MEMORY; + } + } + + struct hostent* ent = gethostbyname(addr.string()); + if (ent == NULL) { + return ERROR_UNKNOWN_HOST; + } + if (!(ent->h_addrtype == AF_INET && ent->h_length == 4)) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mEndpointLock); + mEndpoint = AAH_TXSender::Endpoint( + reinterpret_cast<struct in_addr*>(ent->h_addr)->s_addr, + port); + mEndpointValid = true; + return OK; + }; + + default: + return INVALID_OPERATION; + } +} + +status_t AAH_TXPlayer::getMetadata(const media::Metadata::Filter& ids, + Parcel* records) { + using media::Metadata; + + Metadata metadata(records); + + metadata.appendBool(Metadata::kPauseAvailable, true); + metadata.appendBool(Metadata::kSeekBackwardAvailable, false); + metadata.appendBool(Metadata::kSeekForwardAvailable, false); + metadata.appendBool(Metadata::kSeekAvailable, false); + + return OK; +} + +status_t AAH_TXPlayer::setVolume(float leftVolume, float rightVolume) { + if (leftVolume != rightVolume) { + ALOGE("%s does not support per channel volume: %f, %f", + __PRETTY_FUNCTION__, leftVolume, rightVolume); + } + + float volume = clamp(leftVolume, 0.0f, 1.0f); + + Mutex::Autolock lock(mLock); + mTRTPVolume = static_cast<uint8_t>((leftVolume * 255.0) + 0.5); + + return OK; +} + +status_t AAH_TXPlayer::setAudioStreamType(audio_stream_type_t streamType) { + return OK; +} + +void AAH_TXPlayer::notifyListener_l(int msg, int ext1, int ext2) { + sendEvent(msg, ext1, ext2); +} + +bool AAH_TXPlayer::getBitrate_l(int64_t *bitrate) { + off64_t size; + if (mDurationUs >= 0 && + mCachedSource != NULL && + mCachedSource->getSize(&size) == OK) { + *bitrate = size * 8000000ll / mDurationUs; // in bits/sec + return true; + } + + if (mBitrate >= 0) { + *bitrate = mBitrate; + return true; + } + + *bitrate = 0; + + return false; +} + +// Returns true iff cached duration is available/applicable. +bool AAH_TXPlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) { + int64_t bitrate; + + if (mCachedSource != NULL && getBitrate_l(&bitrate)) { + status_t finalStatus; + size_t cachedDataRemaining = mCachedSource->approxDataRemaining( + &finalStatus); + *durationUs = cachedDataRemaining * 8000000ll / bitrate; + *eos = (finalStatus != OK); + return true; + } + + return false; +} + +void AAH_TXPlayer::ensureCacheIsFetching_l() { + if (mCachedSource != NULL) { + mCachedSource->resumeFetchingIfNecessary(); + } +} + +void AAH_TXPlayer::postBufferingEvent_l() { + if (mBufferingEventPending) { + return; + } + mBufferingEventPending = true; + mQueue.postEventWithDelay(mBufferingEvent, 1000000ll); +} + +void AAH_TXPlayer::postPumpAudioEvent_l(int64_t delayUs) { + if (mPumpAudioEventPending) { + return; + } + mPumpAudioEventPending = true; + mQueue.postEventWithDelay(mPumpAudioEvent, delayUs < 0 ? 10000 : delayUs); +} + +void AAH_TXPlayer::onBufferingUpdate() { + Mutex::Autolock autoLock(mLock); + if (!mBufferingEventPending) { + return; + } + mBufferingEventPending = false; + + if (mCachedSource != NULL) { + status_t finalStatus; + size_t cachedDataRemaining = mCachedSource->approxDataRemaining( + &finalStatus); + bool eos = (finalStatus != OK); + + if (eos) { + if (finalStatus == ERROR_END_OF_STREAM) { + notifyListener_l(MEDIA_BUFFERING_UPDATE, 100); + } + if (mFlags & PREPARING) { + ALOGV("cache has reached EOS, prepare is done."); + finishAsyncPrepare_l(); + } + } else { + int64_t bitrate; + if (getBitrate_l(&bitrate)) { + size_t cachedSize = mCachedSource->cachedSize(); + int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate; + + int percentage = (100.0 * (double) cachedDurationUs) + / mDurationUs; + if (percentage > 100) { + percentage = 100; + } + + notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage); + } else { + // We don't know the bitrate of the stream, use absolute size + // limits to maintain the cache. + + if ((mFlags & PLAYING) && + !eos && + (cachedDataRemaining < kLowWaterMarkBytes)) { + ALOGI("cache is running low (< %d) , pausing.", + kLowWaterMarkBytes); + mFlags |= CACHE_UNDERRUN; + pause_l(); + ensureCacheIsFetching_l(); + notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START); + } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) { + if (mFlags & CACHE_UNDERRUN) { + ALOGI("cache has filled up (> %d), resuming.", + kHighWaterMarkBytes); + mFlags &= ~CACHE_UNDERRUN; + play_l(); + notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END); + } else if (mFlags & PREPARING) { + ALOGV("cache has filled up (> %d), prepare is done", + kHighWaterMarkBytes); + finishAsyncPrepare_l(); + } + } + } + } + } + + int64_t cachedDurationUs; + bool eos; + if (getCachedDuration_l(&cachedDurationUs, &eos)) { + ALOGV("cachedDurationUs = %.2f secs, eos=%d", + cachedDurationUs / 1E6, eos); + + if ((mFlags & PLAYING) && + !eos && + (cachedDurationUs < kLowWaterMarkUs)) { + ALOGI("cache is running low (%.2f secs) , pausing.", + cachedDurationUs / 1E6); + mFlags |= CACHE_UNDERRUN; + pause_l(); + ensureCacheIsFetching_l(); + notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START); + } else if (eos || cachedDurationUs > kHighWaterMarkUs) { + if (mFlags & CACHE_UNDERRUN) { + ALOGI("cache has filled up (%.2f secs), resuming.", + cachedDurationUs / 1E6); + mFlags &= ~CACHE_UNDERRUN; + play_l(); + notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END); + } else if (mFlags & PREPARING) { + ALOGV("cache has filled up (%.2f secs), prepare is done", + cachedDurationUs / 1E6); + finishAsyncPrepare_l(); + } + } + } + + postBufferingEvent_l(); +} + +void AAH_TXPlayer::onPumpAudio() { + while (true) { + Mutex::Autolock autoLock(mLock); + // If this flag is clear, its because someone has externally canceled + // this pump operation (probably because we a resetting/shutting down). + // Get out immediately, do not reschedule ourselves. + if (!mPumpAudioEventPending) { + return; + } + + // Start by checking if there is still work to be doing. If we have + // never queued a payload (so we don't know what the last queued PTS is) + // or we have never established a MediaTime->CommonTime transformation, + // then we have work to do (one time through this loop should establish + // both). Otherwise, we want to keep a fixed amt of presentation time + // worth of data buffered. If we cannot get common time (service is + // unavailable, or common time is undefined)) then we don't have a lot + // of good options here. For now, signal an error up to the app level + // and shut down the transmission pump. + int64_t commonTimeNow; + if (OK != mCCHelper.getCommonTime(&commonTimeNow)) { + // Failed to get common time; either the service is down or common + // time is not synced. Raise an error and shutdown the player. + ALOGE("*** Cannot pump audio, unable to fetch common time." + " Shutting down."); + notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, UNKNOWN_ERROR); + mPumpAudioEventPending = false; + break; + } + + if (mCurrentClockTransformValid && mLastQueuedMediaTimePTSValid) { + int64_t mediaTimeNow; + bool conversionResult = mCurrentClockTransform.doReverseTransform( + commonTimeNow, + &mediaTimeNow); + CHECK(conversionResult); + + if ((mediaTimeNow + + kAAHBufferTimeUs - + mLastQueuedMediaTimePTS) <= 0) { + break; + } + } + + MediaSource::ReadOptions options; + if (mIsSeeking) { + options.setSeekTo(mSeekTimeUs); + } + + MediaBuffer* mediaBuffer; + status_t err = mAudioSource->read(&mediaBuffer, &options); + if (err != NO_ERROR) { + if (err == ERROR_END_OF_STREAM) { + ALOGI("*** %s reached end of stream", __PRETTY_FUNCTION__); + notifyListener_l(MEDIA_BUFFERING_UPDATE, 100); + notifyListener_l(MEDIA_PLAYBACK_COMPLETE); + pause_l(false); + sendEOS_l(); + } else { + ALOGE("*** %s read failed err=%d", __PRETTY_FUNCTION__, err); + } + return; + } + + if (mIsSeeking) { + mIsSeeking = false; + notifyListener_l(MEDIA_SEEK_COMPLETE); + } + + uint8_t* data = (static_cast<uint8_t*>(mediaBuffer->data()) + + mediaBuffer->range_offset()); + ALOGV("*** %s got media buffer data=[%02hhx %02hhx %02hhx %02hhx]" + " offset=%d length=%d", __PRETTY_FUNCTION__, + data[0], data[1], data[2], data[3], + mediaBuffer->range_offset(), mediaBuffer->range_length()); + + int64_t mediaTimeUs; + CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &mediaTimeUs)); + ALOGV("*** timeUs=%lld", mediaTimeUs); + + if (!mCurrentClockTransformValid) { + if (OK == mCCHelper.getCommonTime(&commonTimeNow)) { + mCurrentClockTransform.a_zero = mediaTimeUs; + mCurrentClockTransform.b_zero = commonTimeNow + + kAAHStartupLeadTimeUs; + mCurrentClockTransform.a_to_b_numer = 1; + mCurrentClockTransform.a_to_b_denom = mPlayRateIsPaused ? 0 : 1; + mCurrentClockTransformValid = true; + } else { + // Failed to get common time; either the service is down or + // common time is not synced. Raise an error and shutdown the + // player. + ALOGE("*** Cannot begin transmission, unable to fetch common" + " time. Dropping sample with pts=%lld", mediaTimeUs); + notifyListener_l(MEDIA_ERROR, + MEDIA_ERROR_UNKNOWN, + UNKNOWN_ERROR); + mPumpAudioEventPending = false; + break; + } + } + + ALOGV("*** transmitting packet with pts=%lld", mediaTimeUs); + + sp<TRTPAudioPacket> packet = new TRTPAudioPacket(); + packet->setPTS(mediaTimeUs); + packet->setSubstreamID(1); + + packet->setCodecType(TRTPAudioPacket::kCodecMPEG1Audio); + packet->setVolume(mTRTPVolume); + // TODO : introduce a throttle for this so we can control the + // frequency with which transforms get sent. + packet->setClockTransform(mCurrentClockTransform); + packet->setAccessUnitData(data, mediaBuffer->range_length()); + packet->setRandomAccessPoint(true); + + queuePacketToSender_l(packet); + mediaBuffer->release(); + + mLastQueuedMediaTimePTSValid = true; + mLastQueuedMediaTimePTS = mediaTimeUs; + } + + { // Explicit scope for the autolock pattern. + Mutex::Autolock autoLock(mLock); + + // If someone externally has cleared this flag, its because we should be + // shutting down. Do not reschedule ourselves. + if (!mPumpAudioEventPending) { + return; + } + + // Looks like no one canceled us explicitly. Clear our flag and post a + // new event to ourselves. + mPumpAudioEventPending = false; + postPumpAudioEvent_l(10000); + } +} + +void AAH_TXPlayer::queuePacketToSender_l(const sp<TRTPPacket>& packet) { + if (mAAH_Sender == NULL) { + return; + } + + sp<AMessage> message = new AMessage(AAH_TXSender::kWhatSendPacket, + mAAH_Sender->handlerID()); + + { + Mutex::Autolock lock(mEndpointLock); + if (!mEndpointValid) { + return; + } + + message->setInt32(AAH_TXSender::kSendPacketIPAddr, mEndpoint.addr); + message->setInt32(AAH_TXSender::kSendPacketPort, mEndpoint.port); + } + + packet->setProgramID(mProgramID); + packet->setExpireTime(systemTime() + kAAHRetryKeepAroundTimeNs); + packet->pack(); + + message->setObject(AAH_TXSender::kSendPacketTRTPPacket, packet); + + message->post(); +} + +} // namespace android diff --git a/media/libaah_rtp/aah_tx_player.h b/media/libaah_rtp/aah_tx_player.h new file mode 100644 index 000000000000..64cf5dc11a03 --- /dev/null +++ b/media/libaah_rtp/aah_tx_player.h @@ -0,0 +1,179 @@ +/* + * 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 __AAH_TX_PLAYER_H__ +#define __AAH_TX_PLAYER_H__ + +#include <common_time/cc_helper.h> +#include <libstagefright/include/HTTPBase.h> +#include <libstagefright/include/NuCachedSource2.h> +#include <libstagefright/include/TimedEventQueue.h> +#include <media/MediaPlayerInterface.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <utils/LinearTransform.h> +#include <utils/String8.h> +#include <utils/threads.h> + +#include "aah_tx_sender.h" + +namespace android { + +class AAH_TXPlayer : public MediaPlayerHWInterface { + public: + AAH_TXPlayer(); + + virtual status_t initCheck(); + virtual status_t setDataSource(const char *url, + const KeyedVector<String8, String8>* + headers); + virtual status_t setDataSource(int fd, int64_t offset, int64_t length); + virtual status_t setVideoSurface(const sp<Surface>& surface); + virtual status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>& + surfaceTexture); + virtual status_t prepare(); + virtual status_t prepareAsync(); + virtual status_t start(); + virtual status_t stop(); + virtual status_t pause(); + virtual bool isPlaying(); + virtual status_t seekTo(int msec); + virtual status_t getCurrentPosition(int *msec); + virtual status_t getDuration(int *msec); + virtual status_t reset(); + virtual status_t setLooping(int loop); + virtual player_type playerType(); + virtual status_t setParameter(int key, const Parcel &request); + virtual status_t getParameter(int key, Parcel *reply); + virtual status_t invoke(const Parcel& request, Parcel *reply); + virtual status_t getMetadata(const media::Metadata::Filter& ids, + Parcel* records); + virtual status_t setVolume(float leftVolume, float rightVolume); + virtual status_t setAudioStreamType(audio_stream_type_t streamType); + + // invoke method IDs + enum { + // set the IP address and port of the A@H receiver + kInvokeSetAAHDstIPPort = 1, + + // set the destination IP address and port (and perhaps any additional + // parameters added in the future) packaged in one string + kInvokeSetAAHConfigBlob, + }; + + static const int64_t kAAHRetryKeepAroundTimeNs; + + protected: + virtual ~AAH_TXPlayer(); + + private: + friend struct AwesomeEvent; + + enum { + PLAYING = 1, + PREPARING = 8, + PREPARED = 16, + PREPARE_CANCELLED = 64, + CACHE_UNDERRUN = 128, + + // We are basically done preparing but are currently buffering + // sufficient data to begin playback and finish the preparation + // phase for good. + PREPARING_CONNECTED = 2048, + + INCOGNITO = 32768, + }; + + status_t setDataSource_l(const char *url, + const KeyedVector<String8, String8> *headers); + status_t setDataSource_l(const sp<MediaExtractor>& extractor); + status_t finishSetDataSource_l(); + status_t prepareAsync_l(); + void onPrepareAsyncEvent(); + void finishAsyncPrepare_l(); + void abortPrepare(status_t err); + status_t play_l(); + status_t pause_l(bool doClockUpdate = true); + status_t seekTo_l(int64_t timeUs); + void updateClockTransform_l(bool pause); + void sendEOS_l(); + void cancelPlayerEvents(bool keepBufferingGoing = false); + void reset_l(); + void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0); + bool getBitrate_l(int64_t* bitrate); + status_t getDuration_l(int* msec); + bool getCachedDuration_l(int64_t* durationUs, bool* eos); + void ensureCacheIsFetching_l(); + void postBufferingEvent_l(); + void postPumpAudioEvent_l(int64_t delayUs); + void onBufferingUpdate(); + void onPumpAudio(); + void queuePacketToSender_l(const sp<TRTPPacket>& packet); + + Mutex mLock; + + TimedEventQueue mQueue; + bool mQueueStarted; + + sp<TimedEventQueue::Event> mBufferingEvent; + bool mBufferingEventPending; + + uint32_t mFlags; + uint32_t mExtractorFlags; + + String8 mUri; + KeyedVector<String8, String8> mUriHeaders; + + sp<DataSource> mFileSource; + + sp<TimedEventQueue::Event> mAsyncPrepareEvent; + Condition mPreparedCondition; + status_t mPrepareResult; + + bool mIsSeeking; + int64_t mSeekTimeUs; + + sp<TimedEventQueue::Event> mPumpAudioEvent; + bool mPumpAudioEventPending; + + sp<HTTPBase> mConnectingDataSource; + sp<NuCachedSource2> mCachedSource; + + sp<MediaSource> mAudioSource; + int64_t mDurationUs; + int64_t mBitrate; + + sp<AAH_TXSender> mAAH_Sender; + LinearTransform mCurrentClockTransform; + bool mCurrentClockTransformValid; + int64_t mLastQueuedMediaTimePTS; + bool mLastQueuedMediaTimePTSValid; + bool mPlayRateIsPaused; + CCHelper mCCHelper; + + Mutex mEndpointLock; + AAH_TXSender::Endpoint mEndpoint; + bool mEndpointValid; + bool mEndpointRegistered; + uint16_t mProgramID; + uint8_t mTRTPVolume; + + DISALLOW_EVIL_CONSTRUCTORS(AAH_TXPlayer); +}; + +} // namespace android + +#endif // __AAH_TX_PLAYER_H__ diff --git a/media/libaah_rtp/aah_tx_sender.cpp b/media/libaah_rtp/aah_tx_sender.cpp new file mode 100644 index 000000000000..d991ea76afe2 --- /dev/null +++ b/media/libaah_rtp/aah_tx_sender.cpp @@ -0,0 +1,602 @@ +/* + * 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 "LibAAH_RTP" +#include <media/stagefright/foundation/ADebug.h> + +#include <netinet/in.h> +#include <poll.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> + +#include <media/stagefright/foundation/AMessage.h> +#include <utils/misc.h> + +#include "aah_tx_player.h" +#include "aah_tx_sender.h" + +namespace android { + +const char* AAH_TXSender::kSendPacketIPAddr = "ipaddr"; +const char* AAH_TXSender::kSendPacketPort = "port"; +const char* AAH_TXSender::kSendPacketTRTPPacket = "trtp"; + +const int AAH_TXSender::kRetryTrimIntervalUs = 100000; +const int AAH_TXSender::kHeartbeatIntervalUs = 1000000; +const int AAH_TXSender::kRetryBufferCapacity = 100; +const nsecs_t AAH_TXSender::kHeartbeatTimeout = 600ull * 1000000000ull; + +Mutex AAH_TXSender::sLock; +wp<AAH_TXSender> AAH_TXSender::sInstance; +uint32_t AAH_TXSender::sNextEpoch; +bool AAH_TXSender::sNextEpochValid = false; + +AAH_TXSender::AAH_TXSender() : mSocket(-1) { + mLastSentPacketTime = systemTime(); +} + +sp<AAH_TXSender> AAH_TXSender::GetInstance() { + Mutex::Autolock autoLock(sLock); + + sp<AAH_TXSender> sender = sInstance.promote(); + + if (sender == NULL) { + sender = new AAH_TXSender(); + if (sender == NULL) { + return NULL; + } + + sender->mLooper = new ALooper(); + if (sender->mLooper == NULL) { + return NULL; + } + + sender->mReflector = new AHandlerReflector<AAH_TXSender>(sender.get()); + if (sender->mReflector == NULL) { + return NULL; + } + + sender->mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (sender->mSocket == -1) { + ALOGW("%s unable to create socket", __PRETTY_FUNCTION__); + return NULL; + } + + struct sockaddr_in bind_addr; + memset(&bind_addr, 0, sizeof(bind_addr)); + bind_addr.sin_family = AF_INET; + if (bind(sender->mSocket, + reinterpret_cast<const sockaddr*>(&bind_addr), + sizeof(bind_addr)) < 0) { + ALOGW("%s unable to bind socket (errno %d)", + __PRETTY_FUNCTION__, errno); + return NULL; + } + + sender->mRetryReceiver = new RetryReceiver(sender.get()); + if (sender->mRetryReceiver == NULL) { + return NULL; + } + + sender->mLooper->setName("AAH_TXSender"); + sender->mLooper->registerHandler(sender->mReflector); + sender->mLooper->start(false, false, PRIORITY_AUDIO); + + if (sender->mRetryReceiver->run("AAH_TXSenderRetry", PRIORITY_AUDIO) + != OK) { + ALOGW("%s unable to start retry thread", __PRETTY_FUNCTION__); + return NULL; + } + + sInstance = sender; + } + + return sender; +} + +AAH_TXSender::~AAH_TXSender() { + mLooper->stop(); + mLooper->unregisterHandler(mReflector->id()); + + if (mRetryReceiver != NULL) { + mRetryReceiver->requestExit(); + mRetryReceiver->mWakeupEvent.setEvent(); + if (mRetryReceiver->requestExitAndWait() != OK) { + ALOGW("%s shutdown of retry receiver failed", __PRETTY_FUNCTION__); + } + mRetryReceiver->mSender = NULL; + mRetryReceiver.clear(); + } + + if (mSocket != -1) { + close(mSocket); + } +} + +// Return the next epoch number usable for a newly instantiated endpoint. +uint32_t AAH_TXSender::getNextEpoch() { + Mutex::Autolock autoLock(sLock); + + if (sNextEpochValid) { + sNextEpoch = (sNextEpoch + 1) & TRTPPacket::kTRTPEpochMask; + } else { + sNextEpoch = ns2ms(systemTime()) & TRTPPacket::kTRTPEpochMask; + sNextEpochValid = true; + } + + return sNextEpoch; +} + +// Notify the sender that a player has started sending to this endpoint. +// Returns a program ID for use by the calling player. +uint16_t AAH_TXSender::registerEndpoint(const Endpoint& endpoint) { + Mutex::Autolock lock(mEndpointLock); + + EndpointState* eps = mEndpointMap.valueFor(endpoint); + if (eps) { + eps->playerRefCount++; + } else { + eps = new EndpointState(getNextEpoch()); + mEndpointMap.add(endpoint, eps); + } + + // if this is the first registered endpoint, then send a message to start + // trimming retry buffers and a message to start sending heartbeats. + if (mEndpointMap.size() == 1) { + sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers, + handlerID()); + trimMessage->post(kRetryTrimIntervalUs); + + sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats, + handlerID()); + heartbeatMessage->post(kHeartbeatIntervalUs); + } + + eps->nextProgramID++; + return eps->nextProgramID; +} + +// Notify the sender that a player has ceased sending to this endpoint. +// An endpoint's state can not be deleted until all of the endpoint's +// registered players have called unregisterEndpoint. +void AAH_TXSender::unregisterEndpoint(const Endpoint& endpoint) { + Mutex::Autolock lock(mEndpointLock); + + EndpointState* eps = mEndpointMap.valueFor(endpoint); + if (eps) { + eps->playerRefCount--; + CHECK(eps->playerRefCount >= 0); + } +} + +void AAH_TXSender::onMessageReceived(const sp<AMessage>& msg) { + switch (msg->what()) { + case kWhatSendPacket: + onSendPacket(msg); + break; + + case kWhatTrimRetryBuffers: + trimRetryBuffers(); + break; + + case kWhatSendHeartbeats: + sendHeartbeats(); + break; + + default: + TRESPASS(); + break; + } +} + +void AAH_TXSender::onSendPacket(const sp<AMessage>& msg) { + sp<RefBase> obj; + CHECK(msg->findObject(kSendPacketTRTPPacket, &obj)); + sp<TRTPPacket> packet = static_cast<TRTPPacket*>(obj.get()); + + uint32_t ipAddr; + CHECK(msg->findInt32(kSendPacketIPAddr, + reinterpret_cast<int32_t*>(&ipAddr))); + + int32_t port32; + CHECK(msg->findInt32(kSendPacketPort, &port32)); + uint16_t port = port32; + + Mutex::Autolock lock(mEndpointLock); + doSendPacket_l(packet, Endpoint(ipAddr, port)); + mLastSentPacketTime = systemTime(); +} + +void AAH_TXSender::doSendPacket_l(const sp<TRTPPacket>& packet, + const Endpoint& endpoint) { + EndpointState* eps = mEndpointMap.valueFor(endpoint); + if (!eps) { + // the endpoint state has disappeared, so the player that sent this + // packet must be dead. + return; + } + + // assign the packet's sequence number + packet->setEpoch(eps->epoch); + packet->setSeqNumber(eps->trtpSeqNumber++); + + // add the packet to the retry buffer + RetryBuffer& retry = eps->retry; + retry.push_back(packet); + + // send the packet + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = endpoint.addr; + addr.sin_port = htons(endpoint.port); + + ssize_t result = sendto(mSocket, + packet->getPacket(), + packet->getPacketLen(), + 0, + (const struct sockaddr *) &addr, + sizeof(addr)); + if (result == -1) { + ALOGW("%s sendto failed", __PRETTY_FUNCTION__); + } +} + +void AAH_TXSender::trimRetryBuffers() { + Mutex::Autolock lock(mEndpointLock); + + nsecs_t localTimeNow = systemTime(); + + Vector<Endpoint> endpointsToRemove; + + for (size_t i = 0; i < mEndpointMap.size(); i++) { + EndpointState* eps = mEndpointMap.editValueAt(i); + RetryBuffer& retry = eps->retry; + + while (!retry.isEmpty()) { + if (retry[0]->getExpireTime() < localTimeNow) { + retry.pop_front(); + } else { + break; + } + } + + if (retry.isEmpty() && eps->playerRefCount == 0) { + endpointsToRemove.add(mEndpointMap.keyAt(i)); + } + } + + // remove the state for any endpoints that are no longer in use + for (size_t i = 0; i < endpointsToRemove.size(); i++) { + Endpoint& e = endpointsToRemove.editItemAt(i); + ALOGD("*** %s removing endpoint addr=%08x", __PRETTY_FUNCTION__, e.addr); + size_t index = mEndpointMap.indexOfKey(e); + delete mEndpointMap.valueAt(index); + mEndpointMap.removeItemsAt(index); + } + + // schedule the next trim + if (mEndpointMap.size()) { + sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers, + handlerID()); + trimMessage->post(kRetryTrimIntervalUs); + } +} + +void AAH_TXSender::sendHeartbeats() { + Mutex::Autolock lock(mEndpointLock); + + if (shouldSendHeartbeats_l()) { + for (size_t i = 0; i < mEndpointMap.size(); i++) { + EndpointState* eps = mEndpointMap.editValueAt(i); + const Endpoint& ep = mEndpointMap.keyAt(i); + + sp<TRTPControlPacket> packet = new TRTPControlPacket(); + packet->setCommandID(TRTPControlPacket::kCommandNop); + + packet->setExpireTime(systemTime() + + AAH_TXPlayer::kAAHRetryKeepAroundTimeNs); + packet->pack(); + + doSendPacket_l(packet, ep); + } + } + + // schedule the next heartbeat + if (mEndpointMap.size()) { + sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats, + handlerID()); + heartbeatMessage->post(kHeartbeatIntervalUs); + } +} + +bool AAH_TXSender::shouldSendHeartbeats_l() { + // assert(holding endpoint lock) + return (systemTime() < (mLastSentPacketTime + kHeartbeatTimeout)); +} + +// Receiver + +// initial 4-byte ID of a retry request packet +const uint32_t AAH_TXSender::RetryReceiver::kRetryRequestID = 'Treq'; + +// initial 4-byte ID of a retry NAK packet +const uint32_t AAH_TXSender::RetryReceiver::kRetryNakID = 'Tnak'; + +// initial 4-byte ID of a fast start request packet +const uint32_t AAH_TXSender::RetryReceiver::kFastStartRequestID = 'Tfst'; + +AAH_TXSender::RetryReceiver::RetryReceiver(AAH_TXSender* sender) + : Thread(false), + mSender(sender) {} + + AAH_TXSender::RetryReceiver::~RetryReceiver() { + mWakeupEvent.clearPendingEvents(); + } + +// Returns true if val is within the interval bounded inclusively by +// start and end. Also handles the case where there is a rollover of the +// range between start and end. +template <typename T> +static inline bool withinIntervalWithRollover(T val, T start, T end) { + return ((start <= end && val >= start && val <= end) || + (start > end && (val >= start || val <= end))); +} + +bool AAH_TXSender::RetryReceiver::threadLoop() { + struct pollfd pollFds[2]; + pollFds[0].fd = mSender->mSocket; + pollFds[0].events = POLLIN; + pollFds[0].revents = 0; + pollFds[1].fd = mWakeupEvent.getWakeupHandle(); + pollFds[1].events = POLLIN; + pollFds[1].revents = 0; + + int pollResult = poll(pollFds, NELEM(pollFds), -1); + if (pollResult == -1) { + ALOGE("%s poll failed", __PRETTY_FUNCTION__); + return false; + } + + if (exitPending()) { + ALOGI("*** %s exiting", __PRETTY_FUNCTION__); + return false; + } + + if (pollFds[0].revents) { + handleRetryRequest(); + } + + return true; +} + +void AAH_TXSender::RetryReceiver::handleRetryRequest() { + ALOGV("*** RX %s start", __PRETTY_FUNCTION__); + + RetryPacket request; + struct sockaddr requestSrcAddr; + socklen_t requestSrcAddrLen = sizeof(requestSrcAddr); + + ssize_t result = recvfrom(mSender->mSocket, &request, sizeof(request), 0, + &requestSrcAddr, &requestSrcAddrLen); + if (result == -1) { + ALOGE("%s recvfrom failed, errno=%d", __PRETTY_FUNCTION__, errno); + return; + } + + if (static_cast<size_t>(result) < sizeof(RetryPacket)) { + ALOGW("%s short packet received", __PRETTY_FUNCTION__); + return; + } + + uint32_t host_request_id = ntohl(request.id); + if ((host_request_id != kRetryRequestID) && + (host_request_id != kFastStartRequestID)) { + ALOGW("%s received retry request with bogus ID (%08x)", + __PRETTY_FUNCTION__, host_request_id); + return; + } + + Endpoint endpoint(request.endpointIP, ntohs(request.endpointPort)); + + Mutex::Autolock lock(mSender->mEndpointLock); + + EndpointState* eps = mSender->mEndpointMap.valueFor(endpoint); + + if (eps == NULL || eps->retry.isEmpty()) { + // we have no retry buffer or an empty retry buffer for this endpoint, + // so NAK the entire request + RetryPacket nak = request; + nak.id = htonl(kRetryNakID); + result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, + &requestSrcAddr, requestSrcAddrLen); + if (result == -1) { + ALOGW("%s sendto failed", __PRETTY_FUNCTION__); + } + return; + } + + RetryBuffer& retry = eps->retry; + + uint16_t startSeq = ntohs(request.seqStart); + uint16_t endSeq = ntohs(request.seqEnd); + + uint16_t retryFirstSeq = retry[0]->getSeqNumber(); + uint16_t retryLastSeq = retry[retry.size() - 1]->getSeqNumber(); + + // If this is a fast start, then force the start of the retry to match the + // start of the retransmit ring buffer (unless the end of the retransmit + // ring buffer is already past the point of fast start) + if ((host_request_id == kFastStartRequestID) && + !((startSeq - retryFirstSeq) & 0x8000)) { + startSeq = retryFirstSeq; + } + + int startIndex; + if (withinIntervalWithRollover(startSeq, retryFirstSeq, retryLastSeq)) { + startIndex = static_cast<uint16_t>(startSeq - retryFirstSeq); + } else { + startIndex = -1; + } + + int endIndex; + if (withinIntervalWithRollover(endSeq, retryFirstSeq, retryLastSeq)) { + endIndex = static_cast<uint16_t>(endSeq - retryFirstSeq); + } else { + endIndex = -1; + } + + if (startIndex == -1 && endIndex == -1) { + // no part of the request range is found in the retry buffer + RetryPacket nak = request; + nak.id = htonl(kRetryNakID); + result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, + &requestSrcAddr, requestSrcAddrLen); + if (result == -1) { + ALOGW("%s sendto failed", __PRETTY_FUNCTION__); + } + return; + } + + if (startIndex == -1) { + // NAK a subrange at the front of the request range + RetryPacket nak = request; + nak.id = htonl(kRetryNakID); + nak.seqEnd = htons(retryFirstSeq - 1); + result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, + &requestSrcAddr, requestSrcAddrLen); + if (result == -1) { + ALOGW("%s sendto failed", __PRETTY_FUNCTION__); + } + + startIndex = 0; + } else if (endIndex == -1) { + // NAK a subrange at the back of the request range + RetryPacket nak = request; + nak.id = htonl(kRetryNakID); + nak.seqStart = htons(retryLastSeq + 1); + result = sendto(mSender->mSocket, &nak, sizeof(nak), 0, + &requestSrcAddr, requestSrcAddrLen); + if (result == -1) { + ALOGW("%s sendto failed", __PRETTY_FUNCTION__); + } + + endIndex = retry.size() - 1; + } + + // send the retry packets + for (int i = startIndex; i <= endIndex; i++) { + const sp<TRTPPacket>& replyPacket = retry[i]; + + result = sendto(mSender->mSocket, + replyPacket->getPacket(), + replyPacket->getPacketLen(), + 0, + &requestSrcAddr, + requestSrcAddrLen); + + if (result == -1) { + ALOGW("%s sendto failed", __PRETTY_FUNCTION__); + } + } +} + +// Endpoint + +AAH_TXSender::Endpoint::Endpoint() + : addr(0) + , port(0) { } + +AAH_TXSender::Endpoint::Endpoint(uint32_t a, uint16_t p) + : addr(a) + , port(p) {} + +bool AAH_TXSender::Endpoint::operator<(const Endpoint& other) const { + return ((addr < other.addr) || + (addr == other.addr && port < other.port)); +} + +// EndpointState + +AAH_TXSender::EndpointState::EndpointState(uint32_t _epoch) + : retry(kRetryBufferCapacity) + , playerRefCount(1) + , trtpSeqNumber(0) + , nextProgramID(0) + , epoch(_epoch) { } + +// CircularBuffer + +template <typename T> +CircularBuffer<T>::CircularBuffer(size_t capacity) + : mCapacity(capacity) + , mHead(0) + , mTail(0) + , mFillCount(0) { + mBuffer = new T[capacity]; +} + +template <typename T> +CircularBuffer<T>::~CircularBuffer() { + delete [] mBuffer; +} + +template <typename T> +void CircularBuffer<T>::push_back(const T& item) { + if (this->isFull()) { + this->pop_front(); + } + mBuffer[mHead] = item; + mHead = (mHead + 1) % mCapacity; + mFillCount++; +} + +template <typename T> +void CircularBuffer<T>::pop_front() { + CHECK(!isEmpty()); + mBuffer[mTail] = T(); + mTail = (mTail + 1) % mCapacity; + mFillCount--; +} + +template <typename T> +size_t CircularBuffer<T>::size() const { + return mFillCount; +} + +template <typename T> +bool CircularBuffer<T>::isFull() const { + return (mFillCount == mCapacity); +} + +template <typename T> +bool CircularBuffer<T>::isEmpty() const { + return (mFillCount == 0); +} + +template <typename T> +const T& CircularBuffer<T>::itemAt(size_t index) const { + CHECK(index < mFillCount); + return mBuffer[(mTail + index) % mCapacity]; +} + +template <typename T> +const T& CircularBuffer<T>::operator[](size_t index) const { + return itemAt(index); +} + +} // namespace android diff --git a/media/libaah_rtp/aah_tx_sender.h b/media/libaah_rtp/aah_tx_sender.h new file mode 100644 index 000000000000..74206c49b28b --- /dev/null +++ b/media/libaah_rtp/aah_tx_sender.h @@ -0,0 +1,162 @@ +/* + * 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 __AAH_TX_SENDER_H__ +#define __AAH_TX_SENDER_H__ + +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/foundation/AHandlerReflector.h> +#include <utils/RefBase.h> +#include <utils/threads.h> + +#include "aah_tx_packet.h" +#include "pipe_event.h" + +namespace android { + +template <typename T> class CircularBuffer { + public: + CircularBuffer(size_t capacity); + ~CircularBuffer(); + void push_back(const T& item);; + void pop_front(); + size_t size() const; + bool isFull() const; + bool isEmpty() const; + const T& itemAt(size_t index) const; + const T& operator[](size_t index) const; + + private: + T* mBuffer; + size_t mCapacity; + size_t mHead; + size_t mTail; + size_t mFillCount; + + DISALLOW_EVIL_CONSTRUCTORS(CircularBuffer); +}; + +class AAH_TXSender : public virtual RefBase { + public: + ~AAH_TXSender(); + + static sp<AAH_TXSender> GetInstance(); + + ALooper::handler_id handlerID() { return mReflector->id(); } + + // an IP address and port + struct Endpoint { + Endpoint(); + Endpoint(uint32_t a, uint16_t p); + bool operator<(const Endpoint& other) const; + + uint32_t addr; + uint16_t port; + }; + + uint16_t registerEndpoint(const Endpoint& endpoint); + void unregisterEndpoint(const Endpoint& endpoint); + + enum { + kWhatSendPacket, + kWhatTrimRetryBuffers, + kWhatSendHeartbeats, + }; + + // fields for SendPacket messages + static const char* kSendPacketIPAddr; + static const char* kSendPacketPort; + static const char* kSendPacketTRTPPacket; + + private: + AAH_TXSender(); + + static Mutex sLock; + static wp<AAH_TXSender> sInstance; + static uint32_t sNextEpoch; + static bool sNextEpochValid; + + static uint32_t getNextEpoch(); + + typedef CircularBuffer<sp<TRTPPacket> > RetryBuffer; + + // state maintained on a per-endpoint basis + struct EndpointState { + EndpointState(uint32_t epoch); + RetryBuffer retry; + int playerRefCount; + uint16_t trtpSeqNumber; + uint16_t nextProgramID; + uint32_t epoch; + }; + + friend class AHandlerReflector<AAH_TXSender>; + void onMessageReceived(const sp<AMessage>& msg); + void onSendPacket(const sp<AMessage>& msg); + void doSendPacket_l(const sp<TRTPPacket>& packet, + const Endpoint& endpoint); + void trimRetryBuffers(); + void sendHeartbeats(); + bool shouldSendHeartbeats_l(); + + sp<ALooper> mLooper; + sp<AHandlerReflector<AAH_TXSender> > mReflector; + + int mSocket; + nsecs_t mLastSentPacketTime; + + DefaultKeyedVector<Endpoint, EndpointState*> mEndpointMap; + Mutex mEndpointLock; + + static const int kRetryTrimIntervalUs; + static const int kHeartbeatIntervalUs; + static const int kRetryBufferCapacity; + static const nsecs_t kHeartbeatTimeout; + + class RetryReceiver : public Thread { + private: + friend class AAH_TXSender; + + RetryReceiver(AAH_TXSender* sender); + virtual ~RetryReceiver(); + virtual bool threadLoop(); + void handleRetryRequest(); + + static const int kMaxReceiverPacketLen; + static const uint32_t kRetryRequestID; + static const uint32_t kFastStartRequestID; + static const uint32_t kRetryNakID; + + AAH_TXSender* mSender; + PipeEvent mWakeupEvent; + }; + + sp<RetryReceiver> mRetryReceiver; + + DISALLOW_EVIL_CONSTRUCTORS(AAH_TXSender); +}; + +struct RetryPacket { + uint32_t id; + uint32_t endpointIP; + uint16_t endpointPort; + uint16_t seqStart; + uint16_t seqEnd; +} __attribute__((packed)); + +} // namespace android + +#endif // __AAH_TX_SENDER_H__ diff --git a/media/libaah_rtp/pipe_event.cpp b/media/libaah_rtp/pipe_event.cpp new file mode 100644 index 000000000000..b8e696080ca9 --- /dev/null +++ b/media/libaah_rtp/pipe_event.cpp @@ -0,0 +1,86 @@ +/* + * 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 "LibAAH_RTP" +#include <utils/Log.h> + +#include <errno.h> +#include <fcntl.h> +#include <poll.h> +#include <unistd.h> + +#include "pipe_event.h" + +namespace android { + +PipeEvent::PipeEvent() { + pipe_[0] = -1; + pipe_[1] = -1; + + // Create the pipe. + if (pipe(pipe_) >= 0) { + // Set non-blocking mode on the read side of the pipe so we can + // easily drain it whenever we wakeup. + fcntl(pipe_[0], F_SETFL, O_NONBLOCK); + } else { + ALOGE("Failed to create pipe event %d %d %d", + pipe_[0], pipe_[1], errno); + pipe_[0] = -1; + pipe_[1] = -1; + } +} + +PipeEvent::~PipeEvent() { + if (pipe_[0] >= 0) { + close(pipe_[0]); + } + + if (pipe_[1] >= 0) { + close(pipe_[1]); + } +} + +void PipeEvent::clearPendingEvents() { + char drain_buffer[16]; + while (read(pipe_[0], drain_buffer, sizeof(drain_buffer)) > 0) { + // No body. + } +} + +bool PipeEvent::wait(int timeout) { + struct pollfd wait_fd; + + wait_fd.fd = getWakeupHandle(); + wait_fd.events = POLLIN; + wait_fd.revents = 0; + + int res = poll(&wait_fd, 1, timeout); + + if (res < 0) { + ALOGE("Wait error in PipeEvent; sleeping to prevent overload!"); + usleep(1000); + } + + return (res > 0); +} + +void PipeEvent::setEvent() { + char foo = 'q'; + write(pipe_[1], &foo, 1); +} + +} // namespace android + diff --git a/media/libaah_rtp/pipe_event.h b/media/libaah_rtp/pipe_event.h new file mode 100644 index 000000000000..e53b0fdb74c0 --- /dev/null +++ b/media/libaah_rtp/pipe_event.h @@ -0,0 +1,51 @@ +/* + * 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 __PIPE_EVENT_H__ +#define __PIPE_EVENT_H__ + +#include <media/stagefright/foundation/ABase.h> + +namespace android { + +class PipeEvent { + public: + PipeEvent(); + ~PipeEvent(); + + bool initCheck() const { + return ((pipe_[0] >= 0) && (pipe_[1] >= 0)); + } + + int getWakeupHandle() const { return pipe_[0]; } + + // block until the event fires; returns true if the event fired and false if + // the wait timed out. Timeout is expressed in milliseconds; negative + // values mean wait forever. + bool wait(int timeout = -1); + + void clearPendingEvents(); + void setEvent(); + + private: + int pipe_[2]; + + DISALLOW_EVIL_CONSTRUCTORS(PipeEvent); +}; + +} // namespace android + +#endif // __PIPE_EVENT_H__ diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp index f9f997fe6a32..6808aa22b6c9 100644 --- a/media/libmedia/AudioEffect.cpp +++ b/media/libmedia/AudioEffect.cpp @@ -202,7 +202,7 @@ bool AudioEffect::getEnabled() const status_t AudioEffect::setEnabled(bool enabled) { if (mStatus != NO_ERROR) { - return INVALID_OPERATION; + return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } status_t status = NO_ERROR; @@ -231,7 +231,7 @@ status_t AudioEffect::command(uint32_t cmdCode, { if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) { ALOGV("command() bad status %d", mStatus); - return INVALID_OPERATION; + return mStatus; } if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) { @@ -263,7 +263,7 @@ status_t AudioEffect::command(uint32_t cmdCode, status_t AudioEffect::setParameter(effect_param_t *param) { if (mStatus != NO_ERROR) { - return INVALID_OPERATION; + return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } if (param == NULL || param->psize == 0 || param->vsize == 0) { @@ -281,7 +281,7 @@ status_t AudioEffect::setParameter(effect_param_t *param) status_t AudioEffect::setParameterDeferred(effect_param_t *param) { if (mStatus != NO_ERROR) { - return INVALID_OPERATION; + return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } if (param == NULL || param->psize == 0 || param->vsize == 0) { @@ -307,7 +307,7 @@ status_t AudioEffect::setParameterDeferred(effect_param_t *param) status_t AudioEffect::setParameterCommit() { if (mStatus != NO_ERROR) { - return INVALID_OPERATION; + return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus; } Mutex::Autolock _l(mCblk->lock); @@ -321,7 +321,7 @@ status_t AudioEffect::setParameterCommit() status_t AudioEffect::getParameter(effect_param_t *param) { if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) { - return INVALID_OPERATION; + return mStatus; } if (param == NULL || param->psize == 0 || param->vsize == 0) { @@ -341,7 +341,7 @@ status_t AudioEffect::getParameter(effect_param_t *param) void AudioEffect::binderDied() { ALOGW("IEffect died"); - mStatus = NO_INIT; + mStatus = DEAD_OBJECT; if (mCbf != NULL) { status_t status = DEAD_OBJECT; mCbf(EVENT_ERROR, mUserData, &status); diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp index aead9a1654be..74c97ed932cf 100644 --- a/media/libmedia/AudioTrack.cpp +++ b/media/libmedia/AudioTrack.cpp @@ -80,7 +80,9 @@ status_t AudioTrack::getMinFrameCount( AudioTrack::AudioTrack() : mStatus(NO_INIT), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) + mIsTimed(false), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { } @@ -96,7 +98,9 @@ AudioTrack::AudioTrack( int notificationFrames, int sessionId) : mStatus(NO_INIT), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) + mIsTimed(false), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { mStatus = set(streamType, sampleRate, format, channelMask, frameCount, flags, cbf, user, notificationFrames, @@ -134,7 +138,9 @@ AudioTrack::AudioTrack( int notificationFrames, int sessionId) : mStatus(NO_INIT), - mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) + mIsTimed(false), + mPreviousPriority(ANDROID_PRIORITY_NORMAL), + mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT) { mStatus = set(streamType, sampleRate, format, channelMask, 0, flags, cbf, user, notificationFrames, @@ -540,6 +546,10 @@ status_t AudioTrack::setSampleRate(int rate) { int afSamplingRate; + if (mIsTimed) { + return INVALID_OPERATION; + } + if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) { return NO_INIT; } @@ -553,6 +563,10 @@ status_t AudioTrack::setSampleRate(int rate) uint32_t AudioTrack::getSampleRate() const { + if (mIsTimed) { + return INVALID_OPERATION; + } + AutoMutex lock(mLock); return mCblk->sampleRate; } @@ -578,6 +592,10 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou return NO_ERROR; } + if (mIsTimed) { + return INVALID_OPERATION; + } + if (loopStart >= loopEnd || loopEnd - loopStart > cblk->frameCount || cblk->server > loopStart) { @@ -641,6 +659,8 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const status_t AudioTrack::setPosition(uint32_t position) { + if (mIsTimed) return INVALID_OPERATION; + AutoMutex lock(mLock); if (!stopped_l()) return INVALID_OPERATION; @@ -791,6 +811,7 @@ status_t AudioTrack::createTrack_l( ((uint16_t)flags) << 16, sharedBuffer, output, + mIsTimed, &mSessionId, &status); @@ -957,6 +978,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) { if (mSharedBuffer != 0) return INVALID_OPERATION; + if (mIsTimed) return INVALID_OPERATION; if (ssize_t(userSize) < 0) { // Sanity-check: user is most-likely passing an error code, and it would @@ -1013,6 +1035,59 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize) // ------------------------------------------------------------------------- +TimedAudioTrack::TimedAudioTrack() { + mIsTimed = true; +} + +status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer) +{ + status_t result = UNKNOWN_ERROR; + + // If the track is not invalid already, try to allocate a buffer. alloc + // fails indicating that the server is dead, flag the track as invalid so + // we can attempt to restore in in just a bit. + if (!(mCblk->flags & CBLK_INVALID_MSK)) { + result = mAudioTrack->allocateTimedBuffer(size, buffer); + if (result == DEAD_OBJECT) { + android_atomic_or(CBLK_INVALID_ON, &mCblk->flags); + } + } + + // If the track is invalid at this point, attempt to restore it. and try the + // allocation one more time. + if (mCblk->flags & CBLK_INVALID_MSK) { + mCblk->lock.lock(); + result = restoreTrack_l(mCblk, false); + mCblk->lock.unlock(); + + if (result == OK) + result = mAudioTrack->allocateTimedBuffer(size, buffer); + } + + return result; +} + +status_t TimedAudioTrack::queueTimedBuffer(const sp<IMemory>& buffer, + int64_t pts) +{ + // restart track if it was disabled by audioflinger due to previous underrun + if (mActive && (mCblk->flags & CBLK_DISABLED_MSK)) { + android_atomic_and(~CBLK_DISABLED_ON, &mCblk->flags); + ALOGW("queueTimedBuffer() track %p disabled, restarting", this); + mAudioTrack->start(0); + } + + return mAudioTrack->queueTimedBuffer(buffer, pts); +} + +status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform, + TargetTimeline target) +{ + return mAudioTrack->setMediaTimeTransform(xform, target); +} + +// ------------------------------------------------------------------------- + bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread) { Buffer audioBuffer; diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp index 4507e5db7683..ebadbfa5499c 100644 --- a/media/libmedia/IAudioFlinger.cpp +++ b/media/libmedia/IAudioFlinger.cpp @@ -90,6 +90,7 @@ public: uint32_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, + bool isTimed, int *sessionId, status_t *status) { @@ -105,6 +106,7 @@ public: data.writeInt32(flags); data.writeStrongBinder(sharedBuffer->asBinder()); data.writeInt32((int32_t) output); + data.writeInt32(isTimed); int lSessionId = 0; if (sessionId != NULL) { lSessionId = *sessionId; @@ -689,11 +691,12 @@ status_t BnAudioFlinger::onTransact( uint32_t flags = data.readInt32(); sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder()); audio_io_handle_t output = (audio_io_handle_t) data.readInt32(); + bool isTimed = data.readInt32(); int sessionId = data.readInt32(); status_t status; sp<IAudioTrack> track = createTrack(pid, (audio_stream_type_t) streamType, sampleRate, format, - channelCount, bufferCount, flags, buffer, output, &sessionId, &status); + channelCount, bufferCount, flags, buffer, output, isTimed, &sessionId, &status); reply->writeInt32(sessionId); reply->writeInt32(status); reply->writeStrongBinder(track->asBinder()); diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp index a7958debe1ae..28ebbbfc73be 100644 --- a/media/libmedia/IAudioTrack.cpp +++ b/media/libmedia/IAudioTrack.cpp @@ -35,7 +35,10 @@ enum { FLUSH, MUTE, PAUSE, - ATTACH_AUX_EFFECT + ATTACH_AUX_EFFECT, + ALLOCATE_TIMED_BUFFER, + QUEUE_TIMED_BUFFER, + SET_MEDIA_TIME_TRANSFORM, }; class BpAudioTrack : public BpInterface<IAudioTrack> @@ -114,6 +117,52 @@ public: } return status; } + + virtual status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer) { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeInt32(size); + status_t status = remote()->transact(ALLOCATE_TIMED_BUFFER, + data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + if (status == NO_ERROR) { + *buffer = interface_cast<IMemory>(reply.readStrongBinder()); + } + } + return status; + } + + virtual status_t queueTimedBuffer(const sp<IMemory>& buffer, + int64_t pts) { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeStrongBinder(buffer->asBinder()); + data.writeInt64(pts); + status_t status = remote()->transact(QUEUE_TIMED_BUFFER, + data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + } + return status; + } + + virtual status_t setMediaTimeTransform(const LinearTransform& xform, + int target) { + Parcel data, reply; + data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor()); + data.writeInt64(xform.a_zero); + data.writeInt64(xform.b_zero); + data.writeInt32(xform.a_to_b_numer); + data.writeInt32(xform.a_to_b_denom); + data.writeInt32(target); + status_t status = remote()->transact(SET_MEDIA_TIME_TRANSFORM, + data, &reply); + if (status == NO_ERROR) { + status = reply.readInt32(); + } + return status; + } }; IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack"); @@ -159,10 +208,38 @@ status_t BnAudioTrack::onTransact( reply->writeInt32(attachAuxEffect(data.readInt32())); return NO_ERROR; } break; + case ALLOCATE_TIMED_BUFFER: { + CHECK_INTERFACE(IAudioTrack, data, reply); + sp<IMemory> buffer; + status_t status = allocateTimedBuffer(data.readInt32(), &buffer); + reply->writeInt32(status); + if (status == NO_ERROR) { + reply->writeStrongBinder(buffer->asBinder()); + } + return NO_ERROR; + } break; + case QUEUE_TIMED_BUFFER: { + CHECK_INTERFACE(IAudioTrack, data, reply); + sp<IMemory> buffer = interface_cast<IMemory>( + data.readStrongBinder()); + uint64_t pts = data.readInt64(); + reply->writeInt32(queueTimedBuffer(buffer, pts)); + return NO_ERROR; + } break; + case SET_MEDIA_TIME_TRANSFORM: { + CHECK_INTERFACE(IAudioTrack, data, reply); + LinearTransform xform; + xform.a_zero = data.readInt64(); + xform.b_zero = data.readInt64(); + xform.a_to_b_numer = data.readInt32(); + xform.a_to_b_denom = data.readInt32(); + int target = data.readInt32(); + reply->writeInt32(setMediaTimeTransform(xform, target)); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } } }; // namespace android - diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp index d469e280f647..5d40cc82af48 100644 --- a/media/libmedia/IEffect.cpp +++ b/media/libmedia/IEffect.cpp @@ -83,8 +83,15 @@ public: size = *pReplySize; } data.writeInt32(size); - remote()->transact(COMMAND, data, &reply); - status_t status = reply.readInt32(); + + status_t status = remote()->transact(COMMAND, data, &reply); + if (status != NO_ERROR) { + if (pReplySize != NULL) + *pReplySize = 0; + return status; + } + + status = reply.readInt32(); size = reply.readInt32(); if (size != 0 && pReplyData != NULL && pReplySize != NULL) { reply.read(pReplyData, size); diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp index 13b64e9f2917..70f8c0c28e8e 100644 --- a/media/libmedia/Visualizer.cpp +++ b/media/libmedia/Visualizer.cpp @@ -168,7 +168,7 @@ status_t Visualizer::getWaveForm(uint8_t *waveform) uint32_t replySize = mCaptureSize; status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform); ALOGV("getWaveForm() command returned %d", status); - if (replySize == 0) { + if ((status == NO_ERROR) && (replySize == 0)) { status = NOT_ENOUGH_DATA; } } else { diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk index a3e2517b37d2..e521648fde9f 100644 --- a/media/libmediaplayerservice/Android.mk +++ b/media/libmediaplayerservice/Android.mk @@ -29,7 +29,8 @@ LOCAL_SHARED_LIBRARIES := \ libstagefright_omx \ libstagefright_foundation \ libgui \ - libdl + libdl \ + libaah_rtp LOCAL_STATIC_LIBRARIES := \ libstagefright_nuplayer \ diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 4df7f3d654c4..764eddc840a8 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -70,6 +70,11 @@ #include <OMX.h> +namespace android { +sp<MediaPlayerBase> createAAH_TXPlayer(); +sp<MediaPlayerBase> createAAH_RXPlayer(); +} + namespace { using android::media::Metadata; using android::status_t; @@ -593,6 +598,14 @@ player_type getPlayerType(const char* url) return NU_PLAYER; } + if (!strncasecmp("aahRX://", url, 8)) { + return AAH_RX_PLAYER; + } + + if (!strncasecmp("aahTX://", url, 8)) { + return AAH_TX_PLAYER; + } + // use MidiFile for MIDI extensions int lenURL = strlen(url); for (int i = 0; i < NELEM(FILE_EXTS); ++i) { @@ -629,6 +642,14 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie, ALOGV("Create Test Player stub"); p = new TestPlayerStub(); break; + case AAH_RX_PLAYER: + ALOGV(" create A@H RX Player"); + p = createAAH_RXPlayer(); + break; + case AAH_TX_PLAYER: + ALOGV(" create A@H TX Player"); + p = createAAH_TXPlayer(); + break; default: ALOGE("Unknown player type: %d", playerType); return NULL; @@ -1031,9 +1052,21 @@ status_t MediaPlayerService::Client::setLooping(int loop) status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolume) { ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume); - // TODO: for hardware output, call player instead - Mutex::Autolock l(mLock); - if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume); + + // for hardware output, call player instead + sp<MediaPlayerBase> p = getPlayer(); + { + Mutex::Autolock l(mLock); + if (p != 0 && p->hardwareOutput()) { + MediaPlayerHWInterface* hwp = + reinterpret_cast<MediaPlayerHWInterface*>(p.get()); + return hwp->setVolume(leftVolume, rightVolume); + } else { + if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume); + return NO_ERROR; + } + } + return NO_ERROR; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index fe519b095f30..c5f4f86d38de 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1291,6 +1291,12 @@ status_t StagefrightRecorder::setupCameraSource( videoSize.width = mVideoWidth; videoSize.height = mVideoHeight; if (mCaptureTimeLapse) { + if (mTimeBetweenTimeLapseFrameCaptureUs < 0) { + ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld", + mTimeBetweenTimeLapseFrameCaptureUs); + return BAD_VALUE; + } + mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera( mCamera, mCameraProxy, mCameraId, videoSize, mFrameRate, mPreviewSurface, diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index b731d0f3cd58..dec1c08c16fb 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -387,10 +387,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { audio ? "audio" : "video"); mRenderer->queueEOS(audio, UNKNOWN_ERROR); - } else { - CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer); - + } else if (what == ACodec::kWhatDrainThisBuffer) { renderBuffer(audio, codecRequest); + } else { + ALOGV("Unhandled codec notification %d.", what); } break; @@ -768,7 +768,7 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mediaTimeUs / 1E6); #endif - reply->setObject("buffer", accessUnit); + reply->setBuffer("buffer", accessUnit); reply->post(); return OK; @@ -793,10 +793,8 @@ void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) { return; } - sp<RefBase> obj; - CHECK(msg->findObject("buffer", &obj)); - - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); int64_t &skipUntilMediaTimeUs = audio diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp index 56c2773fa56b..2a518292b19b 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp @@ -214,8 +214,6 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) { buffer->meta()->setInt32("csd", true); mCSD.push(buffer); - - msg->setObject("csd", buffer); } else if (meta->findData(kKeyESDS, &type, &data, &size)) { ESDS esds((const char *)data, size); CHECK_EQ(esds.InitCheck(), (status_t)OK); @@ -242,9 +240,8 @@ void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) { CHECK(msg->findMessage("reply", &reply)); #if 0 - sp<RefBase> obj; - CHECK(msg->findObject("buffer", &obj)); - sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> outBuffer; + CHECK(msg->findBuffer("buffer", &outBuffer)); #else sp<ABuffer> outBuffer; #endif @@ -253,7 +250,7 @@ void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) { outBuffer = mCSD.editItemAt(mCSDIndex++); outBuffer->meta()->setInt64("timeUs", 0); - reply->setObject("buffer", outBuffer); + reply->setBuffer("buffer", outBuffer); reply->post(); return; } diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp index 15259cb5c370..5738ecb60b52 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp @@ -60,7 +60,7 @@ void NuPlayer::Renderer::queueBuffer( const sp<AMessage> ¬ifyConsumed) { sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id()); msg->setInt32("audio", static_cast<int32_t>(audio)); - msg->setObject("buffer", buffer); + msg->setBuffer("buffer", buffer); msg->setMessage("notifyConsumed", notifyConsumed); msg->post(); } @@ -411,9 +411,8 @@ void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) { return; } - sp<RefBase> obj; - CHECK(msg->findObject("buffer", &obj)); - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); sp<AMessage> notifyConsumed; CHECK(msg->findMessage("notifyConsumed", ¬ifyConsumed)); diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp index 6eb0d077aac2..4c65b6564df5 100644 --- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp +++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp @@ -218,10 +218,8 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) { CHECK(msg->findSize("trackIndex", &trackIndex)); CHECK_LT(trackIndex, mTracks.size()); - sp<RefBase> obj; - CHECK(msg->findObject("accessUnit", &obj)); - - sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> accessUnit; + CHECK(msg->findBuffer("accessUnit", &accessUnit)); int32_t damaged; if (accessUnit->meta()->findInt32("damaged", &damaged) diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp index ca44ea364024..c91fbe6a2351 100644 --- a/media/libstagefright/ACodec.cpp +++ b/media/libstagefright/ACodec.cpp @@ -171,6 +171,9 @@ protected: private: void onSetup(const sp<AMessage> &msg); + void onAllocateComponent(const sp<AMessage> &msg); + void onConfigureComponent(const sp<AMessage> &msg); + void onStart(); DISALLOW_EVIL_CONSTRUCTORS(UninitializedState); }; @@ -265,6 +268,8 @@ protected: private: void changeStateIfWeOwnAllBuffers(); + bool mComponentNowIdle; + DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState); }; @@ -309,7 +314,8 @@ private: ACodec::ACodec() : mNode(NULL), - mSentFormat(false) { + mSentFormat(false), + mIsEncoder(false) { mUninitializedState = new UninitializedState(this); mLoadedToIdleState = new LoadedToIdleState(this); mIdleToExecutingState = new IdleToExecutingState(this); @@ -341,6 +347,22 @@ void ACodec::initiateSetup(const sp<AMessage> &msg) { msg->post(); } +void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) { + msg->setWhat(kWhatAllocateComponent); + msg->setTarget(id()); + msg->post(); +} + +void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) { + msg->setWhat(kWhatConfigureComponent); + msg->setTarget(id()); + msg->post(); +} + +void ACodec::initiateStart() { + (new AMessage(kWhatStart, id()))->post(); +} + void ACodec::signalFlush() { ALOGV("[%s] signalFlush", mComponentName.c_str()); (new AMessage(kWhatFlush, id()))->post(); @@ -360,62 +382,75 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) { CHECK(mDealer[portIndex] == NULL); CHECK(mBuffers[portIndex].isEmpty()); + status_t err; if (mNativeWindow != NULL && portIndex == kPortIndexOutput) { - return allocateOutputBuffersFromNativeWindow(); - } + err = allocateOutputBuffersFromNativeWindow(); + } else { + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = portIndex; - OMX_PARAM_PORTDEFINITIONTYPE def; - InitOMXParams(&def); - def.nPortIndex = portIndex; + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); - status_t err = mOMX->getParameter( - mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + if (err == OK) { + ALOGV("[%s] Allocating %lu buffers of size %lu on %s port", + mComponentName.c_str(), + def.nBufferCountActual, def.nBufferSize, + portIndex == kPortIndexInput ? "input" : "output"); - if (err != OK) { - return err; - } + size_t totalSize = def.nBufferCountActual * def.nBufferSize; + mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec"); - ALOGV("[%s] Allocating %lu buffers of size %lu on %s port", - mComponentName.c_str(), - def.nBufferCountActual, def.nBufferSize, - portIndex == kPortIndexInput ? "input" : "output"); + for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) { + sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize); + CHECK(mem.get() != NULL); - size_t totalSize = def.nBufferCountActual * def.nBufferSize; - mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec"); + IOMX::buffer_id buffer; - for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) { - sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize); - CHECK(mem.get() != NULL); + if (!strncasecmp( + mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.", 21)) { + if (portIndex == kPortIndexInput && i == 0) { + // Only log this warning once per allocation round. - IOMX::buffer_id buffer; + ALOGW("OMX.TI.DUCATI1.VIDEO.* require the use of " + "OMX_AllocateBuffer instead of the preferred " + "OMX_UseBuffer. Vendor must fix this."); + } - if (!strcasecmp( - mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) { - if (portIndex == kPortIndexInput && i == 0) { - // Only log this warning once per allocation round. + err = mOMX->allocateBufferWithBackup( + mNode, portIndex, mem, &buffer); + } else { + err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); + } - ALOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of " - "OMX_AllocateBuffer instead of the preferred " - "OMX_UseBuffer. Vendor must fix this."); + BufferInfo info; + info.mBufferID = buffer; + info.mStatus = BufferInfo::OWNED_BY_US; + info.mData = new ABuffer(mem->pointer(), def.nBufferSize); + mBuffers[portIndex].push(info); } - - err = mOMX->allocateBufferWithBackup( - mNode, portIndex, mem, &buffer); - } else { - err = mOMX->useBuffer(mNode, portIndex, mem, &buffer); } + } - if (err != OK) { - return err; - } + if (err != OK) { + return err; + } - BufferInfo info; - info.mBufferID = buffer; - info.mStatus = BufferInfo::OWNED_BY_US; - info.mData = new ABuffer(mem->pointer(), def.nBufferSize); - mBuffers[portIndex].push(info); + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", ACodec::kWhatBuffersAllocated); + + notify->setInt32("portIndex", portIndex); + for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) { + AString name = StringPrintf("buffer-id_%d", i); + notify->setPointer(name.c_str(), mBuffers[portIndex][i].mBufferID); + + name = StringPrintf("data_%d", i); + notify->setBuffer(name.c_str(), mBuffers[portIndex][i].mData); } + notify->post(); + return OK; } @@ -671,7 +706,7 @@ ACodec::BufferInfo *ACodec::findBufferByID( return NULL; } -void ACodec::setComponentRole( +status_t ACodec::setComponentRole( bool isEncoder, const char *mime) { struct MimeToRole { const char *mime; @@ -700,6 +735,8 @@ void ACodec::setComponentRole( "video_decoder.mpeg4", "video_encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_H263, "video_decoder.h263", "video_encoder.h263" }, + { MEDIA_MIMETYPE_VIDEO_VPX, + "video_decoder.vpx", "video_encoder.vpx" }, }; static const size_t kNumMimeToRole = @@ -713,7 +750,7 @@ void ACodec::setComponentRole( } if (i == kNumMimeToRole) { - return; + return ERROR_UNSUPPORTED; } const char *role = @@ -736,50 +773,83 @@ void ACodec::setComponentRole( if (err != OK) { ALOGW("[%s] Failed to set standard component role '%s'.", mComponentName.c_str(), role); + + return err; } } + + return OK; } -void ACodec::configureCodec( +status_t ACodec::configureCodec( const char *mime, const sp<AMessage> &msg) { - setComponentRole(false /* isEncoder */, mime); + int32_t encoder; + if (!msg->findInt32("encoder", &encoder)) { + encoder = false; + } - if (!strncasecmp(mime, "video/", 6)) { - int32_t width, height; - CHECK(msg->findInt32("width", &width)); - CHECK(msg->findInt32("height", &height)); + mIsEncoder = encoder; - CHECK_EQ(setupVideoDecoder(mime, width, height), - (status_t)OK); + status_t err = setComponentRole(encoder /* isEncoder */, mime); + + if (err != OK) { + return err; + } + + int32_t bitRate = 0; + if (encoder && !msg->findInt32("bitrate", &bitRate)) { + return INVALID_OPERATION; + } + + if (!strncasecmp(mime, "video/", 6)) { + if (encoder) { + err = setupVideoEncoder(mime, msg); + } else { + int32_t width, height; + if (!msg->findInt32("width", &width) + || !msg->findInt32("height", &height)) { + err = INVALID_OPERATION; + } else { + err = setupVideoDecoder(mime, width, height); + } + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { int32_t numChannels, sampleRate; - CHECK(msg->findInt32("channel-count", &numChannels)); - CHECK(msg->findInt32("sample-rate", &sampleRate)); - - CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK); + if (!msg->findInt32("channel-count", &numChannels) + || !msg->findInt32("sample-rate", &sampleRate)) { + err = INVALID_OPERATION; + } else { + err = setupAACCodec(encoder, numChannels, sampleRate, bitRate); + } } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) { - CHECK_EQ(setupAMRDecoder(false /* isWAMR */), (status_t)OK); + err = setupAMRCodec(encoder, false /* isWAMR */, bitRate); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) { - CHECK_EQ(setupAMRDecoder(true /* isWAMR */), (status_t)OK); + err = setupAMRCodec(encoder, true /* isWAMR */, bitRate); } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) { // These are PCM-like formats with a fixed sample rate but // a variable number of channels. int32_t numChannels; - CHECK(msg->findInt32("channel-count", &numChannels)); + if (!msg->findInt32("channel-count", &numChannels)) { + err = INVALID_OPERATION; + } else { + err = setupG711Codec(encoder, numChannels); + } + } - CHECK_EQ(setupG711Decoder(numChannels), (status_t)OK); + if (err != OK) { + return err; } int32_t maxInputSize; if (msg->findInt32("max-input-size", &maxInputSize)) { - CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize), - (status_t)OK); + err = setMinBufferSize(kPortIndexInput, (size_t)maxInputSize); } else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) { - CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192), // XXX - (status_t)OK); + err = setMinBufferSize(kPortIndexInput, 8192); // XXX } + + return err; } status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { @@ -819,12 +889,113 @@ status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) { return OK; } -status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) { +status_t ACodec::selectAudioPortFormat( + OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat) { + OMX_AUDIO_PARAM_PORTFORMATTYPE format; + InitOMXParams(&format); + + format.nPortIndex = portIndex; + for (OMX_U32 index = 0;; ++index) { + format.nIndex = index; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamAudioPortFormat, + &format, sizeof(format)); + + if (err != OK) { + return err; + } + + if (format.eEncoding == desiredFormat) { + break; + } + } + + return mOMX->setParameter( + mNode, OMX_IndexParamAudioPortFormat, &format, sizeof(format)); +} + +status_t ACodec::setupAACCodec( + bool encoder, + int32_t numChannels, int32_t sampleRate, int32_t bitRate) { + status_t err = setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, + sampleRate, + numChannels); + + if (err != OK) { + return err; + } + + if (encoder) { + err = selectAudioPortFormat(kPortIndexOutput, OMX_AUDIO_CodingAAC); + + if (err != OK) { + return err; + } + + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + def.nPortIndex = kPortIndexOutput; + + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + def.format.audio.bFlagErrorConcealment = OMX_TRUE; + def.format.audio.eEncoding = OMX_AUDIO_CodingAAC; + + err = mOMX->setParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + OMX_AUDIO_PARAM_AACPROFILETYPE profile; + InitOMXParams(&profile); + profile.nPortIndex = kPortIndexOutput; + + err = mOMX->getParameter( + mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); + + if (err != OK) { + return err; + } + + profile.nChannels = numChannels; + + profile.eChannelMode = + (numChannels == 1) + ? OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo; + + profile.nSampleRate = sampleRate; + profile.nBitRate = bitRate; + profile.nAudioBandWidth = 0; + profile.nFrameLength = 0; + profile.nAACtools = OMX_AUDIO_AACToolAll; + profile.nAACERtools = OMX_AUDIO_AACERNone; + profile.eAACProfile = OMX_AUDIO_AACObjectLC; + profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF; + + err = mOMX->setParameter( + mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); + + if (err != OK) { + return err; + } + + return err; + } + OMX_AUDIO_PARAM_AACPROFILETYPE profile; InitOMXParams(&profile); profile.nPortIndex = kPortIndexInput; - status_t err = mOMX->getParameter( + err = mOMX->getParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); if (err != OK) { @@ -835,16 +1006,59 @@ status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) { profile.nSampleRate = sampleRate; profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS; - err = mOMX->setParameter( + return mOMX->setParameter( mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile)); +} - return err; +static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate( + bool isAMRWB, int32_t bps) { + if (isAMRWB) { + if (bps <= 6600) { + return OMX_AUDIO_AMRBandModeWB0; + } else if (bps <= 8850) { + return OMX_AUDIO_AMRBandModeWB1; + } else if (bps <= 12650) { + return OMX_AUDIO_AMRBandModeWB2; + } else if (bps <= 14250) { + return OMX_AUDIO_AMRBandModeWB3; + } else if (bps <= 15850) { + return OMX_AUDIO_AMRBandModeWB4; + } else if (bps <= 18250) { + return OMX_AUDIO_AMRBandModeWB5; + } else if (bps <= 19850) { + return OMX_AUDIO_AMRBandModeWB6; + } else if (bps <= 23050) { + return OMX_AUDIO_AMRBandModeWB7; + } + + // 23850 bps + return OMX_AUDIO_AMRBandModeWB8; + } else { // AMRNB + if (bps <= 4750) { + return OMX_AUDIO_AMRBandModeNB0; + } else if (bps <= 5150) { + return OMX_AUDIO_AMRBandModeNB1; + } else if (bps <= 5900) { + return OMX_AUDIO_AMRBandModeNB2; + } else if (bps <= 6700) { + return OMX_AUDIO_AMRBandModeNB3; + } else if (bps <= 7400) { + return OMX_AUDIO_AMRBandModeNB4; + } else if (bps <= 7950) { + return OMX_AUDIO_AMRBandModeNB5; + } else if (bps <= 10200) { + return OMX_AUDIO_AMRBandModeNB6; + } + + // 12200 bps + return OMX_AUDIO_AMRBandModeNB7; + } } -status_t ACodec::setupAMRDecoder(bool isWAMR) { +status_t ACodec::setupAMRCodec(bool encoder, bool isWAMR, int32_t bitrate) { OMX_AUDIO_PARAM_AMRTYPE def; InitOMXParams(&def); - def.nPortIndex = kPortIndexInput; + def.nPortIndex = encoder ? kPortIndexOutput : kPortIndexInput; status_t err = mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); @@ -854,14 +1068,24 @@ status_t ACodec::setupAMRDecoder(bool isWAMR) { } def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF; + def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitrate); + + err = mOMX->setParameter( + mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); - def.eAMRBandMode = - isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0; + if (err != OK) { + return err; + } - return mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def)); + return setupRawAudioFormat( + encoder ? kPortIndexInput : kPortIndexOutput, + isWAMR ? 16000 : 8000 /* sampleRate */, + 1 /* numChannels */); } -status_t ACodec::setupG711Decoder(int32_t numChannels) { +status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) { + CHECK(!encoder); // XXX TODO + return setupRawAudioFormat( kPortIndexInput, 8000 /* sampleRate */, numChannels); } @@ -1001,22 +1225,36 @@ status_t ACodec::setSupportedOutputFormat() { &format, sizeof(format)); } -status_t ACodec::setupVideoDecoder( - const char *mime, int32_t width, int32_t height) { - OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused; +static status_t GetVideoCodingTypeFromMime( + const char *mime, OMX_VIDEO_CODINGTYPE *codingType) { if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { - compressionFormat = OMX_VIDEO_CodingAVC; + *codingType = OMX_VIDEO_CodingAVC; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) { - compressionFormat = OMX_VIDEO_CodingMPEG4; + *codingType = OMX_VIDEO_CodingMPEG4; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) { - compressionFormat = OMX_VIDEO_CodingH263; + *codingType = OMX_VIDEO_CodingH263; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) { - compressionFormat = OMX_VIDEO_CodingMPEG2; + *codingType = OMX_VIDEO_CodingMPEG2; + } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) { + *codingType = OMX_VIDEO_CodingVPX; } else { - TRESPASS(); + *codingType = OMX_VIDEO_CodingUnused; + return ERROR_UNSUPPORTED; } - status_t err = setVideoPortFormatType( + return OK; +} + +status_t ACodec::setupVideoDecoder( + const char *mime, int32_t width, int32_t height) { + OMX_VIDEO_CODINGTYPE compressionFormat; + status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat); + + if (err != OK) { + return err; + } + + err = setVideoPortFormatType( kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused); if (err != OK) { @@ -1046,6 +1284,489 @@ status_t ACodec::setupVideoDecoder( return OK; } +status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) { + int32_t tmp; + if (!msg->findInt32("color-format", &tmp)) { + return INVALID_OPERATION; + } + + OMX_COLOR_FORMATTYPE colorFormat = + static_cast<OMX_COLOR_FORMATTYPE>(tmp); + + status_t err = setVideoPortFormatType( + kPortIndexInput, OMX_VIDEO_CodingUnused, colorFormat); + + if (err != OK) { + ALOGE("[%s] does not support color format %d", + mComponentName.c_str(), colorFormat); + + return err; + } + + /* Input port configuration */ + + OMX_PARAM_PORTDEFINITIONTYPE def; + InitOMXParams(&def); + + OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video; + + def.nPortIndex = kPortIndexInput; + + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + int32_t width, height, bitrate; + if (!msg->findInt32("width", &width) + || !msg->findInt32("height", &height) + || !msg->findInt32("bitrate", &bitrate)) { + return INVALID_OPERATION; + } + + video_def->nFrameWidth = width; + video_def->nFrameHeight = height; + + int32_t stride; + if (!msg->findInt32("stride", &stride)) { + stride = width; + } + + video_def->nStride = stride; + + int32_t sliceHeight; + if (!msg->findInt32("slice-height", &sliceHeight)) { + sliceHeight = height; + } + + video_def->nSliceHeight = sliceHeight; + + def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2; + + float frameRate; + if (!msg->findFloat("frame-rate", &frameRate)) { + int32_t tmp; + if (!msg->findInt32("frame-rate", &tmp)) { + return INVALID_OPERATION; + } + frameRate = (float)tmp; + } + + video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f); + video_def->eCompressionFormat = OMX_VIDEO_CodingUnused; + video_def->eColorFormat = colorFormat; + + err = mOMX->setParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + ALOGE("[%s] failed to set input port definition parameters.", + mComponentName.c_str()); + + return err; + } + + /* Output port configuration */ + + OMX_VIDEO_CODINGTYPE compressionFormat; + err = GetVideoCodingTypeFromMime(mime, &compressionFormat); + + if (err != OK) { + return err; + } + + err = setVideoPortFormatType( + kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused); + + if (err != OK) { + ALOGE("[%s] does not support compression format %d", + mComponentName.c_str(), compressionFormat); + + return err; + } + + def.nPortIndex = kPortIndexOutput; + + err = mOMX->getParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + return err; + } + + video_def->nFrameWidth = width; + video_def->nFrameHeight = height; + video_def->xFramerate = 0; + video_def->nBitrate = bitrate; + video_def->eCompressionFormat = compressionFormat; + video_def->eColorFormat = OMX_COLOR_FormatUnused; + + err = mOMX->setParameter( + mNode, OMX_IndexParamPortDefinition, &def, sizeof(def)); + + if (err != OK) { + ALOGE("[%s] failed to set output port definition parameters.", + mComponentName.c_str()); + + return err; + } + + switch (compressionFormat) { + case OMX_VIDEO_CodingMPEG4: + err = setupMPEG4EncoderParameters(msg); + break; + + case OMX_VIDEO_CodingH263: + err = setupH263EncoderParameters(msg); + break; + + case OMX_VIDEO_CodingAVC: + err = setupAVCEncoderParameters(msg); + break; + + default: + break; + } + + ALOGI("setupVideoEncoder succeeded"); + + return err; +} + +static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) { + if (iFramesInterval < 0) { + return 0xFFFFFFFF; + } else if (iFramesInterval == 0) { + return 0; + } + OMX_U32 ret = frameRate * iFramesInterval; + CHECK(ret > 1); + return ret; +} + +status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) { + int32_t bitrate, iFrameInterval; + if (!msg->findInt32("bitrate", &bitrate) + || !msg->findInt32("i-frame-interval", &iFrameInterval)) { + return INVALID_OPERATION; + } + + float frameRate; + if (!msg->findFloat("frame-rate", &frameRate)) { + int32_t tmp; + if (!msg->findInt32("frame-rate", &tmp)) { + return INVALID_OPERATION; + } + frameRate = (float)tmp; + } + + OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type; + InitOMXParams(&mpeg4type); + mpeg4type.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); + + if (err != OK) { + return err; + } + + mpeg4type.nSliceHeaderSpacing = 0; + mpeg4type.bSVH = OMX_FALSE; + mpeg4type.bGov = OMX_FALSE; + + mpeg4type.nAllowedPictureTypes = + OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + + mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate); + if (mpeg4type.nPFrames == 0) { + mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; + } + mpeg4type.nBFrames = 0; + mpeg4type.nIDCVLCThreshold = 0; + mpeg4type.bACPred = OMX_TRUE; + mpeg4type.nMaxPacketSize = 256; + mpeg4type.nTimeIncRes = 1000; + mpeg4type.nHeaderExtension = 0; + mpeg4type.bReversibleVLC = OMX_FALSE; + + int32_t profile; + if (msg->findInt32("profile", &profile)) { + int32_t level; + if (!msg->findInt32("level", &level)) { + return INVALID_OPERATION; + } + + err = verifySupportForProfileAndLevel(profile, level); + + if (err != OK) { + return err; + } + + mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profile); + mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level); + } + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type)); + + if (err != OK) { + return err; + } + + err = configureBitrate(bitrate); + + if (err != OK) { + return err; + } + + return setupErrorCorrectionParameters(); +} + +status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) { + int32_t bitrate, iFrameInterval; + if (!msg->findInt32("bitrate", &bitrate) + || !msg->findInt32("i-frame-interval", &iFrameInterval)) { + return INVALID_OPERATION; + } + + float frameRate; + if (!msg->findFloat("frame-rate", &frameRate)) { + int32_t tmp; + if (!msg->findInt32("frame-rate", &tmp)) { + return INVALID_OPERATION; + } + frameRate = (float)tmp; + } + + OMX_VIDEO_PARAM_H263TYPE h263type; + InitOMXParams(&h263type); + h263type.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); + + if (err != OK) { + return err; + } + + h263type.nAllowedPictureTypes = + OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + + h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate); + if (h263type.nPFrames == 0) { + h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; + } + h263type.nBFrames = 0; + + int32_t profile; + if (msg->findInt32("profile", &profile)) { + int32_t level; + if (!msg->findInt32("level", &level)) { + return INVALID_OPERATION; + } + + err = verifySupportForProfileAndLevel(profile, level); + + if (err != OK) { + return err; + } + + h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profile); + h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(level); + } + + h263type.bPLUSPTYPEAllowed = OMX_FALSE; + h263type.bForceRoundingTypeToZero = OMX_FALSE; + h263type.nPictureHeaderRepetition = 0; + h263type.nGOBHeaderInterval = 0; + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type)); + + if (err != OK) { + return err; + } + + err = configureBitrate(bitrate); + + if (err != OK) { + return err; + } + + return setupErrorCorrectionParameters(); +} + +status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) { + int32_t bitrate, iFrameInterval; + if (!msg->findInt32("bitrate", &bitrate) + || !msg->findInt32("i-frame-interval", &iFrameInterval)) { + return INVALID_OPERATION; + } + + float frameRate; + if (!msg->findFloat("frame-rate", &frameRate)) { + int32_t tmp; + if (!msg->findInt32("frame-rate", &tmp)) { + return INVALID_OPERATION; + } + frameRate = (float)tmp; + } + + OMX_VIDEO_PARAM_AVCTYPE h264type; + InitOMXParams(&h264type); + h264type.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); + + if (err != OK) { + return err; + } + + h264type.nAllowedPictureTypes = + OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP; + + int32_t profile; + if (msg->findInt32("profile", &profile)) { + int32_t level; + if (!msg->findInt32("level", &level)) { + return INVALID_OPERATION; + } + + err = verifySupportForProfileAndLevel(profile, level); + + if (err != OK) { + return err; + } + + h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile); + h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level); + } + + // XXX + if (!strncmp(mComponentName.c_str(), "OMX.TI.DUCATI1", 14)) { + h264type.eProfile = OMX_VIDEO_AVCProfileBaseline; + } + + if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) { + h264type.nSliceHeaderSpacing = 0; + h264type.bUseHadamard = OMX_TRUE; + h264type.nRefFrames = 1; + h264type.nBFrames = 0; + h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate); + if (h264type.nPFrames == 0) { + h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI; + } + h264type.nRefIdx10ActiveMinus1 = 0; + h264type.nRefIdx11ActiveMinus1 = 0; + h264type.bEntropyCodingCABAC = OMX_FALSE; + h264type.bWeightedPPrediction = OMX_FALSE; + h264type.bconstIpred = OMX_FALSE; + h264type.bDirect8x8Inference = OMX_FALSE; + h264type.bDirectSpatialTemporal = OMX_FALSE; + h264type.nCabacInitIdc = 0; + } + + if (h264type.nBFrames != 0) { + h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB; + } + + h264type.bEnableUEP = OMX_FALSE; + h264type.bEnableFMO = OMX_FALSE; + h264type.bEnableASO = OMX_FALSE; + h264type.bEnableRS = OMX_FALSE; + h264type.bFrameMBsOnly = OMX_TRUE; + h264type.bMBAFF = OMX_FALSE; + h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable; + + if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName.c_str())) { + h264type.eLevel = OMX_VIDEO_AVCLevelMax; + } + + err = mOMX->setParameter( + mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type)); + + if (err != OK) { + return err; + } + + return configureBitrate(bitrate); +} + +status_t ACodec::verifySupportForProfileAndLevel( + int32_t profile, int32_t level) { + OMX_VIDEO_PARAM_PROFILELEVELTYPE params; + InitOMXParams(¶ms); + params.nPortIndex = kPortIndexOutput; + + for (params.nProfileIndex = 0;; ++params.nProfileIndex) { + status_t err = mOMX->getParameter( + mNode, + OMX_IndexParamVideoProfileLevelQuerySupported, + ¶ms, + sizeof(params)); + + if (err != OK) { + return err; + } + + int32_t supportedProfile = static_cast<int32_t>(params.eProfile); + int32_t supportedLevel = static_cast<int32_t>(params.eLevel); + + if (profile == supportedProfile && level <= supportedLevel) { + return OK; + } + } +} + +status_t ACodec::configureBitrate(int32_t bitrate) { + OMX_VIDEO_PARAM_BITRATETYPE bitrateType; + InitOMXParams(&bitrateType); + bitrateType.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoBitrate, + &bitrateType, sizeof(bitrateType)); + + if (err != OK) { + return err; + } + + bitrateType.eControlRate = OMX_Video_ControlRateVariable; + bitrateType.nTargetBitrate = bitrate; + + return mOMX->setParameter( + mNode, OMX_IndexParamVideoBitrate, + &bitrateType, sizeof(bitrateType)); +} + +status_t ACodec::setupErrorCorrectionParameters() { + OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType; + InitOMXParams(&errorCorrectionType); + errorCorrectionType.nPortIndex = kPortIndexOutput; + + status_t err = mOMX->getParameter( + mNode, OMX_IndexParamVideoErrorCorrection, + &errorCorrectionType, sizeof(errorCorrectionType)); + + if (err != OK) { + return OK; // Optional feature. Ignore this failure + } + + errorCorrectionType.bEnableHEC = OMX_FALSE; + errorCorrectionType.bEnableResync = OMX_TRUE; + errorCorrectionType.nResynchMarkerSpacing = 256; + errorCorrectionType.bEnableDataPartitioning = OMX_FALSE; + errorCorrectionType.bEnableRVLC = OMX_FALSE; + + return mOMX->setParameter( + mNode, OMX_IndexParamVideoErrorCorrection, + &errorCorrectionType, sizeof(errorCorrectionType)); +} + status_t ACodec::setVideoFormatOnPort( OMX_U32 portIndex, int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) { @@ -1166,6 +1887,9 @@ void ACodec::sendFormatChange() { notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW); notify->setInt32("width", videoDef->nFrameWidth); notify->setInt32("height", videoDef->nFrameHeight); + notify->setInt32("stride", videoDef->nStride); + notify->setInt32("slice-height", videoDef->nSliceHeight); + notify->setInt32("color-format", videoDef->eColorFormat); OMX_CONFIG_RECTTYPE rect; InitOMXParams(&rect); @@ -1241,10 +1965,11 @@ void ACodec::sendFormatChange() { mSentFormat = true; } -void ACodec::signalError(OMX_ERRORTYPE error) { +void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", ACodec::kWhatError); notify->setInt32("omx-error", error); + notify->setInt32("err", internalError); notify->post(); } @@ -1417,7 +2142,7 @@ void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) { notify->setPointer("buffer-id", info->mBufferID); info->mData->meta()->clear(); - notify->setObject("buffer", info->mData); + notify->setBuffer("buffer", info->mData); sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id()); reply->setPointer("buffer-id", info->mBufferID); @@ -1433,18 +2158,26 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { IOMX::buffer_id bufferID; CHECK(msg->findPointer("buffer-id", &bufferID)); - sp<RefBase> obj; + sp<ABuffer> buffer; int32_t err = OK; - if (!msg->findObject("buffer", &obj)) { + bool eos = false; + + if (!msg->findBuffer("buffer", &buffer)) { CHECK(msg->findInt32("err", &err)); ALOGV("[%s] saw error %d instead of an input buffer", mCodec->mComponentName.c_str(), err); - obj.clear(); + buffer.clear(); + + eos = true; } - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + int32_t tmp; + if (buffer != NULL && buffer->meta()->findInt32("eos", &tmp) && tmp) { + eos = true; + err = ERROR_END_OF_STREAM; + } BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID); CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM); @@ -1456,7 +2189,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { switch (mode) { case KEEP_BUFFERS: { - if (buffer == NULL) { + if (eos) { if (!mCodec->mPortEOS[kPortIndexInput]) { mCodec->mPortEOS[kPortIndexInput] = true; mCodec->mInputEOSResult = err; @@ -1467,9 +2200,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { case RESUBMIT_BUFFERS: { - if (buffer != NULL) { - CHECK(!mCodec->mPortEOS[kPortIndexInput]); - + if (buffer != NULL && !mCodec->mPortEOS[kPortIndexInput]) { int64_t timeUs; CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); @@ -1480,6 +2211,10 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { flags |= OMX_BUFFERFLAG_CODECCONFIG; } + if (eos) { + flags |= OMX_BUFFERFLAG_EOS; + } + if (buffer != info->mData) { if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) { ALOGV("[%s] Needs to copy input data.", @@ -1493,6 +2228,9 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { if (flags & OMX_BUFFERFLAG_CODECCONFIG) { ALOGV("[%s] calling emptyBuffer %p w/ codec specific data", mCodec->mComponentName.c_str(), bufferID); + } else if (flags & OMX_BUFFERFLAG_EOS) { + ALOGV("[%s] calling emptyBuffer %p w/ EOS", + mCodec->mComponentName.c_str(), bufferID); } else { ALOGV("[%s] calling emptyBuffer %p w/ time %lld us", mCodec->mComponentName.c_str(), bufferID, timeUs); @@ -1509,7 +2247,15 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) { info->mStatus = BufferInfo::OWNED_BY_COMPONENT; - getMoreInputDataIfPossible(); + if (!eos) { + getMoreInputDataIfPossible(); + } else { + ALOGV("[%s] Signalled EOS on the input port", + mCodec->mComponentName.c_str()); + + mCodec->mPortEOS[kPortIndexInput] = true; + mCodec->mInputEOSResult = err; + } } else if (!mCodec->mPortEOS[kPortIndexInput]) { if (err != ERROR_END_OF_STREAM) { ALOGV("[%s] Signalling EOS on the input port " @@ -1582,8 +2328,8 @@ bool ACodec::BaseState::onOMXFillBufferDone( int64_t timeUs, void *platformPrivate, void *dataPtr) { - ALOGV("[%s] onOMXFillBufferDone %p time %lld us", - mCodec->mComponentName.c_str(), bufferID, timeUs); + ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx", + mCodec->mComponentName.c_str(), bufferID, timeUs, flags); ssize_t index; BufferInfo *info = @@ -1601,46 +2347,48 @@ bool ACodec::BaseState::onOMXFillBufferDone( case RESUBMIT_BUFFERS: { - if (rangeLength == 0) { - if (!(flags & OMX_BUFFERFLAG_EOS)) { - ALOGV("[%s] calling fillBuffer %p", - mCodec->mComponentName.c_str(), info->mBufferID); + if (rangeLength == 0 && !(flags & OMX_BUFFERFLAG_EOS)) { + ALOGV("[%s] calling fillBuffer %p", + mCodec->mComponentName.c_str(), info->mBufferID); - CHECK_EQ(mCodec->mOMX->fillBuffer( - mCodec->mNode, info->mBufferID), - (status_t)OK); + CHECK_EQ(mCodec->mOMX->fillBuffer( + mCodec->mNode, info->mBufferID), + (status_t)OK); - info->mStatus = BufferInfo::OWNED_BY_COMPONENT; - } - } else { - if (!mCodec->mSentFormat) { - mCodec->sendFormatChange(); - } + info->mStatus = BufferInfo::OWNED_BY_COMPONENT; + break; + } - if (mCodec->mNativeWindow == NULL) { - info->mData->setRange(rangeOffset, rangeLength); - } + if (!mCodec->mIsEncoder && !mCodec->mSentFormat) { + mCodec->sendFormatChange(); + } - info->mData->meta()->setInt64("timeUs", timeUs); + if (mCodec->mNativeWindow == NULL) { + info->mData->setRange(rangeOffset, rangeLength); + } - sp<AMessage> notify = mCodec->mNotify->dup(); - notify->setInt32("what", ACodec::kWhatDrainThisBuffer); - notify->setPointer("buffer-id", info->mBufferID); - notify->setObject("buffer", info->mData); + info->mData->meta()->setInt64("timeUs", timeUs); - sp<AMessage> reply = - new AMessage(kWhatOutputBufferDrained, mCodec->id()); + sp<AMessage> notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatDrainThisBuffer); + notify->setPointer("buffer-id", info->mBufferID); + notify->setBuffer("buffer", info->mData); + notify->setInt32("flags", flags); - reply->setPointer("buffer-id", info->mBufferID); + sp<AMessage> reply = + new AMessage(kWhatOutputBufferDrained, mCodec->id()); - notify->setMessage("reply", reply); + reply->setPointer("buffer-id", info->mBufferID); - notify->post(); + notify->setMessage("reply", reply); - info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM; - } + notify->post(); + + info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM; if (flags & OMX_BUFFERFLAG_EOS) { + ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str()); + sp<AMessage> notify = mCodec->mNotify->dup(); notify->setInt32("what", ACodec::kWhatEOS); notify->setInt32("err", mCodec->mInputEOSResult); @@ -1678,12 +2426,13 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) { && msg->findInt32("render", &render) && render != 0) { // The client wants this buffer to be rendered. - if (mCodec->mNativeWindow->queueBuffer( + status_t err; + if ((err = mCodec->mNativeWindow->queueBuffer( mCodec->mNativeWindow.get(), - info->mGraphicBuffer.get()) == OK) { + info->mGraphicBuffer.get())) == OK) { info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW; } else { - mCodec->signalError(); + mCodec->signalError(OMX_ErrorUndefined, err); info->mStatus = BufferInfo::OWNED_BY_US; } } else { @@ -1758,6 +2507,27 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) { break; } + case ACodec::kWhatAllocateComponent: + { + onAllocateComponent(msg); + handled = true; + break; + } + + case ACodec::kWhatConfigureComponent: + { + onConfigureComponent(msg); + handled = true; + break; + } + + case ACodec::kWhatStart: + { + onStart(); + handled = true; + break; + } + case ACodec::kWhatShutdown: { sp<AMessage> notify = mCodec->mNotify->dup(); @@ -1787,27 +2557,54 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) { void ACodec::UninitializedState::onSetup( const sp<AMessage> &msg) { + onAllocateComponent(msg); + onConfigureComponent(msg); + onStart(); +} + +void ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) { + ALOGV("onAllocateComponent"); + + if (mCodec->mNode != NULL) { + CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK); + + mCodec->mNativeWindow.clear(); + mCodec->mNode = NULL; + mCodec->mOMX.clear(); + mCodec->mComponentName.clear(); + } + OMXClient client; CHECK_EQ(client.connect(), (status_t)OK); sp<IOMX> omx = client.interface(); + Vector<String8> matchingCodecs; + AString mime; - CHECK(msg->findString("mime", &mime)); - Vector<String8> matchingCodecs; - OMXCodec::findMatchingCodecs( - mime.c_str(), - false, // createEncoder - NULL, // matchComponentName - 0, // flags - &matchingCodecs); + AString componentName; + if (msg->findString("componentName", &componentName)) { + matchingCodecs.push_back(String8(componentName.c_str())); + } else { + CHECK(msg->findString("mime", &mime)); + + int32_t encoder; + if (!msg->findInt32("encoder", &encoder)) { + encoder = false; + } + + OMXCodec::findMatchingCodecs( + mime.c_str(), + encoder, // createEncoder + NULL, // matchComponentName + 0, // flags + &matchingCodecs); + } sp<CodecObserver> observer = new CodecObserver; IOMX::node_id node = NULL; - AString componentName; - for (size_t matchIndex = 0; matchIndex < matchingCodecs.size(); ++matchIndex) { componentName = matchingCodecs.itemAt(matchIndex).string(); @@ -1826,7 +2623,12 @@ void ACodec::UninitializedState::onSetup( } if (node == NULL) { - ALOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str()); + if (!mime.empty()) { + ALOGE("Unable to instantiate a decoder for type '%s'.", + mime.c_str()); + } else { + ALOGE("Unable to instantiate decoder '%s'.", componentName.c_str()); + } mCodec->signalError(OMX_ErrorComponentNotFound); return; @@ -1844,20 +2646,52 @@ void ACodec::UninitializedState::onSetup( mCodec->mInputEOSResult = OK; - mCodec->configureCodec(mime.c_str(), msg); + { + sp<AMessage> notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatComponentAllocated); + notify->setString("componentName", mCodec->mComponentName.c_str()); + notify->post(); + } +} + +void ACodec::UninitializedState::onConfigureComponent( + const sp<AMessage> &msg) { + ALOGV("onConfigureComponent"); + + CHECK(mCodec->mNode != NULL); + + AString mime; + CHECK(msg->findString("mime", &mime)); + + status_t err = mCodec->configureCodec(mime.c_str(), msg); + + if (err != OK) { + mCodec->signalError(OMX_ErrorUndefined, err); + return; + } sp<RefBase> obj; if (msg->findObject("native-window", &obj) - && strncmp("OMX.google.", componentName.c_str(), 11)) { + && strncmp("OMX.google.", mCodec->mComponentName.c_str(), 11)) { sp<NativeWindowWrapper> nativeWindow( static_cast<NativeWindowWrapper *>(obj.get())); CHECK(nativeWindow != NULL); mCodec->mNativeWindow = nativeWindow->getNativeWindow(); } - CHECK_EQ((status_t)OK, mCodec->initNativeWindow()); - CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle), + { + sp<AMessage> notify = mCodec->mNotify->dup(); + notify->setInt32("what", ACodec::kWhatComponentConfigured); + notify->post(); + } +} + +void ACodec::UninitializedState::onStart() { + ALOGV("onStart"); + + CHECK_EQ(mCodec->mOMX->sendCommand( + mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle), (status_t)OK); mCodec->changeState(mCodec->mLoadedToIdleState); @@ -1878,7 +2712,7 @@ void ACodec::LoadedToIdleState::stateEntered() { "(error 0x%08x)", err); - mCodec->signalError(); + mCodec->signalError(OMX_ErrorUndefined, err); } } @@ -2202,7 +3036,7 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent( "port reconfiguration (error 0x%08x)", err); - mCodec->signalError(); + mCodec->signalError(OMX_ErrorUndefined, err); // This is technically not correct, since we were unable // to allocate output buffers and therefore the output port @@ -2240,7 +3074,8 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent( //////////////////////////////////////////////////////////////////////////////// ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec) - : BaseState(codec) { + : BaseState(codec), + mComponentNowIdle(false) { } bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) { @@ -2274,6 +3109,7 @@ bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) { void ACodec::ExecutingToIdleState::stateEntered() { ALOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str()); + mComponentNowIdle = false; mCodec->mSentFormat = false; } @@ -2285,6 +3121,8 @@ bool ACodec::ExecutingToIdleState::onOMXEvent( CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet); CHECK_EQ(data2, (OMX_U32)OMX_StateIdle); + mComponentNowIdle = true; + changeStateIfWeOwnAllBuffers(); return true; @@ -2303,7 +3141,7 @@ bool ACodec::ExecutingToIdleState::onOMXEvent( } void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() { - if (mCodec->allYourBuffersAreBelongToUs()) { + if (mComponentNowIdle && mCodec->allYourBuffersAreBelongToUs()) { CHECK_EQ(mCodec->mOMX->sendCommand( mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded), (status_t)OK); diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index 483e5ab0c160..cfb1e29aa1bf 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -29,12 +29,14 @@ LOCAL_SRC_FILES:= \ MPEG4Writer.cpp \ MediaBuffer.cpp \ MediaBufferGroup.cpp \ + MediaCodec.cpp \ MediaDefs.cpp \ MediaExtractor.cpp \ MediaSource.cpp \ MediaSourceSplitter.cpp \ MetaData.cpp \ NuCachedSource2.cpp \ + NuMediaExtractor.cpp \ OMXClient.cpp \ OMXCodec.cpp \ OggExtractor.cpp \ @@ -61,20 +63,26 @@ LOCAL_C_INCLUDES:= \ $(TOP)/external/openssl/include \ LOCAL_SHARED_LIBRARIES := \ - libbinder \ - libmedia \ - libutils \ - libcutils \ - libui \ - libsonivox \ - libvorbisidec \ + libbinder \ + libmedia \ + libutils \ + libcutils \ + libui \ + libsonivox \ + libvorbisidec \ libstagefright_yuv \ libcamera_client \ - libdrmframework \ - libcrypto \ - libssl \ - libgui \ + libdrmframework \ + libcrypto \ + libssl \ + libgui \ libstagefright_omx \ + liblog \ + libicuuc \ + libicui18n \ + libz \ + libdl \ + libchromium_net \ LOCAL_STATIC_LIBRARIES := \ libstagefright_color_conversion \ @@ -88,57 +96,14 @@ LOCAL_STATIC_LIBRARIES := \ libstagefright_httplive \ libstagefright_id3 \ libFLAC \ + libstagefright_chromium_http \ -################################################################################ - -# The following was shamelessly copied from external/webkit/Android.mk and -# currently must follow the same logic to determine how webkit was built and -# if it's safe to link against libchromium.net - -# V8 also requires an ARMv7 CPU, and since we must use jsc, we cannot -# use the Chrome http stack either. -ifneq ($(strip $(ARCH_ARM_HAVE_ARMV7A)),true) - USE_ALT_HTTP := true -endif - -# See if the user has specified a stack they want to use -HTTP_STACK = $(HTTP) -# We default to the Chrome HTTP stack. -DEFAULT_HTTP = chrome -ALT_HTTP = android - -ifneq ($(HTTP_STACK),chrome) - ifneq ($(HTTP_STACK),android) - # No HTTP stack is specified, pickup the one we want as default. - ifeq ($(USE_ALT_HTTP),true) - HTTP_STACK = $(ALT_HTTP) - else - HTTP_STACK = $(DEFAULT_HTTP) - endif - endif -endif - -ifeq ($(HTTP_STACK),chrome) - -LOCAL_SHARED_LIBRARIES += \ - liblog \ - libicuuc \ - libicui18n \ - libz \ - libdl \ - -LOCAL_STATIC_LIBRARIES += \ - libstagefright_chromium_http - -LOCAL_SHARED_LIBRARIES += libstlport libchromium_net +LOCAL_SHARED_LIBRARIES += libstlport include external/stlport/libstlport.mk +# TODO: Chromium is always available, so this flag can be removed. LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1 -endif # ifeq ($(HTTP_STACK),chrome) - -################################################################################ - LOCAL_SHARED_LIBRARIES += \ libstagefright_enc_common \ libstagefright_avc_common \ diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index fef2a006baa0..5b2ea1f80232 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -282,8 +282,6 @@ status_t AudioSource::dataCallbackTimestamp( mPrevSampleTimeUs = mStartTimeUs; } - int64_t timestampUs = mPrevSampleTimeUs; - size_t numLostBytes = 0; if (mNumFramesReceived > 0) { // Ignore earlier frame lost // getInputFramesLost() returns the number of lost frames. @@ -293,37 +291,58 @@ status_t AudioSource::dataCallbackTimestamp( CHECK_EQ(numLostBytes & 1, 0u); CHECK_EQ(audioBuffer.size & 1, 0u); - size_t bufferSize = numLostBytes + audioBuffer.size; - MediaBuffer *buffer = new MediaBuffer(bufferSize); if (numLostBytes > 0) { - memset(buffer->data(), 0, numLostBytes); - memcpy((uint8_t *) buffer->data() + numLostBytes, - audioBuffer.i16, audioBuffer.size); - } else { - if (audioBuffer.size == 0) { - ALOGW("Nothing is available from AudioRecord callback buffer"); - buffer->release(); - return OK; + // Loss of audio frames should happen rarely; thus the LOGW should + // not cause a logging spam + ALOGW("Lost audio record data: %d bytes", numLostBytes); + } + + while (numLostBytes > 0) { + size_t bufferSize = numLostBytes; + if (numLostBytes > kMaxBufferSize) { + numLostBytes -= kMaxBufferSize; + bufferSize = kMaxBufferSize; + } else { + numLostBytes = 0; } - memcpy((uint8_t *) buffer->data(), - audioBuffer.i16, audioBuffer.size); + MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize); + memset(lostAudioBuffer->data(), 0, bufferSize); + lostAudioBuffer->set_range(0, bufferSize); + queueInputBuffer_l(lostAudioBuffer, timeUs); + } + + if (audioBuffer.size == 0) { + ALOGW("Nothing is available from AudioRecord callback buffer"); + return OK; } + const size_t bufferSize = audioBuffer.size; + MediaBuffer *buffer = new MediaBuffer(bufferSize); + memcpy((uint8_t *) buffer->data(), + audioBuffer.i16, audioBuffer.size); buffer->set_range(0, bufferSize); - timestampUs += ((1000000LL * (bufferSize >> 1)) + - (mSampleRate >> 1)) / mSampleRate; + queueInputBuffer_l(buffer, timeUs); + return OK; +} + +void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) { + const size_t bufferSize = buffer->range_length(); + const size_t frameSize = mRecord->frameSize(); + const int64_t timestampUs = + mPrevSampleTimeUs + + ((1000000LL * (bufferSize / frameSize)) + + (mSampleRate >> 1)) / mSampleRate; if (mNumFramesReceived == 0) { buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs); } + buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs); buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs); mPrevSampleTimeUs = timestampUs; - mNumFramesReceived += buffer->range_length() / sizeof(int16_t); + mNumFramesReceived += bufferSize / frameSize; mBuffersReceived.push_back(buffer); mFrameAvailableCondition.signal(); - - return OK; } void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) { diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp index 0b4ecbea4e6c..f70237628291 100644 --- a/media/libstagefright/MPEG2TSWriter.cpp +++ b/media/libstagefright/MPEG2TSWriter.cpp @@ -244,7 +244,7 @@ void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kNotifyBuffer); - notify->setObject("buffer", out); + notify->setBuffer("buffer", out); notify->setInt32("oob", true); notify->post(); } @@ -270,7 +270,7 @@ void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) { copy->meta()->setInt32("isSync", true); } - notify->setObject("buffer", copy); + notify->setBuffer("buffer", copy); notify->post(); } @@ -351,7 +351,7 @@ bool MPEG2TSWriter::SourceInfo::flushAACFrames() { sp<AMessage> notify = mNotify->dup(); notify->setInt32("what", kNotifyBuffer); - notify->setObject("buffer", mAACBuffer); + notify->setBuffer("buffer", mAACBuffer); notify->post(); mAACBuffer.clear(); @@ -614,10 +614,8 @@ void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) { ++mNumSourcesDone; } else if (what == SourceInfo::kNotifyBuffer) { - sp<RefBase> obj; - CHECK(msg->findObject("buffer", &obj)); - - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); int32_t oob; if (msg->findInt32("oob", &oob) && oob) { diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp new file mode 100644 index 000000000000..4acbdbe8b0f4 --- /dev/null +++ b/media/libstagefright/MediaCodec.cpp @@ -0,0 +1,1180 @@ +/* + * Copyright 2012, 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_NDEBUG 0 +#define LOG_TAG "MediaCodec" +#include <utils/Log.h> + +#include <media/stagefright/MediaCodec.h> + +#include "include/SoftwareRenderer.h" + +#include <gui/SurfaceTextureClient.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/ACodec.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/NativeWindowWrapper.h> + +namespace android { + +// static +sp<MediaCodec> MediaCodec::CreateByType( + const sp<ALooper> &looper, const char *mime, bool encoder) { + sp<MediaCodec> codec = new MediaCodec(looper); + if (codec->init(mime, true /* nameIsType */, encoder) != OK) { + return NULL; + } + + return codec; +} + +// static +sp<MediaCodec> MediaCodec::CreateByComponentName( + const sp<ALooper> &looper, const char *name) { + sp<MediaCodec> codec = new MediaCodec(looper); + if (codec->init(name, false /* nameIsType */, false /* encoder */) != OK) { + return NULL; + } + + return codec; +} + +MediaCodec::MediaCodec(const sp<ALooper> &looper) + : mState(UNINITIALIZED), + mLooper(looper), + mCodec(new ACodec), + mFlags(0), + mSoftRenderer(NULL), + mDequeueInputTimeoutGeneration(0), + mDequeueInputReplyID(0), + mDequeueOutputTimeoutGeneration(0), + mDequeueOutputReplyID(0) { +} + +MediaCodec::~MediaCodec() { + CHECK_EQ(mState, UNINITIALIZED); +} + +// static +status_t MediaCodec::PostAndAwaitResponse( + const sp<AMessage> &msg, sp<AMessage> *response) { + status_t err = msg->postAndAwaitResponse(response); + + if (err != OK) { + return err; + } + + if (!(*response)->findInt32("err", &err)) { + err = OK; + } + + return err; +} + +status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) { + // Current video decoders do not return from OMX_FillThisBuffer + // quickly, violating the OpenMAX specs, until that is remedied + // we need to invest in an extra looper to free the main event + // queue. + bool needDedicatedLooper = false; + if (nameIsType && !strncasecmp(name, "video/", 6)) { + needDedicatedLooper = true; + } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) { + needDedicatedLooper = true; + } + + if (needDedicatedLooper) { + if (mCodecLooper == NULL) { + mCodecLooper = new ALooper; + mCodecLooper->setName("CodecLooper"); + mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + } + + mCodecLooper->registerHandler(mCodec); + } else { + mLooper->registerHandler(mCodec); + } + + mLooper->registerHandler(this); + + mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id())); + + sp<AMessage> msg = new AMessage(kWhatInit, id()); + msg->setString("name", name); + msg->setInt32("nameIsType", nameIsType); + + if (nameIsType) { + msg->setInt32("encoder", encoder); + } + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::configure( + const sp<AMessage> &format, + const sp<SurfaceTextureClient> &nativeWindow, + uint32_t flags) { + sp<AMessage> msg = new AMessage(kWhatConfigure, id()); + + msg->setMessage("format", format); + msg->setInt32("flags", flags); + + if (nativeWindow != NULL) { + if (!(mFlags & kFlagIsSoftwareCodec)) { + msg->setObject( + "native-window", + new NativeWindowWrapper(nativeWindow)); + } else { + mNativeWindow = nativeWindow; + } + } + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::start() { + sp<AMessage> msg = new AMessage(kWhatStart, id()); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::stop() { + sp<AMessage> msg = new AMessage(kWhatStop, id()); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::queueInputBuffer( + size_t index, + size_t offset, + size_t size, + int64_t presentationTimeUs, + uint32_t flags) { + sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id()); + msg->setSize("index", index); + msg->setSize("offset", offset); + msg->setSize("size", size); + msg->setInt64("timeUs", presentationTimeUs); + msg->setInt32("flags", flags); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) { + sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id()); + msg->setInt64("timeoutUs", timeoutUs); + + sp<AMessage> response; + status_t err; + if ((err = PostAndAwaitResponse(msg, &response)) != OK) { + return err; + } + + CHECK(response->findSize("index", index)); + + return OK; +} + +status_t MediaCodec::dequeueOutputBuffer( + size_t *index, + size_t *offset, + size_t *size, + int64_t *presentationTimeUs, + uint32_t *flags, + int64_t timeoutUs) { + sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, id()); + msg->setInt64("timeoutUs", timeoutUs); + + sp<AMessage> response; + status_t err; + if ((err = PostAndAwaitResponse(msg, &response)) != OK) { + return err; + } + + CHECK(response->findSize("index", index)); + CHECK(response->findSize("offset", offset)); + CHECK(response->findSize("size", size)); + CHECK(response->findInt64("timeUs", presentationTimeUs)); + CHECK(response->findInt32("flags", (int32_t *)flags)); + + return OK; +} + +status_t MediaCodec::renderOutputBufferAndRelease(size_t index) { + sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); + msg->setSize("index", index); + msg->setInt32("render", true); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::releaseOutputBuffer(size_t index) { + sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id()); + msg->setSize("index", index); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const { + sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id()); + + sp<AMessage> response; + status_t err; + if ((err = PostAndAwaitResponse(msg, &response)) != OK) { + return err; + } + + CHECK(response->findMessage("format", format)); + + return OK; +} + +status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetBuffers, id()); + msg->setInt32("portIndex", kPortIndexInput); + msg->setPointer("buffers", buffers); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const { + sp<AMessage> msg = new AMessage(kWhatGetBuffers, id()); + msg->setInt32("portIndex", kPortIndexOutput); + msg->setPointer("buffers", buffers); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +status_t MediaCodec::flush() { + sp<AMessage> msg = new AMessage(kWhatFlush, id()); + + sp<AMessage> response; + return PostAndAwaitResponse(msg, &response); +} + +//////////////////////////////////////////////////////////////////////////////// + +void MediaCodec::cancelPendingDequeueOperations() { + if (mFlags & kFlagDequeueInputPending) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + response->postReply(mDequeueInputReplyID); + + ++mDequeueInputTimeoutGeneration; + mDequeueInputReplyID = 0; + mFlags &= ~kFlagDequeueInputPending; + } + + if (mFlags & kFlagDequeueOutputPending) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + response->postReply(mDequeueOutputReplyID); + + ++mDequeueOutputTimeoutGeneration; + mDequeueOutputReplyID = 0; + mFlags &= ~kFlagDequeueOutputPending; + } +} + +bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) { + if (mState != STARTED + || (mFlags & kFlagStickyError) + || (newRequest && (mFlags & kFlagDequeueInputPending))) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + + return true; + } + + ssize_t index = dequeuePortBuffer(kPortIndexInput); + + if (index < 0) { + CHECK_EQ(index, -EAGAIN); + return false; + } + + sp<AMessage> response = new AMessage; + response->setSize("index", index); + response->postReply(replyID); + + return true; +} + +bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) { + sp<AMessage> response = new AMessage; + + if (mState != STARTED + || (mFlags & kFlagStickyError) + || (newRequest && (mFlags & kFlagDequeueOutputPending))) { + response->setInt32("err", INVALID_OPERATION); + } else if (mFlags & kFlagOutputBuffersChanged) { + response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED); + mFlags &= ~kFlagOutputBuffersChanged; + } else if (mFlags & kFlagOutputFormatChanged) { + response->setInt32("err", INFO_FORMAT_CHANGED); + mFlags &= ~kFlagOutputFormatChanged; + } else { + ssize_t index = dequeuePortBuffer(kPortIndexOutput); + + if (index < 0) { + CHECK_EQ(index, -EAGAIN); + return false; + } + + const sp<ABuffer> &buffer = + mPortBuffers[kPortIndexOutput].itemAt(index).mData; + + response->setSize("index", index); + response->setSize("offset", buffer->offset()); + response->setSize("size", buffer->size()); + + int64_t timeUs; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + response->setInt64("timeUs", timeUs); + + int32_t omxFlags; + CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags)); + + uint32_t flags = 0; + if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) { + flags |= BUFFER_FLAG_SYNCFRAME; + } + if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) { + flags |= BUFFER_FLAG_CODECCONFIG; + } + if (omxFlags & OMX_BUFFERFLAG_EOS) { + flags |= BUFFER_FLAG_EOS; + } + + response->setInt32("flags", flags); + } + + response->postReply(replyID); + + return true; +} + +void MediaCodec::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatCodecNotify: + { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case ACodec::kWhatError: + { + int32_t omxError, internalError; + CHECK(msg->findInt32("omx-error", &omxError)); + CHECK(msg->findInt32("err", &internalError)); + + ALOGE("Codec reported an error. " + "(omx error 0x%08x, internalError %d)", + omxError, internalError); + + bool sendErrorReponse = true; + + switch (mState) { + case INITIALIZING: + { + setState(UNINITIALIZED); + break; + } + + case CONFIGURING: + { + setState(INITIALIZED); + break; + } + + case STARTING: + { + setState(CONFIGURED); + break; + } + + case STOPPING: + { + // Ignore the error, assuming we'll still get + // the shutdown complete notification. + + sendErrorReponse = false; + break; + } + + case FLUSHING: + { + setState(STARTED); + break; + } + + case STARTED: + { + sendErrorReponse = false; + + mFlags |= kFlagStickyError; + + cancelPendingDequeueOperations(); + break; + } + + default: + { + sendErrorReponse = false; + + mFlags |= kFlagStickyError; + break; + } + } + + if (sendErrorReponse) { + sp<AMessage> response = new AMessage; + response->setInt32("err", UNKNOWN_ERROR); + + response->postReply(mReplyID); + } + break; + } + + case ACodec::kWhatComponentAllocated: + { + CHECK_EQ(mState, INITIALIZING); + setState(INITIALIZED); + + AString componentName; + CHECK(msg->findString("componentName", &componentName)); + + if (componentName.startsWith("OMX.google.")) { + mFlags |= kFlagIsSoftwareCodec; + } else { + mFlags &= ~kFlagIsSoftwareCodec; + } + + (new AMessage)->postReply(mReplyID); + break; + } + + case ACodec::kWhatComponentConfigured: + { + CHECK_EQ(mState, CONFIGURING); + setState(CONFIGURED); + + (new AMessage)->postReply(mReplyID); + break; + } + + case ACodec::kWhatBuffersAllocated: + { + int32_t portIndex; + CHECK(msg->findInt32("portIndex", &portIndex)); + + ALOGV("%s buffers allocated", + portIndex == kPortIndexInput ? "input" : "output"); + + CHECK(portIndex == kPortIndexInput + || portIndex == kPortIndexOutput); + + mPortBuffers[portIndex].clear(); + + Vector<BufferInfo> *buffers = &mPortBuffers[portIndex]; + for (size_t i = 0;; ++i) { + AString name = StringPrintf("buffer-id_%d", i); + + void *bufferID; + if (!msg->findPointer(name.c_str(), &bufferID)) { + break; + } + + name = StringPrintf("data_%d", i); + + BufferInfo info; + info.mBufferID = bufferID; + info.mOwnedByClient = false; + CHECK(msg->findBuffer(name.c_str(), &info.mData)); + + buffers->push_back(info); + } + + if (portIndex == kPortIndexOutput) { + if (mState == STARTING) { + // We're always allocating output buffers after + // allocating input buffers, so this is a good + // indication that now all buffers are allocated. + setState(STARTED); + (new AMessage)->postReply(mReplyID); + } else { + mFlags |= kFlagOutputBuffersChanged; + } + } + break; + } + + case ACodec::kWhatOutputFormatChanged: + { + ALOGV("codec output format changed"); + + if ((mFlags & kFlagIsSoftwareCodec) + && mNativeWindow != NULL) { + AString mime; + CHECK(msg->findString("mime", &mime)); + + if (!strncasecmp("video/", mime.c_str(), 6)) { + delete mSoftRenderer; + mSoftRenderer = NULL; + + int32_t width, height; + CHECK(msg->findInt32("width", &width)); + CHECK(msg->findInt32("height", &height)); + + int32_t colorFormat; + CHECK(msg->findInt32( + "color-format", &colorFormat)); + + sp<MetaData> meta = new MetaData; + meta->setInt32(kKeyWidth, width); + meta->setInt32(kKeyHeight, height); + meta->setInt32(kKeyColorFormat, colorFormat); + + mSoftRenderer = + new SoftwareRenderer(mNativeWindow, meta); + } + } + + mOutputFormat = msg; + mFlags |= kFlagOutputFormatChanged; + break; + } + + case ACodec::kWhatFillThisBuffer: + { + /* size_t index = */updateBuffers(kPortIndexInput, msg); + + if (mState == FLUSHING) { + returnBuffersToCodecOnPort(kPortIndexInput); + break; + } + + if (mFlags & kFlagDequeueInputPending) { + CHECK(handleDequeueInputBuffer(mDequeueInputReplyID)); + + ++mDequeueInputTimeoutGeneration; + mFlags &= ~kFlagDequeueInputPending; + mDequeueInputReplyID = 0; + } + break; + } + + case ACodec::kWhatDrainThisBuffer: + { + /* size_t index = */updateBuffers(kPortIndexOutput, msg); + + if (mState == FLUSHING) { + returnBuffersToCodecOnPort(kPortIndexOutput); + break; + } + + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + int32_t omxFlags; + CHECK(msg->findInt32("flags", &omxFlags)); + + buffer->meta()->setInt32("omxFlags", omxFlags); + + if (mFlags & kFlagDequeueOutputPending) { + CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID)); + + ++mDequeueOutputTimeoutGeneration; + mFlags &= ~kFlagDequeueOutputPending; + mDequeueOutputReplyID = 0; + } + break; + } + + case ACodec::kWhatEOS: + { + // We already notify the client of this by using the + // corresponding flag in "onOutputBufferReady". + break; + } + + case ACodec::kWhatShutdownCompleted: + { + CHECK_EQ(mState, STOPPING); + setState(UNINITIALIZED); + + (new AMessage)->postReply(mReplyID); + break; + } + + case ACodec::kWhatFlushCompleted: + { + CHECK_EQ(mState, FLUSHING); + setState(STARTED); + + mCodec->signalResume(); + + (new AMessage)->postReply(mReplyID); + break; + } + + default: + TRESPASS(); + } + break; + } + + case kWhatInit: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != UNINITIALIZED) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + setState(INITIALIZING); + + AString name; + CHECK(msg->findString("name", &name)); + + int32_t nameIsType; + int32_t encoder = false; + if (!msg->findInt32("nameIsType", &nameIsType)) { + nameIsType = false; + } else { + CHECK(msg->findInt32("encoder", &encoder)); + } + + sp<AMessage> format = new AMessage; + + if (nameIsType) { + format->setString("mime", name.c_str()); + format->setInt32("encoder", encoder); + } else { + format->setString("componentName", name.c_str()); + } + + mCodec->initiateAllocateComponent(format); + break; + } + + case kWhatConfigure: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != INITIALIZED) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + setState(CONFIGURING); + + sp<RefBase> obj; + if (!msg->findObject("native-window", &obj)) { + obj.clear(); + } + + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); + + if (obj != NULL) { + format->setObject("native-window", obj); + } + + uint32_t flags; + CHECK(msg->findInt32("flags", (int32_t *)&flags)); + + if (flags & CONFIGURE_FLAG_ENCODE) { + format->setInt32("encoder", true); + } + + mCodec->initiateConfigureComponent(format); + break; + } + + case kWhatStart: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != CONFIGURED) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + setState(STARTING); + + mCodec->initiateStart(); + break; + } + + case kWhatStop: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != INITIALIZED + && mState != CONFIGURED && mState != STARTED) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + setState(STOPPING); + + mCodec->initiateShutdown(); + returnBuffersToCodec(); + break; + } + + case kWhatDequeueInputBuffer: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (handleDequeueInputBuffer(replyID, true /* new request */)) { + break; + } + + int64_t timeoutUs; + CHECK(msg->findInt64("timeoutUs", &timeoutUs)); + + if (timeoutUs == 0ll) { + sp<AMessage> response = new AMessage; + response->setInt32("err", -EAGAIN); + response->postReply(replyID); + break; + } + + mFlags |= kFlagDequeueInputPending; + mDequeueInputReplyID = replyID; + + if (timeoutUs > 0ll) { + sp<AMessage> timeoutMsg = + new AMessage(kWhatDequeueInputTimedOut, id()); + timeoutMsg->setInt32( + "generation", ++mDequeueInputTimeoutGeneration); + timeoutMsg->post(timeoutUs); + } + break; + } + + case kWhatDequeueInputTimedOut: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mDequeueInputTimeoutGeneration) { + // Obsolete + break; + } + + CHECK(mFlags & kFlagDequeueInputPending); + + sp<AMessage> response = new AMessage; + response->setInt32("err", -EAGAIN); + response->postReply(mDequeueInputReplyID); + + mFlags &= ~kFlagDequeueInputPending; + mDequeueInputReplyID = 0; + break; + } + + case kWhatQueueInputBuffer: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != STARTED || (mFlags & kFlagStickyError)) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + status_t err = onQueueInputBuffer(msg); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatDequeueOutputBuffer: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (handleDequeueOutputBuffer(replyID, true /* new request */)) { + break; + } + + int64_t timeoutUs; + CHECK(msg->findInt64("timeoutUs", &timeoutUs)); + + if (timeoutUs == 0ll) { + sp<AMessage> response = new AMessage; + response->setInt32("err", -EAGAIN); + response->postReply(replyID); + break; + } + + mFlags |= kFlagDequeueOutputPending; + mDequeueOutputReplyID = replyID; + + if (timeoutUs > 0ll) { + sp<AMessage> timeoutMsg = + new AMessage(kWhatDequeueOutputTimedOut, id()); + timeoutMsg->setInt32( + "generation", ++mDequeueOutputTimeoutGeneration); + timeoutMsg->post(timeoutUs); + } + break; + } + + case kWhatDequeueOutputTimedOut: + { + int32_t generation; + CHECK(msg->findInt32("generation", &generation)); + + if (generation != mDequeueOutputTimeoutGeneration) { + // Obsolete + break; + } + + CHECK(mFlags & kFlagDequeueOutputPending); + + sp<AMessage> response = new AMessage; + response->setInt32("err", -EAGAIN); + response->postReply(mDequeueOutputReplyID); + + mFlags &= ~kFlagDequeueOutputPending; + mDequeueOutputReplyID = 0; + break; + } + + case kWhatReleaseOutputBuffer: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != STARTED || (mFlags & kFlagStickyError)) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + status_t err = onReleaseOutputBuffer(msg); + + sp<AMessage> response = new AMessage; + response->setInt32("err", err); + response->postReply(replyID); + break; + } + + case kWhatGetBuffers: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != STARTED || (mFlags & kFlagStickyError)) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + int32_t portIndex; + CHECK(msg->findInt32("portIndex", &portIndex)); + + Vector<sp<ABuffer> > *dstBuffers; + CHECK(msg->findPointer("buffers", (void **)&dstBuffers)); + + dstBuffers->clear(); + const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex]; + + for (size_t i = 0; i < srcBuffers.size(); ++i) { + const BufferInfo &info = srcBuffers.itemAt(i); + + dstBuffers->push_back(info.mData); + } + + (new AMessage)->postReply(replyID); + break; + } + + case kWhatFlush: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if (mState != STARTED || (mFlags & kFlagStickyError)) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + mReplyID = replyID; + setState(FLUSHING); + + mCodec->signalFlush(); + returnBuffersToCodec(); + break; + } + + case kWhatGetOutputFormat: + { + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); + + if ((mState != STARTED && mState != FLUSHING) + || (mFlags & kFlagStickyError)) { + sp<AMessage> response = new AMessage; + response->setInt32("err", INVALID_OPERATION); + + response->postReply(replyID); + break; + } + + sp<AMessage> response = new AMessage; + response->setMessage("format", mOutputFormat); + response->postReply(replyID); + break; + } + + default: + TRESPASS(); + } +} + +void MediaCodec::setState(State newState) { + if (newState == UNINITIALIZED) { + delete mSoftRenderer; + mSoftRenderer = NULL; + + mNativeWindow.clear(); + + mOutputFormat.clear(); + mFlags &= ~kFlagOutputFormatChanged; + mFlags &= ~kFlagOutputBuffersChanged; + mFlags &= ~kFlagStickyError; + } + + mState = newState; + + cancelPendingDequeueOperations(); +} + +void MediaCodec::returnBuffersToCodec() { + returnBuffersToCodecOnPort(kPortIndexInput); + returnBuffersToCodecOnPort(kPortIndexOutput); +} + +void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) { + CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput); + + Vector<BufferInfo> *buffers = &mPortBuffers[portIndex]; + + for (size_t i = 0; i < buffers->size(); ++i) { + BufferInfo *info = &buffers->editItemAt(i); + + if (info->mNotify != NULL) { + sp<AMessage> msg = info->mNotify; + info->mNotify = NULL; + info->mOwnedByClient = false; + + if (portIndex == kPortIndexInput) { + msg->setInt32("err", ERROR_END_OF_STREAM); + } + msg->post(); + } + } + + mAvailPortBuffers[portIndex].clear(); +} + +size_t MediaCodec::updateBuffers( + int32_t portIndex, const sp<AMessage> &msg) { + CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput); + + void *bufferID; + CHECK(msg->findPointer("buffer-id", &bufferID)); + + Vector<BufferInfo> *buffers = &mPortBuffers[portIndex]; + + for (size_t i = 0; i < buffers->size(); ++i) { + BufferInfo *info = &buffers->editItemAt(i); + + if (info->mBufferID == bufferID) { + CHECK(info->mNotify == NULL); + CHECK(msg->findMessage("reply", &info->mNotify)); + + mAvailPortBuffers[portIndex].push_back(i); + + return i; + } + } + + TRESPASS(); + + return 0; +} + +status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) { + size_t index; + size_t offset; + size_t size; + int64_t timeUs; + uint32_t flags; + CHECK(msg->findSize("index", &index)); + CHECK(msg->findSize("offset", &offset)); + CHECK(msg->findSize("size", &size)); + CHECK(msg->findInt64("timeUs", &timeUs)); + CHECK(msg->findInt32("flags", (int32_t *)&flags)); + + if (index >= mPortBuffers[kPortIndexInput].size()) { + return -ERANGE; + } + + BufferInfo *info = &mPortBuffers[kPortIndexInput].editItemAt(index); + + if (info->mNotify == NULL || !info->mOwnedByClient) { + return -EACCES; + } + + if (offset + size > info->mData->capacity()) { + return -EINVAL; + } + + sp<AMessage> reply = info->mNotify; + info->mNotify = NULL; + info->mOwnedByClient = false; + + info->mData->setRange(offset, size); + info->mData->meta()->setInt64("timeUs", timeUs); + + if (flags & BUFFER_FLAG_EOS) { + info->mData->meta()->setInt32("eos", true); + } + + if (flags & BUFFER_FLAG_CODECCONFIG) { + info->mData->meta()->setInt32("csd", true); + } + + reply->setBuffer("buffer", info->mData); + reply->post(); + + return OK; +} + +status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) { + size_t index; + CHECK(msg->findSize("index", &index)); + + int32_t render; + if (!msg->findInt32("render", &render)) { + render = 0; + } + + if (mState != STARTED) { + return -EINVAL; + } + + if (index >= mPortBuffers[kPortIndexOutput].size()) { + return -ERANGE; + } + + BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index); + + if (info->mNotify == NULL || !info->mOwnedByClient) { + return -EACCES; + } + + if (render) { + info->mNotify->setInt32("render", true); + + if (mSoftRenderer != NULL) { + mSoftRenderer->render( + info->mData->data(), info->mData->size(), NULL); + } + } + + info->mNotify->post(); + info->mNotify = NULL; + info->mOwnedByClient = false; + + return OK; +} + +ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) { + CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput); + + List<size_t> *availBuffers = &mAvailPortBuffers[portIndex]; + + if (availBuffers->empty()) { + return -EAGAIN; + } + + size_t index = *availBuffers->begin(); + availBuffers->erase(availBuffers->begin()); + + BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index); + CHECK(!info->mOwnedByClient); + info->mOwnedByClient = true; + + return index; +} + +} // namespace android diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp new file mode 100644 index 000000000000..afd476356047 --- /dev/null +++ b/media/libstagefright/NuMediaExtractor.cpp @@ -0,0 +1,433 @@ +/* + * Copyright 2012, 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_NDEBUG 0 +#define LOG_TAG "NuMediaExtractor" +#include <utils/Log.h> + +#include <media/stagefright/NuMediaExtractor.h> + +#include "include/ESDS.h" + +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/DataSource.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaExtractor.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +namespace android { + +NuMediaExtractor::NuMediaExtractor() { +} + +NuMediaExtractor::~NuMediaExtractor() { + releaseTrackSamples(); + + for (size_t i = 0; i < mSelectedTracks.size(); ++i) { + TrackInfo *info = &mSelectedTracks.editItemAt(i); + + CHECK_EQ((status_t)OK, info->mSource->stop()); + } + + mSelectedTracks.clear(); +} + +status_t NuMediaExtractor::setDataSource(const char *path) { + sp<DataSource> dataSource = DataSource::CreateFromURI(path); + + if (dataSource == NULL) { + return -ENOENT; + } + + mImpl = MediaExtractor::Create(dataSource); + + if (mImpl == NULL) { + return ERROR_UNSUPPORTED; + } + + return OK; +} + +size_t NuMediaExtractor::countTracks() const { + return mImpl == NULL ? 0 : mImpl->countTracks(); +} + +status_t NuMediaExtractor::getTrackFormat( + size_t index, sp<AMessage> *format) const { + *format = NULL; + + if (mImpl == NULL) { + return -EINVAL; + } + + if (index >= mImpl->countTracks()) { + return -ERANGE; + } + + sp<MetaData> meta = mImpl->getTrackMetaData(index); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + sp<AMessage> msg = new AMessage; + msg->setString("mime", mime); + + if (!strncasecmp("video/", mime, 6)) { + int32_t width, height; + CHECK(meta->findInt32(kKeyWidth, &width)); + CHECK(meta->findInt32(kKeyHeight, &height)); + + msg->setInt32("width", width); + msg->setInt32("height", height); + } else { + CHECK(!strncasecmp("audio/", mime, 6)); + + int32_t numChannels, sampleRate; + CHECK(meta->findInt32(kKeyChannelCount, &numChannels)); + CHECK(meta->findInt32(kKeySampleRate, &sampleRate)); + + msg->setInt32("channel-count", numChannels); + msg->setInt32("sample-rate", sampleRate); + } + + int32_t maxInputSize; + if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) { + msg->setInt32("max-input-size", maxInputSize); + } + + uint32_t type; + const void *data; + size_t size; + 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; + + sp<ABuffer> buffer = new ABuffer(1024); + buffer->setRange(0, 0); + + for (size_t i = 0; i < numSeqParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); + memcpy(buffer->data() + buffer->size() + 4, ptr, length); + buffer->setRange(0, buffer->size() + 4 + length); + + ptr += length; + size -= length; + } + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + + msg->setBuffer("csd-0", buffer); + + buffer = new ABuffer(1024); + buffer->setRange(0, 0); + + 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); + + memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4); + memcpy(buffer->data() + buffer->size() + 4, ptr, length); + buffer->setRange(0, buffer->size() + 4 + length); + + ptr += length; + size -= length; + } + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-1", buffer); + } else if (meta->findData(kKeyESDS, &type, &data, &size)) { + ESDS esds((const char *)data, size); + CHECK_EQ(esds.InitCheck(), (status_t)OK); + + const void *codec_specific_data; + size_t codec_specific_data_size; + esds.getCodecSpecificInfo( + &codec_specific_data, &codec_specific_data_size); + + sp<ABuffer> buffer = new ABuffer(codec_specific_data_size); + + memcpy(buffer->data(), codec_specific_data, + codec_specific_data_size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-0", buffer); + } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) { + sp<ABuffer> buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-0", buffer); + + if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) { + return -EINVAL; + } + + buffer = new ABuffer(size); + memcpy(buffer->data(), data, size); + + buffer->meta()->setInt32("csd", true); + buffer->meta()->setInt64("timeUs", 0); + msg->setBuffer("csd-1", buffer); + } + + *format = msg; + + return OK; +} + +status_t NuMediaExtractor::selectTrack(size_t index) { + if (mImpl == NULL) { + return -EINVAL; + } + + if (index >= mImpl->countTracks()) { + return -ERANGE; + } + + for (size_t i = 0; i < mSelectedTracks.size(); ++i) { + TrackInfo *info = &mSelectedTracks.editItemAt(i); + + if (info->mTrackIndex == index) { + // This track has already been selected. + return OK; + } + } + + sp<MediaSource> source = mImpl->getTrack(index); + + CHECK_EQ((status_t)OK, source->start()); + + mSelectedTracks.push(); + TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1); + + info->mSource = source; + info->mTrackIndex = index; + info->mFinalResult = OK; + info->mSample = NULL; + info->mSampleTimeUs = -1ll; + info->mFlags = 0; + + const char *mime; + CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) { + info->mFlags |= kIsVorbis; + } + + return OK; +} + +void NuMediaExtractor::releaseTrackSamples() { + for (size_t i = 0; i < mSelectedTracks.size(); ++i) { + TrackInfo *info = &mSelectedTracks.editItemAt(i); + + if (info->mSample != NULL) { + info->mSample->release(); + info->mSample = NULL; + + info->mSampleTimeUs = -1ll; + } + } +} + +ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) { + TrackInfo *minInfo = NULL; + ssize_t minIndex = -1; + + for (size_t i = 0; i < mSelectedTracks.size(); ++i) { + TrackInfo *info = &mSelectedTracks.editItemAt(i); + + if (seekTimeUs >= 0ll) { + info->mFinalResult = OK; + + if (info->mSample != NULL) { + info->mSample->release(); + info->mSample = NULL; + info->mSampleTimeUs = -1ll; + } + } else if (info->mFinalResult != OK) { + continue; + } + + if (info->mSample == NULL) { + MediaSource::ReadOptions options; + if (seekTimeUs >= 0ll) { + options.setSeekTo(seekTimeUs); + } + status_t err = info->mSource->read(&info->mSample, &options); + + if (err != OK) { + CHECK(info->mSample == NULL); + + info->mFinalResult = err; + info->mSampleTimeUs = -1ll; + continue; + } else { + CHECK(info->mSample != NULL); + CHECK(info->mSample->meta_data()->findInt64( + kKeyTime, &info->mSampleTimeUs)); + } + } + + if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) { + minInfo = info; + minIndex = i; + } + } + + return minIndex; +} + +status_t NuMediaExtractor::seekTo(int64_t timeUs) { + return fetchTrackSamples(timeUs); +} + +status_t NuMediaExtractor::advance() { + ssize_t minIndex = fetchTrackSamples(); + + if (minIndex < 0) { + return ERROR_END_OF_STREAM; + } + + TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); + + info->mSample->release(); + info->mSample = NULL; + info->mSampleTimeUs = -1ll; + + return OK; +} + +status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) { + ssize_t minIndex = fetchTrackSamples(); + + if (minIndex < 0) { + return ERROR_END_OF_STREAM; + } + + TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); + + size_t sampleSize = info->mSample->range_length(); + + if (info->mFlags & kIsVorbis) { + // Each sample's data is suffixed by the number of page samples + // or -1 if not available. + sampleSize += sizeof(int32_t); + } + + if (buffer->capacity() < sampleSize) { + return -ENOMEM; + } + + const uint8_t *src = + (const uint8_t *)info->mSample->data() + + info->mSample->range_offset(); + + memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length()); + + if (info->mFlags & kIsVorbis) { + int32_t numPageSamples; + if (!info->mSample->meta_data()->findInt32( + kKeyValidSamples, &numPageSamples)) { + numPageSamples = -1; + } + + memcpy((uint8_t *)buffer->data() + info->mSample->range_length(), + &numPageSamples, + sizeof(numPageSamples)); + } + + buffer->setRange(0, sampleSize); + + return OK; +} + +status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) { + ssize_t minIndex = fetchTrackSamples(); + + if (minIndex < 0) { + return ERROR_END_OF_STREAM; + } + + TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); + *trackIndex = info->mTrackIndex; + + return OK; +} + +status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) { + ssize_t minIndex = fetchTrackSamples(); + + if (minIndex < 0) { + return ERROR_END_OF_STREAM; + } + + TrackInfo *info = &mSelectedTracks.editItemAt(minIndex); + *sampleTimeUs = info->mSampleTimeUs; + + return OK; +} + +} // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index 470f7502be66..1325462714bc 100755 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1541,6 +1541,8 @@ void OMXCodec::setComponentRole( "video_decoder.mpeg4", "video_encoder.mpeg4" }, { MEDIA_MIMETYPE_VIDEO_H263, "video_decoder.h263", "video_encoder.h263" }, + { MEDIA_MIMETYPE_VIDEO_VPX, + "video_decoder.vpx", "video_encoder.vpx" }, }; static const size_t kNumMimeToRole = @@ -3556,6 +3558,7 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t //////////////// output port //////////////////// // format OMX_AUDIO_PARAM_PORTFORMATTYPE format; + InitOMXParams(&format); format.nPortIndex = kPortIndexOutput; format.nIndex = 0; status_t err = OMX_ErrorNone; diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp index 0a6776ef5130..9a001865e50b 100644 --- a/media/libstagefright/foundation/AMessage.cpp +++ b/media/libstagefright/foundation/AMessage.cpp @@ -19,6 +19,7 @@ #include <ctype.h> #include "AAtomizer.h" +#include "ABuffer.h" #include "ADebug.h" #include "ALooperRoster.h" #include "AString.h" @@ -157,14 +158,23 @@ void AMessage::setString( item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len); } -void AMessage::setObject(const char *name, const sp<RefBase> &obj) { +void AMessage::setObjectInternal( + const char *name, const sp<RefBase> &obj, Type type) { Item *item = allocateItem(name); - item->mType = kTypeObject; + item->mType = type; if (obj != NULL) { obj->incStrong(this); } item->u.refValue = obj.get(); } +void AMessage::setObject(const char *name, const sp<RefBase> &obj) { + setObjectInternal(name, obj, kTypeObject); +} + +void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) { + setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer); +} + void AMessage::setMessage(const char *name, const sp<AMessage> &obj) { Item *item = allocateItem(name); item->mType = kTypeMessage; @@ -203,6 +213,15 @@ bool AMessage::findObject(const char *name, sp<RefBase> *obj) const { return false; } +bool AMessage::findBuffer(const char *name, sp<ABuffer> *buf) const { + const Item *item = findItem(name, kTypeBuffer); + if (item) { + *buf = (ABuffer *)(item->u.refValue); + return true; + } + return false; +} + bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const { const Item *item = findItem(name, kTypeMessage); if (item) { @@ -542,4 +561,20 @@ void AMessage::writeToParcel(Parcel *parcel) const { } } +size_t AMessage::countEntries() const { + return mNumItems; +} + +const char *AMessage::getEntryNameAt(size_t index, Type *type) const { + if (index >= mNumItems) { + *type = kTypeInt32; + + return NULL; + } + + *type = mItems[index].mType; + + return mItems[index].mName; +} + } // namespace android diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp index 9d72b1fd9a85..fb8abc580c17 100644 --- a/media/libstagefright/rtsp/AAMRAssembler.cpp +++ b/media/libstagefright/rtsp/AAMRAssembler.cpp @@ -211,7 +211,7 @@ ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket( } sp<AMessage> msg = mNotifyMsg->dup(); - msg->setObject("access-unit", accessUnit); + msg->setBuffer("access-unit", accessUnit); msg->post(); queue->erase(queue->begin()); diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp index ed8b1df2dda8..7ea132e54cf4 100644 --- a/media/libstagefright/rtsp/AAVCAssembler.cpp +++ b/media/libstagefright/rtsp/AAVCAssembler.cpp @@ -345,7 +345,7 @@ void AAVCAssembler::submitAccessUnit() { mAccessUnitDamaged = false; sp<AMessage> msg = mNotifyMsg->dup(); - msg->setObject("access-unit", accessUnit); + msg->setBuffer("access-unit", accessUnit); msg->post(); } diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp index 498295c46e1b..ded70fa17439 100644 --- a/media/libstagefright/rtsp/AH263Assembler.cpp +++ b/media/libstagefright/rtsp/AH263Assembler.cpp @@ -166,7 +166,7 @@ void AH263Assembler::submitAccessUnit() { mAccessUnitDamaged = false; sp<AMessage> msg = mNotifyMsg->dup(); - msg->setObject("access-unit", accessUnit); + msg->setBuffer("access-unit", accessUnit); msg->post(); } diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp index b0c7007c3737..24c2f30209ef 100644 --- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp @@ -571,7 +571,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() { mAccessUnitDamaged = false; sp<AMessage> msg = mNotifyMsg->dup(); - msg->setObject("access-unit", accessUnit); + msg->setBuffer("access-unit", accessUnit); msg->post(); } diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp index 2f2e2c26e395..687d72bbda56 100644 --- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp +++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp @@ -368,7 +368,7 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() { mAccessUnitDamaged = false; sp<AMessage> msg = mNotifyMsg->dup(); - msg->setObject("access-unit", accessUnit); + msg->setBuffer("access-unit", accessUnit); msg->post(); } diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp index 8c9dd8d3d5ce..44988a34733f 100644 --- a/media/libstagefright/rtsp/ARTPConnection.cpp +++ b/media/libstagefright/rtsp/ARTPConnection.cpp @@ -639,7 +639,7 @@ sp<ARTPSource> ARTPConnection::findSource(StreamInfo *info, uint32_t srcId) { void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) { sp<AMessage> msg = new AMessage(kWhatInjectPacket, id()); msg->setInt32("index", index); - msg->setObject("buffer", buffer); + msg->setBuffer("buffer", buffer); msg->post(); } @@ -647,10 +647,8 @@ void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) { int32_t index; CHECK(msg->findInt32("index", &index)); - sp<RefBase> obj; - CHECK(msg->findObject("buffer", &obj)); - - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); List<StreamInfo>::iterator it = mStreams.begin(); while (it != mStreams.end() diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp index 7a05b888f630..ba4e33c813db 100644 --- a/media/libstagefright/rtsp/ARTPSession.cpp +++ b/media/libstagefright/rtsp/ARTPSession.cpp @@ -145,10 +145,8 @@ void ARTPSession::onMessageReceived(const sp<AMessage> &msg) { break; } - sp<RefBase> obj; - CHECK(msg->findObject("access-unit", &obj)); - - sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> accessUnit; + CHECK(msg->findBuffer("access-unit", &accessUnit)); uint64_t ntpTime; CHECK(accessUnit->meta()->findInt64( diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp index 80a010e2f466..539a88823cab 100644 --- a/media/libstagefright/rtsp/ARTSPConnection.cpp +++ b/media/libstagefright/rtsp/ARTSPConnection.cpp @@ -612,7 +612,7 @@ bool ARTSPConnection::receiveRTSPReponse() { if (mObserveBinaryMessage != NULL) { sp<AMessage> notify = mObserveBinaryMessage->dup(); - notify->setObject("buffer", buffer); + notify->setBuffer("buffer", buffer); notify->post(); } else { ALOGW("received binary data, but no one cares."); diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp index 98bee82e23a0..0da5dd250e9b 100644 --- a/media/libstagefright/rtsp/ARawAudioAssembler.cpp +++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp @@ -94,7 +94,7 @@ ARTPAssembler::AssemblyStatus ARawAudioAssembler::addPacket( } sp<AMessage> msg = mNotifyMsg->dup(); - msg->setObject("access-unit", buffer); + msg->setBuffer("access-unit", buffer); msg->post(); queue->erase(queue->begin()); diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h index 9a7dd704807a..deee30f14600 100644 --- a/media/libstagefright/rtsp/MyHandler.h +++ b/media/libstagefright/rtsp/MyHandler.h @@ -857,10 +857,8 @@ struct MyHandler : public AHandler { return; } - sp<RefBase> obj; - CHECK(msg->findObject("access-unit", &obj)); - - sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> accessUnit; + CHECK(msg->findBuffer("access-unit", &accessUnit)); uint32_t seqNum = (uint32_t)accessUnit->int32Data(); @@ -1005,9 +1003,8 @@ struct MyHandler : public AHandler { case 'biny': { - sp<RefBase> obj; - CHECK(msg->findObject("buffer", &obj)); - sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); int32_t index; CHECK(buffer->meta()->findInt32("index", &index)); @@ -1488,7 +1485,7 @@ private: sp<AMessage> msg = mNotify->dup(); msg->setInt32("what", kWhatAccessUnit); msg->setSize("trackIndex", trackIndex); - msg->setObject("accessUnit", accessUnit); + msg->setBuffer("accessUnit", accessUnit); msg->post(); } diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java index 6f1959cce8b6..d15a53570c18 100644 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java @@ -196,7 +196,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_001 @LargeTest public void testPerformanceAddRemoveVideoItem() throws Exception { final String videoItemFileName = INPUT_FILE_PATH + @@ -241,7 +240,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_002 @LargeTest public void testPerformanceAddRemoveImageItem() throws Exception { final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg"; @@ -280,7 +278,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_003 @LargeTest public void testPerformanceAddRemoveTransition() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -360,7 +357,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_004 @LargeTest public void testPerformanceExport() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -541,7 +537,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_005 @LargeTest public void testPerformanceThumbnailVideoItem() throws Exception { final String videoItemFileName = INPUT_FILE_PATH @@ -574,7 +569,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_006 @LargeTest public void testPerformanceOverlayVideoItem() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -629,7 +623,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_007 @LargeTest public void testPerformanceVideoItemProperties() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -688,7 +681,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_008 @LargeTest public void testPerformanceGeneratePreviewWithTransitions() throws Exception { @@ -740,7 +732,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_009 @LargeTest public void testPerformanceWithKenBurn() throws Exception { final String videoItemFileName = INPUT_FILE_PATH + @@ -795,7 +786,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_010 @LargeTest public void testPerformanceEffectOverlappingTransition() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -864,7 +854,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_011 @LargeTest public void testPerformanceTransitionWithEffectOverlapping() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -994,7 +983,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_014 @LargeTest public void testPerformanceWithAudioTrack() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -1049,7 +1037,6 @@ public class VideoEditorPerformance extends * * @throws Exception */ - // TODO : remove PRF_015 @LargeTest public void testPerformanceAddRemoveImageItem640x480() throws Exception { final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java index 4d3078473e2f..7784c7b6ad09 100755 --- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java +++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java @@ -167,7 +167,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_001 @LargeTest public void testStressAddRemoveVideoItem() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -241,7 +240,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_002 @LargeTest public void testStressAddRemoveImageItem() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -310,7 +308,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_003 @LargeTest public void testStressAddRemoveTransition() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -428,7 +425,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_004 @LargeTest public void testStressAddRemoveOverlay() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -493,7 +489,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_005 @LargeTest public void testStressAddRemoveEffects() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -590,7 +585,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_006 @LargeTest public void testStressThumbnailVideoItem() throws Exception { final String videoItemFileName = INPUT_FILE_PATH @@ -651,7 +645,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_007 @LargeTest public void testStressMediaProperties() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -747,7 +740,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_008 @LargeTest public void testStressInsertMovieItems() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -759,7 +751,7 @@ public class VideoEditorStressTest "MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp"; final String[] loggingInfo = new String[1]; int i = 0; - writeTestCaseHeader("testStressInsertMoveItems"); + writeTestCaseHeader("testStressInsertMovieItems"); final MediaVideoItem mediaItem1 = new MediaVideoItem(mVideoEditor, "m1", VideoItemFileName1, renderingMode); @@ -801,7 +793,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_009 @LargeTest public void testStressLoadAndSave() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -916,7 +907,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_010 @LargeTest public void testStressMultipleExport() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -1007,7 +997,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_011 @LargeTest public void testStressOverlayTransKenBurn() throws Exception { final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER; @@ -1094,7 +1083,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_012 @LargeTest public void testStressAudioTrackVideo() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -1147,7 +1135,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_013 @LargeTest public void testStressStoryBoard() throws Exception { final String videoItemFileName1 = INPUT_FILE_PATH + @@ -1237,7 +1224,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_014 @LargeTest public void testStressAudioTrackOnly() throws Exception { @@ -1267,7 +1253,6 @@ public class VideoEditorStressTest * * @throws Exception */ - // TODO : remove TC_STR_016 -- New Test Case @LargeTest public void testStressThumbnailImageItem() throws Exception { final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg"; diff --git a/native/android/Android.mk b/native/android/Android.mk index 9940442fed45..e2c99ee72b62 100644 --- a/native/android/Android.mk +++ b/native/android/Android.mk @@ -18,6 +18,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libcutils \ + libandroidfw \ libutils \ libbinder \ libui \ diff --git a/native/android/asset_manager.cpp b/native/android/asset_manager.cpp index f5db57c1ff85..01db1d3cace4 100644 --- a/native/android/asset_manager.cpp +++ b/native/android/asset_manager.cpp @@ -18,9 +18,9 @@ #include <utils/Log.h> #include <android/asset_manager_jni.h> -#include <utils/AssetManager.h> -#include <utils/AssetDir.h> -#include <utils/Asset.h> +#include <androidfw/Asset.h> +#include <androidfw/AssetDir.h> +#include <androidfw/AssetManager.h> #include <utils/threads.h> #include "jni.h" diff --git a/native/android/configuration.cpp b/native/android/configuration.cpp index 687924be72ee..7eb51ddacc37 100644 --- a/native/android/configuration.cpp +++ b/native/android/configuration.cpp @@ -17,7 +17,7 @@ #define LOG_TAG "Configuration" #include <utils/Log.h> -#include <utils/AssetManager.h> +#include <androidfw/AssetManager.h> #include <android_runtime/android_content_res_Configuration.h> diff --git a/native/android/input.cpp b/native/android/input.cpp index 91671c3e9d54..6eb29902fd82 100644 --- a/native/android/input.cpp +++ b/native/android/input.cpp @@ -18,8 +18,8 @@ #include <utils/Log.h> #include <android/input.h> -#include <ui/Input.h> -#include <ui/InputTransport.h> +#include <androidfw/Input.h> +#include <androidfw/InputTransport.h> #include <utils/Looper.h> #include <utils/RefBase.h> #include <utils/Vector.h> diff --git a/native/android/obb.cpp b/native/android/obb.cpp index e0cb1a6a9aa6..e9900249b289 100644 --- a/native/android/obb.cpp +++ b/native/android/obb.cpp @@ -18,8 +18,8 @@ #include <android/obb.h> +#include <androidfw/ObbFile.h> #include <utils/Log.h> -#include <utils/ObbFile.h> using namespace android; diff --git a/opengl/java/android/opengl/Group.java b/opengl/java/android/opengl/Group.java deleted file mode 100644 index 1ef29539d2c6..000000000000 --- a/opengl/java/android/opengl/Group.java +++ /dev/null @@ -1,155 +0,0 @@ -/* - * Copyright (C) 2006 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 java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.ShortBuffer; -import java.io.DataInputStream; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Iterator; -import javax.microedition.khronos.opengles.*; - -class MaterialIndices { - - private Material material = null; - private ShortBuffer indexBuffer = null; - - public MaterialIndices(Material material, ShortBuffer indexBuffer) { - this.material = material; - this.indexBuffer = indexBuffer; - } - - public Material getMaterial() { - return material; - } - - public ShortBuffer getIndexBuffer() { - return indexBuffer; - } -} - -/** - * {@hide} - */ -public class Group { - - private Object3D parent; - private String name; - - private List<MaterialIndices> materialIndices = - new ArrayList<MaterialIndices>(); - - public Group(Object3D parent) { - this.parent = parent; - } - - public String getName() { - return name; - } - - public void load(DataInputStream dis) throws IOException { - dis.readInt(); // name length - this.name = dis.readUTF(); - - int numMaterials = dis.readInt(); - - for (int i = 0; i < numMaterials; i++) { - dis.readInt(); // material name length - String matName = dis.readUTF(); - Material material = parent.getMaterial(matName); - - int numIndices = dis.readInt(); - byte[] indicesBytes = new byte[numIndices * 2]; - dis.readFully(indicesBytes); - - // Swap bytes from network to native order if necessary - if (ByteOrder.nativeOrder() == ByteOrder.LITTLE_ENDIAN) { - int idx = 0; - for (int j = 0; j < numIndices; j++) { - byte b0 = indicesBytes[idx]; - byte b1 = indicesBytes[idx + 1]; - indicesBytes[idx] = b1; - indicesBytes[idx + 1] = b0; - idx += 2; - } - } - - ByteBuffer ibb = ByteBuffer.allocateDirect(2*numIndices); - ibb.order(ByteOrder.nativeOrder()); - ibb.put(indicesBytes); - ibb.position(0); - - ShortBuffer sb = ibb.asShortBuffer(); - materialIndices.add(new MaterialIndices(material, sb)); - } - } - - public int getNumTriangles() { - int numTriangles = 0; - Iterator<MaterialIndices> iter = materialIndices.iterator(); - while (iter.hasNext()) { - MaterialIndices matIdx = iter.next(); - ShortBuffer indexBuffer = matIdx.getIndexBuffer(); - numTriangles += indexBuffer.capacity()/3; - } - return numTriangles; - } - - public void draw(GL10 gl) { - gl.glDisableClientState(gl.GL_COLOR_ARRAY); - - gl.glVertexPointer(3, gl.GL_FIXED, 0, parent.getVertexBuffer()); - gl.glEnableClientState(gl.GL_VERTEX_ARRAY); - - gl.glNormalPointer(gl.GL_FIXED, 0, parent.getNormalBuffer()); - gl.glEnableClientState(gl.GL_NORMAL_ARRAY); - - if (parent.hasTexcoords()) { - gl.glTexCoordPointer(2, gl.GL_FIXED, 0, parent.getTexcoordBuffer()); - gl.glEnableClientState(gl.GL_TEXTURE_COORD_ARRAY); - gl.glEnable(gl.GL_TEXTURE_2D); - } else { - gl.glDisable(gl.GL_TEXTURE_2D); - } - - Iterator<MaterialIndices> iter = materialIndices.iterator(); - while (iter.hasNext()) { - MaterialIndices matIdx = iter.next(); - ShortBuffer indexBuffer = matIdx.getIndexBuffer(); - Material mat = matIdx.getMaterial(); - mat.setMaterialParameters(gl); - if (parent.hasTexcoords() && mat.getMap_Kd().length() > 0) { - Texture texture = parent.getTexture(mat.getMap_Kd()); - texture.setTextureParameters(gl); - } - - gl.glDrawElements(gl.GL_TRIANGLES, - indexBuffer.capacity(), - gl.GL_UNSIGNED_SHORT, - indexBuffer); - } - } - - public String toString() { - return "Group[" + - "name=" + name + - "]"; - } -} diff --git a/opengl/java/android/opengl/Material.java b/opengl/java/android/opengl/Material.java deleted file mode 100644 index 60a3e7256eac..000000000000 --- a/opengl/java/android/opengl/Material.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright (C) 2006 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 java.io.DataInputStream; -import java.io.IOException; -import javax.microedition.khronos.opengles.GL10; - -/** - * {@hide} - */ -public class Material { - - private Object3D parent; - private String name; - private String map_kd; - private float[] ka = new float[4]; - private float[] kd = new float[4]; - private float[] ks = new float[4]; - private float ns; - private int illum; - private float d; - - private static float[] black = { 0.0f, 0.0f, 0.0f, 1.0f }; - - public Material(Object3D parent) { - this.parent = parent; - } - - public String getName() { - return name; - } - - public String getMap_Kd() { - return map_kd; - } - - public void setMaterialParameters(GL10 gl) { - gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, kd, 0); - gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_DIFFUSE, kd, 0); - gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_SPECULAR, ks, 0); - gl.glMaterialf(gl.GL_FRONT_AND_BACK, gl.GL_SHININESS, - Math.min(Math.max(ns, 0), 128)); - -// if (illum == 0) { -// gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, kd, 0); -// } else { -// gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_AMBIENT, ka, 0); -// gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_DIFFUSE, kd, 0); -// } - -// if (illum > 1) { -// gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_SPECULAR, ks, 0); -// gl.glMaterialf(gl.GL_FRONT_AND_BACK, gl.GL_SHININESS, -// Math.min(Math.max(ns, 0), 128)); -// } else { -// gl.glMaterialfv(gl.GL_FRONT_AND_BACK, gl.GL_SPECULAR, black, 0); -// } - } - - public void load(DataInputStream dis) throws IOException { - dis.readInt(); // name length - this.name = dis.readUTF(); - - dis.readInt(); // map_kdLength - this.map_kd = dis.readUTF(); - - if (parent.hasTexcoords() && map_kd.length() > 0) { - parent.loadTexture(map_kd); - } - - this.ka[0] = dis.readFloat(); - this.ka[1] = dis.readFloat(); - this.ka[2] = dis.readFloat(); - this.ka[3] = dis.readFloat(); - - this.kd[0] = dis.readFloat(); - this.kd[1] = dis.readFloat(); - this.kd[2] = dis.readFloat(); - this.kd[3] = dis.readFloat(); - - this.ks[0] = dis.readFloat(); - this.ks[1] = dis.readFloat(); - this.ks[2] = dis.readFloat(); - this.ks[3] = dis.readFloat(); - - this.ns = dis.readFloat(); - this.illum = dis.readInt(); - this.d = dis.readFloat(); - } - - public String toString() { - return "Material[" + - "name=\"" + name + "\"," + - "ka={" + ka[0] + "," + ka[1] + "," + ka[2] + "}," + - "kd={" + kd[0] + "," + kd[1] + "," + kd[2] + "}," + - "ks={" + ks[0] + "," + ks[1] + "," + ks[2] + "}," + - "ns=" + ns + "," + - "map_kd=\"" + - (map_kd == null ? "" : map_kd) + - "\"," + - "illum=" + illum + "," + - "d=" + d + - "]"; - } -} diff --git a/opengl/java/android/opengl/Object3D.java b/opengl/java/android/opengl/Object3D.java deleted file mode 100644 index 340c6a788155..000000000000 --- a/opengl/java/android/opengl/Object3D.java +++ /dev/null @@ -1,245 +0,0 @@ -/* - * Copyright (C) 2006 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 java.io.BufferedReader; -import java.io.DataInputStream; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import java.nio.IntBuffer; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import javax.microedition.khronos.opengles.*; - -/** - * {@hide} - */ -public abstract class Object3D { - - private boolean mHasTexcoords = false; - - private float mBoundsMinX = Float.MAX_VALUE; - private float mBoundsMaxX = Float.MIN_VALUE; - private float mBoundsMinY = Float.MAX_VALUE; - private float mBoundsMaxY = Float.MIN_VALUE; - private float mBoundsMinZ = Float.MAX_VALUE; - private float mBoundsMaxZ = Float.MIN_VALUE; - - private IntBuffer mVertexBuffer; - private IntBuffer mNormalBuffer; - private IntBuffer mTexcoordBuffer; - - // All groups, by name - private Map<String, Group> mGroups; - - // All materials, by name - private Map<String, Material> mMaterials; - - // All texture maps, by name - private Map<String, Texture> mTextures; - - public Object3D() { - reset(); - } - - /** - * Override this method with an implementation that contructs - * and InputStream from the given filename. For example, if the - * source files are to be retrieved using an AssetManager, - * the implementation would use AssetManager.load() to - * get the input stream. - */ - public abstract InputStream readFile(String filename) throws IOException; - - private void reset() { - mVertexBuffer = mNormalBuffer = mTexcoordBuffer = null; - - mGroups = new HashMap<String,Group>(); - mMaterials = new HashMap<String,Material>(); - mTextures = new HashMap<String,Texture>(); - } - - public Material getMaterial(String name) { - Material mat = mMaterials.get(name); - return mat; - } - - public Texture getTexture(String name) { - return mTextures.get(name); - } - - public IntBuffer getVertexBuffer() { - return mVertexBuffer; - } - - public IntBuffer getNormalBuffer() { - return mNormalBuffer; - } - - public IntBuffer getTexcoordBuffer() { - return mTexcoordBuffer; - } - - public int getNumTriangles() { - int numTriangles = 0; - Iterator<Group> iter = mGroups.values().iterator(); - while (iter.hasNext()) { - numTriangles += iter.next().getNumTriangles(); - } - return numTriangles; - } - - public boolean hasTexcoords() { - return mHasTexcoords; - } - - public float getBoundsMinX() { - return mBoundsMinX; - } - - public float getBoundsMaxX() { - return mBoundsMaxX; - } - - public float getBoundsMinY() { - return mBoundsMinY; - } - - public float getBoundsMaxY() { - return mBoundsMaxY; - } - - public float getBoundsMinZ() { - return mBoundsMinZ; - } - - public float getBoundsMaxZ() { - return mBoundsMaxZ; - } - - public void loadTexture(String name) throws IOException { - InputStream is = readFile(name + ".raw"); - Texture texture = new Texture(is); - mTextures.put(name, texture); - } - - private static void verifyByte(DataInputStream dis, int b) - throws IOException { - int x = dis.read() & 0xff; - if (x != b) { - throw new RuntimeException("Bad byte: " + - x + - " (expected " + b + ")"); - } - } - - public void load(String filename) throws IOException { - reset(); - - DataInputStream dis = new DataInputStream(readFile(filename)); - verifyByte(dis, 'g' + 128); - verifyByte(dis, 'l'); - verifyByte(dis, 'e'); - verifyByte(dis, 's'); - - int numTuples = dis.readInt(); - - this.mBoundsMinX = dis.readFloat(); - this.mBoundsMaxX = dis.readFloat(); - this.mBoundsMinY = dis.readFloat(); - this.mBoundsMaxY = dis.readFloat(); - this.mBoundsMinZ = dis.readFloat(); - this.mBoundsMaxZ = dis.readFloat(); - - this.mHasTexcoords = dis.readInt() == 1; - - int intsPerTuple = mHasTexcoords ? 8 : 6; - int numInts = numTuples*intsPerTuple; - - int len = 4*numTuples*(mHasTexcoords ? 8 : 6); - - byte[] tmp = new byte[len]; - int tidx = 0; - while (tidx < len) { - tidx += dis.read(tmp, tidx, len - tidx); - } - if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { - for (int i = 0; i < len; i += 4) { - byte tmp0 = tmp[i]; - byte tmp1 = tmp[i + 1]; - byte tmp2 = tmp[i + 2]; - byte tmp3 = tmp[i + 3]; - tmp[i] = tmp3; - tmp[i + 1] = tmp2; - tmp[i + 2] = tmp1; - tmp[i + 3] = tmp0; - } - } - - ByteBuffer allbb = ByteBuffer.allocateDirect(len); - allbb.order(ByteOrder.nativeOrder()); - allbb.put(tmp); - - allbb.position(0); - allbb.limit(4*3*numTuples); - ByteBuffer vbb = allbb.slice(); - this.mVertexBuffer = vbb.asIntBuffer(); - mVertexBuffer.position(0); - - if (mHasTexcoords) { - allbb.position(allbb.limit()); - allbb.limit(allbb.position() + 4*2*numTuples); - ByteBuffer tbb = allbb.slice(); - this.mTexcoordBuffer = tbb.asIntBuffer(); - mTexcoordBuffer.position(0); - } - - allbb.position(allbb.limit()); - allbb.limit(allbb.position() + 4*3*numTuples); - ByteBuffer nbb = allbb.slice(); - this.mNormalBuffer = nbb.asIntBuffer(); - mNormalBuffer.position(0); - - int numMaterials = dis.readInt(); - for (int i = 0; i < numMaterials; i++) { - Material mat = new Material(this); - mat.load(dis); - mMaterials.put(mat.getName(), mat); - } - - int numGroups = dis.readInt(); - for (int i = 0; i < numGroups; i++) { - Group g = new Group(this); - g.load(dis); - mGroups.put(g.getName(), g); - } - } - - public void draw(GL10 gl) { - Iterator<Group> iter = mGroups.values().iterator(); - while (iter.hasNext()) { - iter.next().draw(gl); - } - } -} - diff --git a/opengl/java/android/opengl/Texture.java b/opengl/java/android/opengl/Texture.java deleted file mode 100644 index dcd894d19cfc..000000000000 --- a/opengl/java/android/opengl/Texture.java +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright (C) 2006 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 java.io.InputStream; -import java.io.IOException; -import java.nio.Buffer; -import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import javax.microedition.khronos.opengles.GL10; - -import android.content.res.AssetManager; - -/** - * {@hide} - */ -public class Texture { - - private int width, height, bpp; - private ByteBuffer data; - private int name = -1; - - // Texture maps have the following format. All integers - // are 16 bits, high byte first. Pixels are in 5/6/5 - // RGB format, low byte first. - // - // width - // height - // pixel (0, 0) - // pixel (1, 0) - // ... - // pixel (width - 1, height - 1) - - private int readInt16(InputStream is) throws IOException { - return is.read() | (is.read() << 8); - } - - public Texture(InputStream is) throws IOException { - this.width = readInt16(is); - this.height = readInt16(is); - this.bpp = 2; - - int npixels = width*height; - int nbytes = npixels*bpp; - byte[] arr = new byte[nbytes]; - - int idx = 0; - while (idx < nbytes) { - int nread = is.read(arr, idx, nbytes - idx); - idx += nread; - } - - if (ByteOrder.nativeOrder() == ByteOrder.BIG_ENDIAN) { - // Swap pairs of bytes on big-endian platforms - for (int i = 0; i < npixels; i++) { - int j = i*2; - int k = j + 1; - - byte tmp = arr[j]; - arr[j] = arr[k]; - arr[k] = tmp; - } - } - - this.data = ByteBuffer.allocateDirect(arr.length); - this.data.order(ByteOrder.nativeOrder()); - data.put(arr); - data.position(0); - } - - private int loadTexture(GL10 gl, - int textureUnit, - int minFilter, int magFilter, - int wrapS, int wrapT, - int mode, - int width, int height, - int dataType, - Buffer data) { - int[] texture = new int[1]; - gl.glGenTextures(1, texture, 0); - - gl.glEnable(gl.GL_TEXTURE_2D); - gl.glClientActiveTexture(textureUnit); - gl.glBindTexture(gl.GL_TEXTURE_2D, texture[0]); - gl.glTexParameterf(gl.GL_TEXTURE_2D, - gl.GL_TEXTURE_MIN_FILTER, - minFilter); - gl.glTexParameterf(gl.GL_TEXTURE_2D, - gl.GL_TEXTURE_MAG_FILTER, - magFilter); - gl.glTexParameterf(gl.GL_TEXTURE_2D, - gl.GL_TEXTURE_WRAP_S, - wrapS); - gl.glTexParameterf(gl.GL_TEXTURE_2D, - gl.GL_TEXTURE_WRAP_T, - wrapT); - gl.glTexEnvf(gl.GL_TEXTURE_ENV, gl.GL_TEXTURE_ENV_MODE, mode); - - gl.glTexImage2D(gl.GL_TEXTURE_2D, 0, gl.GL_RGB, - width, height, - 0, gl.GL_RGB, dataType, - data); - - return texture[0]; - } - - public void setTextureParameters(GL10 gl) { - if (name < 0) { - name = loadTexture(gl, - gl.GL_TEXTURE0, - gl.GL_NEAREST, gl.GL_NEAREST, - gl.GL_REPEAT, gl.GL_REPEAT, - gl.GL_MODULATE, - width, height, - gl.GL_UNSIGNED_SHORT_5_6_5, - data); - } - - gl.glBindTexture(gl.GL_TEXTURE_2D, name); - } -} diff --git a/opengl/libs/GLES_trace/TODO.txt b/opengl/libs/GLES_trace/TODO.txt deleted file mode 100644 index f5e6e9518c7c..000000000000 --- a/opengl/libs/GLES_trace/TODO.txt +++ /dev/null @@ -1,14 +0,0 @@ -TODO: - - Context - Currently, we don't do anything regarding the contexts that are created. - Need to maintain more state regarding contexts, and figure out what happens in the - presence of multiple contexts. - - - Transport: Each GLMessage is sent via a socket as soon as the message is received. - i.e., there is no buffering of messages. Buffering should improve performance. - - - Initialization: On first connection, send some basic information that includes: - 1. version of the trace library - 2. implementation dependent GL state variables such as # of vertex arrays etc. - - - eglSwapBuffers: The images are lzf compressed, but there is no mode that transfers - only the differences from the previous images. diff --git a/opengl/libs/GLES_trace/gltrace.proto b/opengl/libs/GLES_trace/gltrace.proto index 11cf24f5900b..2893e6e87454 100644 --- a/opengl/libs/GLES_trace/gltrace.proto +++ b/opengl/libs/GLES_trace/gltrace.proto @@ -543,11 +543,13 @@ message GLMessage { required int32 context_id = 1; // GL context ID required int64 start_time = 2; // time when call was invoked - required int32 duration = 3; // duration of the call + required int32 duration = 3; // duration of the call (MONOTONIC TIME) required Function function = 4 [default = invalid]; // GL function called repeated DataType args = 5; // GL function's arguments optional DataType returnValue = 6; // GL function's return value optional FrameBuffer fb = 7; // contents of the framebuffer + + optional int32 threadtime = 8; // duration of the call (THREAD TIME) }; diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.cpp b/opengl/libs/GLES_trace/src/gltrace.pb.cpp index bb9d4a70e829..d5f8180e2630 100644 --- a/opengl/libs/GLES_trace/src/gltrace.pb.cpp +++ b/opengl/libs/GLES_trace/src/gltrace.pb.cpp @@ -1663,6 +1663,7 @@ const int GLMessage::kFunctionFieldNumber; const int GLMessage::kArgsFieldNumber; const int GLMessage::kReturnValueFieldNumber; const int GLMessage::kFbFieldNumber; +const int GLMessage::kThreadtimeFieldNumber; #endif // !_MSC_VER GLMessage::GLMessage() @@ -1689,6 +1690,7 @@ void GLMessage::SharedCtor() { function_ = 3000; returnvalue_ = NULL; fb_ = NULL; + threadtime_ = 0; ::memset(_has_bits_, 0, sizeof(_has_bits_)); } @@ -1730,6 +1732,7 @@ void GLMessage::Clear() { if (_has_bit(6)) { if (fb_ != NULL) fb_->::android::gltrace::GLMessage_FrameBuffer::Clear(); } + threadtime_ = 0; } args_.Clear(); ::memset(_has_bits_, 0, sizeof(_has_bits_)); @@ -1846,6 +1849,22 @@ bool GLMessage::MergePartialFromCodedStream( } else { goto handle_uninterpreted; } + if (input->ExpectTag(64)) goto parse_threadtime; + break; + } + + // optional int32 threadtime = 8; + case 8: { + if (::google::protobuf::internal::WireFormatLite::GetTagWireType(tag) == + ::google::protobuf::internal::WireFormatLite::WIRETYPE_VARINT) { + parse_threadtime: + DO_((::google::protobuf::internal::WireFormatLite::ReadPrimitive< + ::google::protobuf::int32, ::google::protobuf::internal::WireFormatLite::TYPE_INT32>( + input, &threadtime_))); + _set_bit(7); + } else { + goto handle_uninterpreted; + } if (input->ExpectAtEnd()) return true; break; } @@ -1906,6 +1925,11 @@ void GLMessage::SerializeWithCachedSizes( 7, this->fb(), output); } + // optional int32 threadtime = 8; + if (_has_bit(7)) { + ::google::protobuf::internal::WireFormatLite::WriteInt32(8, this->threadtime(), output); + } + } int GLMessage::ByteSize() const { @@ -1953,6 +1977,13 @@ int GLMessage::ByteSize() const { this->fb()); } + // optional int32 threadtime = 8; + if (has_threadtime()) { + total_size += 1 + + ::google::protobuf::internal::WireFormatLite::Int32Size( + this->threadtime()); + } + } // repeated .android.gltrace.GLMessage.DataType args = 5; total_size += 1 * this->args_size(); @@ -1995,6 +2026,9 @@ void GLMessage::MergeFrom(const GLMessage& from) { if (from._has_bit(6)) { mutable_fb()->::android::gltrace::GLMessage_FrameBuffer::MergeFrom(from.fb()); } + if (from._has_bit(7)) { + set_threadtime(from.threadtime()); + } } } @@ -2028,6 +2062,7 @@ void GLMessage::Swap(GLMessage* other) { args_.Swap(&other->args_); std::swap(returnvalue_, other->returnvalue_); std::swap(fb_, other->fb_); + std::swap(threadtime_, other->threadtime_); std::swap(_has_bits_[0], other->_has_bits_[0]); std::swap(_cached_size_, other->_cached_size_); } diff --git a/opengl/libs/GLES_trace/src/gltrace.pb.h b/opengl/libs/GLES_trace/src/gltrace.pb.h index e3b8990faee7..a4fcbd3545eb 100644 --- a/opengl/libs/GLES_trace/src/gltrace.pb.h +++ b/opengl/libs/GLES_trace/src/gltrace.pb.h @@ -1418,6 +1418,13 @@ class GLMessage : public ::google::protobuf::MessageLite { inline const ::android::gltrace::GLMessage_FrameBuffer& fb() const; inline ::android::gltrace::GLMessage_FrameBuffer* mutable_fb(); + // optional int32 threadtime = 8; + inline bool has_threadtime() const; + inline void clear_threadtime(); + static const int kThreadtimeFieldNumber = 8; + inline ::google::protobuf::int32 threadtime() const; + inline void set_threadtime(::google::protobuf::int32 value); + // @@protoc_insertion_point(class_scope:android.gltrace.GLMessage) private: mutable int _cached_size_; @@ -1429,11 +1436,12 @@ class GLMessage : public ::google::protobuf::MessageLite { ::google::protobuf::RepeatedPtrField< ::android::gltrace::GLMessage_DataType > args_; ::android::gltrace::GLMessage_DataType* returnvalue_; ::android::gltrace::GLMessage_FrameBuffer* fb_; + ::google::protobuf::int32 threadtime_; friend void protobuf_AddDesc_gltrace_2eproto(); friend void protobuf_AssignDesc_gltrace_2eproto(); friend void protobuf_ShutdownFile_gltrace_2eproto(); - ::google::protobuf::uint32 _has_bits_[(7 + 31) / 32]; + ::google::protobuf::uint32 _has_bits_[(8 + 31) / 32]; // WHY DOES & HAVE LOWER PRECEDENCE THAN != !? inline bool _has_bit(int index) const { @@ -1860,6 +1868,22 @@ inline ::android::gltrace::GLMessage_FrameBuffer* GLMessage::mutable_fb() { return fb_; } +// optional int32 threadtime = 8; +inline bool GLMessage::has_threadtime() const { + return _has_bit(7); +} +inline void GLMessage::clear_threadtime() { + threadtime_ = 0; + _clear_bit(7); +} +inline ::google::protobuf::int32 GLMessage::threadtime() const { + return threadtime_; +} +inline void GLMessage::set_threadtime(::google::protobuf::int32 value) { + _set_bit(7); + threadtime_ = value; +} + // @@protoc_insertion_point(namespace_scope) diff --git a/opengl/libs/GLES_trace/src/gltrace_api.cpp b/opengl/libs/GLES_trace/src/gltrace_api.cpp index a2366ac9b95a..358bf54ce445 100644 --- a/opengl/libs/GLES_trace/src/gltrace_api.cpp +++ b/opengl/libs/GLES_trace/src/gltrace_api.cpp @@ -43,11 +43,15 @@ void GLTrace_glActiveTexture(GLenum texture) { arg_texture->add_intvalue((int)texture); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glActiveTexture(texture); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -70,11 +74,15 @@ void GLTrace_glAttachShader(GLuint program, GLuint shader) { arg_shader->add_intvalue(shader); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glAttachShader(program, shader); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -103,11 +111,15 @@ void GLTrace_glBindAttribLocation(GLuint program, GLuint index, const GLchar* na arg_name->add_intvalue((int)name); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindAttribLocation(program, index, name); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -130,11 +142,15 @@ void GLTrace_glBindBuffer(GLenum target, GLuint buffer) { arg_buffer->add_intvalue(buffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindBuffer(target, buffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -157,11 +173,15 @@ void GLTrace_glBindFramebuffer(GLenum target, GLuint framebuffer) { arg_framebuffer->add_intvalue(framebuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindFramebuffer(target, framebuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -184,11 +204,15 @@ void GLTrace_glBindRenderbuffer(GLenum target, GLuint renderbuffer) { arg_renderbuffer->add_intvalue(renderbuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindRenderbuffer(target, renderbuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -211,11 +235,15 @@ void GLTrace_glBindTexture(GLenum target, GLuint texture) { arg_texture->add_intvalue(texture); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindTexture(target, texture); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -250,11 +278,15 @@ void GLTrace_glBlendColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf arg_alpha->add_floatvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendColor(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -271,11 +303,15 @@ void GLTrace_glBlendEquation(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendEquation(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -298,11 +334,15 @@ void GLTrace_glBlendEquationSeparate(GLenum modeRGB, GLenum modeAlpha) { arg_modeAlpha->add_intvalue((int)modeAlpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendEquationSeparate(modeRGB, modeAlpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -325,11 +365,15 @@ void GLTrace_glBlendFunc(GLenum sfactor, GLenum dfactor) { arg_dfactor->add_intvalue((int)dfactor); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendFunc(sfactor, dfactor); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -364,11 +408,15 @@ void GLTrace_glBlendFuncSeparate(GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, arg_dstAlpha->add_intvalue((int)dstAlpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendFuncSeparate(srcRGB, dstRGB, srcAlpha, dstAlpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -403,11 +451,15 @@ void GLTrace_glBufferData(GLenum target, GLsizeiptr size, const GLvoid* data, GL arg_usage->add_intvalue((int)usage); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBufferData(target, size, data, usage); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -442,11 +494,15 @@ void GLTrace_glBufferSubData(GLenum target, GLintptr offset, GLsizeiptr size, co arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBufferSubData(target, offset, size, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -463,9 +519,11 @@ GLenum GLTrace_glCheckFramebufferStatus(GLenum target) { arg_target->add_intvalue((int)target); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLenum retValue = glContext->hooks->gl.glCheckFramebufferStatus(target); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -473,7 +531,9 @@ GLenum GLTrace_glCheckFramebufferStatus(GLenum target) { rt->set_type(GLMessage::DataType::ENUM); rt->add_intvalue((int)retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -492,11 +552,15 @@ void GLTrace_glClear(GLbitfield mask) { arg_mask->add_intvalue(mask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClear(mask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -531,11 +595,15 @@ void GLTrace_glClearColor(GLclampf red, GLclampf green, GLclampf blue, GLclampf arg_alpha->add_floatvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearColor(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -552,11 +620,15 @@ void GLTrace_glClearDepthf(GLclampf depth) { arg_depth->add_floatvalue(depth); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearDepthf(depth); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -573,11 +645,15 @@ void GLTrace_glClearStencil(GLint s) { arg_s->add_intvalue(s); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearStencil(s); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -612,11 +688,15 @@ void GLTrace_glColorMask(GLboolean red, GLboolean green, GLboolean blue, GLboole arg_alpha->add_boolvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glColorMask(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -633,11 +713,15 @@ void GLTrace_glCompileShader(GLuint shader) { arg_shader->add_intvalue(shader); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCompileShader(shader); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -696,11 +780,15 @@ void GLTrace_glCompressedTexImage2D(GLenum target, GLint level, GLenum internalf arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCompressedTexImage2D(target, level, internalformat, width, height, border, imageSize, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -765,11 +853,15 @@ void GLTrace_glCompressedTexSubImage2D(GLenum target, GLint level, GLint xoffset arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCompressedTexSubImage2D(target, level, xoffset, yoffset, width, height, format, imageSize, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -828,11 +920,15 @@ void GLTrace_glCopyTexImage2D(GLenum target, GLint level, GLenum internalformat, arg_border->add_intvalue(border); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCopyTexImage2D(target, level, internalformat, x, y, width, height, border); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -891,11 +987,15 @@ void GLTrace_glCopyTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLin arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCopyTexSubImage2D(target, level, xoffset, yoffset, x, y, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -906,9 +1006,11 @@ GLuint GLTrace_glCreateProgram(void) { glmsg.set_function(GLMessage::glCreateProgram); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLuint retValue = glContext->hooks->gl.glCreateProgram(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -916,7 +1018,9 @@ GLuint GLTrace_glCreateProgram(void) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -935,9 +1039,11 @@ GLuint GLTrace_glCreateShader(GLenum type) { arg_type->add_intvalue((int)type); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLuint retValue = glContext->hooks->gl.glCreateShader(type); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -945,7 +1051,9 @@ GLuint GLTrace_glCreateShader(GLenum type) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -964,11 +1072,15 @@ void GLTrace_glCullFace(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCullFace(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -991,11 +1103,15 @@ void GLTrace_glDeleteBuffers(GLsizei n, const GLuint* buffers) { arg_buffers->add_intvalue((int)buffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteBuffers(n, buffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1018,11 +1134,15 @@ void GLTrace_glDeleteFramebuffers(GLsizei n, const GLuint* framebuffers) { arg_framebuffers->add_intvalue((int)framebuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteFramebuffers(n, framebuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1039,11 +1159,15 @@ void GLTrace_glDeleteProgram(GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteProgram(program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1066,11 +1190,15 @@ void GLTrace_glDeleteRenderbuffers(GLsizei n, const GLuint* renderbuffers) { arg_renderbuffers->add_intvalue((int)renderbuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteRenderbuffers(n, renderbuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1087,11 +1215,15 @@ void GLTrace_glDeleteShader(GLuint shader) { arg_shader->add_intvalue(shader); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteShader(shader); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1114,11 +1246,15 @@ void GLTrace_glDeleteTextures(GLsizei n, const GLuint* textures) { arg_textures->add_intvalue((int)textures); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteTextures(n, textures); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1135,11 +1271,15 @@ void GLTrace_glDepthFunc(GLenum func) { arg_func->add_intvalue((int)func); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDepthFunc(func); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1156,11 +1296,15 @@ void GLTrace_glDepthMask(GLboolean flag) { arg_flag->add_boolvalue(flag); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDepthMask(flag); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1183,11 +1327,15 @@ void GLTrace_glDepthRangef(GLclampf zNear, GLclampf zFar) { arg_zFar->add_floatvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDepthRangef(zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1210,11 +1358,15 @@ void GLTrace_glDetachShader(GLuint program, GLuint shader) { arg_shader->add_intvalue(shader); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDetachShader(program, shader); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1231,11 +1383,15 @@ void GLTrace_glDisable(GLenum cap) { arg_cap->add_intvalue((int)cap); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDisable(cap); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1252,11 +1408,15 @@ void GLTrace_glDisableVertexAttribArray(GLuint index) { arg_index->add_intvalue(index); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDisableVertexAttribArray(index); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1285,11 +1445,15 @@ void GLTrace_glDrawArrays(GLenum mode, GLint first, GLsizei count) { arg_count->add_intvalue(count); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawArrays(mode, first, count); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1324,11 +1488,15 @@ void GLTrace_glDrawElements(GLenum mode, GLsizei count, GLenum type, const GLvoi arg_indices->add_intvalue((int)indices); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawElements(mode, count, type, indices); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1345,11 +1513,15 @@ void GLTrace_glEnable(GLenum cap) { arg_cap->add_intvalue((int)cap); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEnable(cap); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1366,11 +1538,15 @@ void GLTrace_glEnableVertexAttribArray(GLuint index) { arg_index->add_intvalue(index); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEnableVertexAttribArray(index); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1381,11 +1557,15 @@ void GLTrace_glFinish(void) { glmsg.set_function(GLMessage::glFinish); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFinish(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1396,11 +1576,15 @@ void GLTrace_glFlush(void) { glmsg.set_function(GLMessage::glFlush); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFlush(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1435,11 +1619,15 @@ void GLTrace_glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum arg_renderbuffer->add_intvalue(renderbuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferRenderbuffer(target, attachment, renderbuffertarget, renderbuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1480,11 +1668,15 @@ void GLTrace_glFramebufferTexture2D(GLenum target, GLenum attachment, GLenum tex arg_level->add_intvalue(level); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferTexture2D(target, attachment, textarget, texture, level); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1501,11 +1693,15 @@ void GLTrace_glFrontFace(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFrontFace(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1528,11 +1724,15 @@ void GLTrace_glGenBuffers(GLsizei n, GLuint* buffers) { arg_buffers->add_intvalue((int)buffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenBuffers(n, buffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1549,11 +1749,15 @@ void GLTrace_glGenerateMipmap(GLenum target) { arg_target->add_intvalue((int)target); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenerateMipmap(target); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1576,11 +1780,15 @@ void GLTrace_glGenFramebuffers(GLsizei n, GLuint* framebuffers) { arg_framebuffers->add_intvalue((int)framebuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenFramebuffers(n, framebuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1603,11 +1811,15 @@ void GLTrace_glGenRenderbuffers(GLsizei n, GLuint* renderbuffers) { arg_renderbuffers->add_intvalue((int)renderbuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenRenderbuffers(n, renderbuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1630,11 +1842,15 @@ void GLTrace_glGenTextures(GLsizei n, GLuint* textures) { arg_textures->add_intvalue((int)textures); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenTextures(n, textures); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1687,11 +1903,15 @@ void GLTrace_glGetActiveAttrib(GLuint program, GLuint index, GLsizei bufsize, GL arg_name->add_intvalue((int)name); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetActiveAttrib(program, index, bufsize, length, size, type, name); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1744,11 +1964,15 @@ void GLTrace_glGetActiveUniform(GLuint program, GLuint index, GLsizei bufsize, G arg_name->add_intvalue((int)name); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetActiveUniform(program, index, bufsize, length, size, type, name); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1783,11 +2007,15 @@ void GLTrace_glGetAttachedShaders(GLuint program, GLsizei maxcount, GLsizei* cou arg_shaders->add_intvalue((int)shaders); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetAttachedShaders(program, maxcount, count, shaders); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1810,9 +2038,11 @@ int GLTrace_glGetAttribLocation(GLuint program, const GLchar* name) { arg_name->add_intvalue((int)name); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); int retValue = glContext->hooks->gl.glGetAttribLocation(program, name); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -1820,7 +2050,9 @@ int GLTrace_glGetAttribLocation(GLuint program, const GLchar* name) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -1845,11 +2077,15 @@ void GLTrace_glGetBooleanv(GLenum pname, GLboolean* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetBooleanv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1878,11 +2114,15 @@ void GLTrace_glGetBufferParameteriv(GLenum target, GLenum pname, GLint* params) arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetBufferParameteriv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1893,9 +2133,11 @@ GLenum GLTrace_glGetError(void) { glmsg.set_function(GLMessage::glGetError); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLenum retValue = glContext->hooks->gl.glGetError(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -1903,7 +2145,9 @@ GLenum GLTrace_glGetError(void) { rt->set_type(GLMessage::DataType::ENUM); rt->add_intvalue((int)retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -1928,11 +2172,15 @@ void GLTrace_glGetFloatv(GLenum pname, GLfloat* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetFloatv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1967,11 +2215,15 @@ void GLTrace_glGetFramebufferAttachmentParameteriv(GLenum target, GLenum attachm arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetFramebufferAttachmentParameteriv(target, attachment, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -1994,11 +2246,15 @@ void GLTrace_glGetIntegerv(GLenum pname, GLint* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetIntegerv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2027,11 +2283,15 @@ void GLTrace_glGetProgramiv(GLuint program, GLenum pname, GLint* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetProgramiv(program, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2066,11 +2326,15 @@ void GLTrace_glGetProgramInfoLog(GLuint program, GLsizei bufsize, GLsizei* lengt arg_infolog->add_intvalue((int)infolog); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetProgramInfoLog(program, bufsize, length, infolog); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2099,11 +2363,15 @@ void GLTrace_glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint* pa arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetRenderbufferParameteriv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2132,11 +2400,15 @@ void GLTrace_glGetShaderiv(GLuint shader, GLenum pname, GLint* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetShaderiv(shader, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2171,11 +2443,15 @@ void GLTrace_glGetShaderInfoLog(GLuint shader, GLsizei bufsize, GLsizei* length, arg_infolog->add_intvalue((int)infolog); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetShaderInfoLog(shader, bufsize, length, infolog); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2210,11 +2486,15 @@ void GLTrace_glGetShaderPrecisionFormat(GLenum shadertype, GLenum precisiontype, arg_precision->add_intvalue((int)precision); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetShaderPrecisionFormat(shadertype, precisiontype, range, precision); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2249,11 +2529,15 @@ void GLTrace_glGetShaderSource(GLuint shader, GLsizei bufsize, GLsizei* length, arg_source->add_intvalue((int)source); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetShaderSource(shader, bufsize, length, source); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2270,9 +2554,11 @@ const GLubyte* GLTrace_glGetString(GLenum name) { arg_name->add_intvalue((int)name); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); const GLubyte* retValue = glContext->hooks->gl.glGetString(name); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2280,7 +2566,9 @@ const GLubyte* GLTrace_glGetString(GLenum name) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue((int)retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2311,11 +2599,15 @@ void GLTrace_glGetTexParameterfv(GLenum target, GLenum pname, GLfloat* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexParameterfv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2344,11 +2636,15 @@ void GLTrace_glGetTexParameteriv(GLenum target, GLenum pname, GLint* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexParameteriv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2377,11 +2673,15 @@ void GLTrace_glGetUniformfv(GLuint program, GLint location, GLfloat* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetUniformfv(program, location, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2410,11 +2710,15 @@ void GLTrace_glGetUniformiv(GLuint program, GLint location, GLint* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetUniformiv(program, location, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2437,9 +2741,11 @@ int GLTrace_glGetUniformLocation(GLuint program, const GLchar* name) { arg_name->add_intvalue((int)name); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); int retValue = glContext->hooks->gl.glGetUniformLocation(program, name); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2447,7 +2753,9 @@ int GLTrace_glGetUniformLocation(GLuint program, const GLchar* name) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2478,11 +2786,15 @@ void GLTrace_glGetVertexAttribfv(GLuint index, GLenum pname, GLfloat* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetVertexAttribfv(index, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2511,11 +2823,15 @@ void GLTrace_glGetVertexAttribiv(GLuint index, GLenum pname, GLint* params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetVertexAttribiv(index, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2544,11 +2860,15 @@ void GLTrace_glGetVertexAttribPointerv(GLuint index, GLenum pname, GLvoid** poin arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetVertexAttribPointerv(index, pname, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2571,11 +2891,15 @@ void GLTrace_glHint(GLenum target, GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glHint(target, mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2592,9 +2916,11 @@ GLboolean GLTrace_glIsBuffer(GLuint buffer) { arg_buffer->add_intvalue(buffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsBuffer(buffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2602,7 +2928,9 @@ GLboolean GLTrace_glIsBuffer(GLuint buffer) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2621,9 +2949,11 @@ GLboolean GLTrace_glIsEnabled(GLenum cap) { arg_cap->add_intvalue((int)cap); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsEnabled(cap); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2631,7 +2961,9 @@ GLboolean GLTrace_glIsEnabled(GLenum cap) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2650,9 +2982,11 @@ GLboolean GLTrace_glIsFramebuffer(GLuint framebuffer) { arg_framebuffer->add_intvalue(framebuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsFramebuffer(framebuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2660,7 +2994,9 @@ GLboolean GLTrace_glIsFramebuffer(GLuint framebuffer) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2679,9 +3015,11 @@ GLboolean GLTrace_glIsProgram(GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsProgram(program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2689,7 +3027,9 @@ GLboolean GLTrace_glIsProgram(GLuint program) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2708,9 +3048,11 @@ GLboolean GLTrace_glIsRenderbuffer(GLuint renderbuffer) { arg_renderbuffer->add_intvalue(renderbuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsRenderbuffer(renderbuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2718,7 +3060,9 @@ GLboolean GLTrace_glIsRenderbuffer(GLuint renderbuffer) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2737,9 +3081,11 @@ GLboolean GLTrace_glIsShader(GLuint shader) { arg_shader->add_intvalue(shader); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsShader(shader); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2747,7 +3093,9 @@ GLboolean GLTrace_glIsShader(GLuint shader) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2766,9 +3114,11 @@ GLboolean GLTrace_glIsTexture(GLuint texture) { arg_texture->add_intvalue(texture); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsTexture(texture); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -2776,7 +3126,9 @@ GLboolean GLTrace_glIsTexture(GLuint texture) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -2795,11 +3147,15 @@ void GLTrace_glLineWidth(GLfloat width) { arg_width->add_floatvalue(width); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLineWidth(width); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2816,11 +3172,15 @@ void GLTrace_glLinkProgram(GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLinkProgram(program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2843,11 +3203,15 @@ void GLTrace_glPixelStorei(GLenum pname, GLint param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPixelStorei(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2870,11 +3234,15 @@ void GLTrace_glPolygonOffset(GLfloat factor, GLfloat units) { arg_units->add_floatvalue(units); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPolygonOffset(factor, units); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2927,11 +3295,15 @@ void GLTrace_glReadPixels(GLint x, GLint y, GLsizei width, GLsizei height, GLenu arg_pixels->add_intvalue((int)pixels); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glReadPixels(x, y, width, height, format, type, pixels); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2942,11 +3314,15 @@ void GLTrace_glReleaseShaderCompiler(void) { glmsg.set_function(GLMessage::glReleaseShaderCompiler); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glReleaseShaderCompiler(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -2981,11 +3357,15 @@ void GLTrace_glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRenderbufferStorage(target, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3008,11 +3388,15 @@ void GLTrace_glSampleCoverage(GLclampf value, GLboolean invert) { arg_invert->add_boolvalue(invert); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glSampleCoverage(value, invert); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3047,11 +3431,15 @@ void GLTrace_glScissor(GLint x, GLint y, GLsizei width, GLsizei height) { arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glScissor(x, y, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3092,11 +3480,15 @@ void GLTrace_glShaderBinary(GLsizei n, const GLuint* shaders, GLenum binaryforma arg_length->add_intvalue(length); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glShaderBinary(n, shaders, binaryformat, binary, length); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3131,11 +3523,15 @@ void GLTrace_glShaderSource(GLuint shader, GLsizei count, const GLchar** string, arg_length->add_intvalue((int)length); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glShaderSource(shader, count, string, length); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3164,11 +3560,15 @@ void GLTrace_glStencilFunc(GLenum func, GLint ref, GLuint mask) { arg_mask->add_intvalue(mask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStencilFunc(func, ref, mask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3203,11 +3603,15 @@ void GLTrace_glStencilFuncSeparate(GLenum face, GLenum func, GLint ref, GLuint m arg_mask->add_intvalue(mask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStencilFuncSeparate(face, func, ref, mask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3224,11 +3628,15 @@ void GLTrace_glStencilMask(GLuint mask) { arg_mask->add_intvalue(mask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStencilMask(mask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3251,11 +3659,15 @@ void GLTrace_glStencilMaskSeparate(GLenum face, GLuint mask) { arg_mask->add_intvalue(mask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStencilMaskSeparate(face, mask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3284,11 +3696,15 @@ void GLTrace_glStencilOp(GLenum fail, GLenum zfail, GLenum zpass) { arg_zpass->add_intvalue((int)zpass); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStencilOp(fail, zfail, zpass); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3323,11 +3739,15 @@ void GLTrace_glStencilOpSeparate(GLenum face, GLenum fail, GLenum zfail, GLenum arg_zpass->add_intvalue((int)zpass); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStencilOpSeparate(face, fail, zfail, zpass); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3392,11 +3812,15 @@ void GLTrace_glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsi arg_pixels->add_intvalue((int)pixels); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexImage2D(target, level, internalformat, width, height, border, format, type, pixels); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3425,11 +3849,15 @@ void GLTrace_glTexParameterf(GLenum target, GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameterf(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3458,11 +3886,15 @@ void GLTrace_glTexParameterfv(GLenum target, GLenum pname, const GLfloat* params arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameterfv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3491,11 +3923,15 @@ void GLTrace_glTexParameteri(GLenum target, GLenum pname, GLint param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameteri(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3524,11 +3960,15 @@ void GLTrace_glTexParameteriv(GLenum target, GLenum pname, const GLint* params) arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameteriv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3593,11 +4033,15 @@ void GLTrace_glTexSubImage2D(GLenum target, GLint level, GLint xoffset, GLint yo arg_pixels->add_intvalue((int)pixels); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexSubImage2D(target, level, xoffset, yoffset, width, height, format, type, pixels); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3620,11 +4064,15 @@ void GLTrace_glUniform1f(GLint location, GLfloat x) { arg_x->add_floatvalue(x); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform1f(location, x); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3653,11 +4101,15 @@ void GLTrace_glUniform1fv(GLint location, GLsizei count, const GLfloat* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform1fv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3680,11 +4132,15 @@ void GLTrace_glUniform1i(GLint location, GLint x) { arg_x->add_intvalue(x); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform1i(location, x); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3713,11 +4169,15 @@ void GLTrace_glUniform1iv(GLint location, GLsizei count, const GLint* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform1iv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3746,11 +4206,15 @@ void GLTrace_glUniform2f(GLint location, GLfloat x, GLfloat y) { arg_y->add_floatvalue(y); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform2f(location, x, y); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3779,11 +4243,15 @@ void GLTrace_glUniform2fv(GLint location, GLsizei count, const GLfloat* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform2fv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3812,11 +4280,15 @@ void GLTrace_glUniform2i(GLint location, GLint x, GLint y) { arg_y->add_intvalue(y); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform2i(location, x, y); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3845,11 +4317,15 @@ void GLTrace_glUniform2iv(GLint location, GLsizei count, const GLint* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform2iv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3884,11 +4360,15 @@ void GLTrace_glUniform3f(GLint location, GLfloat x, GLfloat y, GLfloat z) { arg_z->add_floatvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform3f(location, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3917,11 +4397,15 @@ void GLTrace_glUniform3fv(GLint location, GLsizei count, const GLfloat* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform3fv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3956,11 +4440,15 @@ void GLTrace_glUniform3i(GLint location, GLint x, GLint y, GLint z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform3i(location, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -3989,11 +4477,15 @@ void GLTrace_glUniform3iv(GLint location, GLsizei count, const GLint* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform3iv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4034,11 +4526,15 @@ void GLTrace_glUniform4f(GLint location, GLfloat x, GLfloat y, GLfloat z, GLfloa arg_w->add_floatvalue(w); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform4f(location, x, y, z, w); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4067,11 +4563,15 @@ void GLTrace_glUniform4fv(GLint location, GLsizei count, const GLfloat* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform4fv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4112,11 +4612,15 @@ void GLTrace_glUniform4i(GLint location, GLint x, GLint y, GLint z, GLint w) { arg_w->add_intvalue(w); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform4i(location, x, y, z, w); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4145,11 +4649,15 @@ void GLTrace_glUniform4iv(GLint location, GLsizei count, const GLint* v) { arg_v->add_intvalue((int)v); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniform4iv(location, count, v); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4184,11 +4692,15 @@ void GLTrace_glUniformMatrix2fv(GLint location, GLsizei count, GLboolean transpo arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniformMatrix2fv(location, count, transpose, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4223,11 +4735,15 @@ void GLTrace_glUniformMatrix3fv(GLint location, GLsizei count, GLboolean transpo arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniformMatrix3fv(location, count, transpose, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4262,11 +4778,15 @@ void GLTrace_glUniformMatrix4fv(GLint location, GLsizei count, GLboolean transpo arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUniformMatrix4fv(location, count, transpose, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4283,11 +4803,15 @@ void GLTrace_glUseProgram(GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUseProgram(program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4304,11 +4828,15 @@ void GLTrace_glValidateProgram(GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glValidateProgram(program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4331,11 +4859,15 @@ void GLTrace_glVertexAttrib1f(GLuint indx, GLfloat x) { arg_x->add_floatvalue(x); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib1f(indx, x); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4358,11 +4890,15 @@ void GLTrace_glVertexAttrib1fv(GLuint indx, const GLfloat* values) { arg_values->add_intvalue((int)values); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib1fv(indx, values); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4391,11 +4927,15 @@ void GLTrace_glVertexAttrib2f(GLuint indx, GLfloat x, GLfloat y) { arg_y->add_floatvalue(y); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib2f(indx, x, y); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4418,11 +4958,15 @@ void GLTrace_glVertexAttrib2fv(GLuint indx, const GLfloat* values) { arg_values->add_intvalue((int)values); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib2fv(indx, values); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4457,11 +5001,15 @@ void GLTrace_glVertexAttrib3f(GLuint indx, GLfloat x, GLfloat y, GLfloat z) { arg_z->add_floatvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib3f(indx, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4484,11 +5032,15 @@ void GLTrace_glVertexAttrib3fv(GLuint indx, const GLfloat* values) { arg_values->add_intvalue((int)values); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib3fv(indx, values); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4529,11 +5081,15 @@ void GLTrace_glVertexAttrib4f(GLuint indx, GLfloat x, GLfloat y, GLfloat z, GLfl arg_w->add_floatvalue(w); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib4f(indx, x, y, z, w); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4556,11 +5112,15 @@ void GLTrace_glVertexAttrib4fv(GLuint indx, const GLfloat* values) { arg_values->add_intvalue((int)values); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttrib4fv(indx, values); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4607,11 +5167,15 @@ void GLTrace_glVertexAttribPointer(GLuint indx, GLint size, GLenum type, GLboole arg_ptr->add_intvalue((int)ptr); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexAttribPointer(indx, size, type, normalized, stride, ptr); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4646,11 +5210,15 @@ void GLTrace_glViewport(GLint x, GLint y, GLsizei width, GLsizei height) { arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glViewport(x, y, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4676,11 +5244,15 @@ void GLTrace_glEGLImageTargetTexture2DOES(GLenum target, GLeglImageOES image) { arg_image->add_intvalue((int)image); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEGLImageTargetTexture2DOES(target, image); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4703,11 +5275,15 @@ void GLTrace_glEGLImageTargetRenderbufferStorageOES(GLenum target, GLeglImageOES arg_image->add_intvalue((int)image); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEGLImageTargetRenderbufferStorageOES(target, image); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4748,11 +5324,15 @@ void GLTrace_glGetProgramBinaryOES(GLuint program, GLsizei bufSize, GLsizei *len arg_binary->add_intvalue((int)binary); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetProgramBinaryOES(program, bufSize, length, binaryFormat, binary); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4787,11 +5367,15 @@ void GLTrace_glProgramBinaryOES(GLuint program, GLenum binaryFormat, const GLvoi arg_length->add_intvalue(length); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramBinaryOES(program, binaryFormat, binary, length); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4814,9 +5398,11 @@ void* GLTrace_glMapBufferOES(GLenum target, GLenum access) { arg_access->add_intvalue((int)access); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); void* retValue = glContext->hooks->gl.glMapBufferOES(target, access); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -4824,7 +5410,9 @@ void* GLTrace_glMapBufferOES(GLenum target, GLenum access) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue((int)retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -4843,9 +5431,11 @@ GLboolean GLTrace_glUnmapBufferOES(GLenum target) { arg_target->add_intvalue((int)target); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glUnmapBufferOES(target); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -4853,7 +5443,9 @@ GLboolean GLTrace_glUnmapBufferOES(GLenum target) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -4884,11 +5476,15 @@ void GLTrace_glGetBufferPointervOES(GLenum target, GLenum pname, GLvoid** params arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetBufferPointervOES(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -4959,11 +5555,15 @@ void GLTrace_glTexImage3DOES(GLenum target, GLint level, GLenum internalformat, arg_pixels->add_intvalue((int)pixels); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexImage3DOES(target, level, internalformat, width, height, depth, border, format, type, pixels); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5040,11 +5640,15 @@ void GLTrace_glTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, GLint arg_pixels->add_intvalue((int)pixels); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexSubImage3DOES(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, pixels); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5109,11 +5713,15 @@ void GLTrace_glCopyTexSubImage3DOES(GLenum target, GLint level, GLint xoffset, G arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCopyTexSubImage3DOES(target, level, xoffset, yoffset, zoffset, x, y, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5178,11 +5786,15 @@ void GLTrace_glCompressedTexImage3DOES(GLenum target, GLint level, GLenum intern arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCompressedTexImage3DOES(target, level, internalformat, width, height, depth, border, imageSize, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5259,11 +5871,15 @@ void GLTrace_glCompressedTexSubImage3DOES(GLenum target, GLint level, GLint xoff arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCompressedTexSubImage3DOES(target, level, xoffset, yoffset, zoffset, width, height, depth, format, imageSize, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5310,11 +5926,15 @@ void GLTrace_glFramebufferTexture3DOES(GLenum target, GLenum attachment, GLenum arg_zoffset->add_intvalue(zoffset); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferTexture3DOES(target, attachment, textarget, texture, level, zoffset); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5331,11 +5951,15 @@ void GLTrace_glBindVertexArrayOES(GLuint array) { arg_array->add_intvalue(array); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindVertexArrayOES(array); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5358,11 +5982,15 @@ void GLTrace_glDeleteVertexArraysOES(GLsizei n, const GLuint *arrays) { arg_arrays->add_intvalue((int)arrays); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteVertexArraysOES(n, arrays); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5385,11 +6013,15 @@ void GLTrace_glGenVertexArraysOES(GLsizei n, GLuint *arrays) { arg_arrays->add_intvalue((int)arrays); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenVertexArraysOES(n, arrays); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5406,9 +6038,11 @@ GLboolean GLTrace_glIsVertexArrayOES(GLuint array) { arg_array->add_intvalue(array); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsVertexArrayOES(array); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -5416,7 +6050,9 @@ GLboolean GLTrace_glIsVertexArrayOES(GLuint array) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -5447,11 +6083,15 @@ void GLTrace_glGetPerfMonitorGroupsAMD(GLint *numGroups, GLsizei groupsSize, GLu arg_groups->add_intvalue((int)groups); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPerfMonitorGroupsAMD(numGroups, groupsSize, groups); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5492,11 +6132,15 @@ void GLTrace_glGetPerfMonitorCountersAMD(GLuint group, GLint *numCounters, GLint arg_counters->add_intvalue((int)counters); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPerfMonitorCountersAMD(group, numCounters, maxActiveCounters, counterSize, counters); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5531,11 +6175,15 @@ void GLTrace_glGetPerfMonitorGroupStringAMD(GLuint group, GLsizei bufSize, GLsiz arg_groupString->add_intvalue((int)groupString); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPerfMonitorGroupStringAMD(group, bufSize, length, groupString); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5576,11 +6224,15 @@ void GLTrace_glGetPerfMonitorCounterStringAMD(GLuint group, GLuint counter, GLsi arg_counterString->add_intvalue((int)counterString); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPerfMonitorCounterStringAMD(group, counter, bufSize, length, counterString); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5615,11 +6267,15 @@ void GLTrace_glGetPerfMonitorCounterInfoAMD(GLuint group, GLuint counter, GLenum arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPerfMonitorCounterInfoAMD(group, counter, pname, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5642,11 +6298,15 @@ void GLTrace_glGenPerfMonitorsAMD(GLsizei n, GLuint *monitors) { arg_monitors->add_intvalue((int)monitors); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenPerfMonitorsAMD(n, monitors); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5669,11 +6329,15 @@ void GLTrace_glDeletePerfMonitorsAMD(GLsizei n, GLuint *monitors) { arg_monitors->add_intvalue((int)monitors); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeletePerfMonitorsAMD(n, monitors); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5714,11 +6378,15 @@ void GLTrace_glSelectPerfMonitorCountersAMD(GLuint monitor, GLboolean enable, GL arg_countersList->add_intvalue((int)countersList); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glSelectPerfMonitorCountersAMD(monitor, enable, group, numCounters, countersList); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5735,11 +6403,15 @@ void GLTrace_glBeginPerfMonitorAMD(GLuint monitor) { arg_monitor->add_intvalue(monitor); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBeginPerfMonitorAMD(monitor); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5756,11 +6428,15 @@ void GLTrace_glEndPerfMonitorAMD(GLuint monitor) { arg_monitor->add_intvalue(monitor); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEndPerfMonitorAMD(monitor); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5801,11 +6477,15 @@ void GLTrace_glGetPerfMonitorCounterDataAMD(GLuint monitor, GLenum pname, GLsize arg_bytesWritten->add_intvalue((int)bytesWritten); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPerfMonitorCounterDataAMD(monitor, pname, dataSize, data, bytesWritten); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5876,11 +6556,15 @@ void GLTrace_glBlitFramebufferANGLE(GLint srcX0, GLint srcY0, GLint srcX1, GLint arg_filter->add_intvalue((int)filter); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlitFramebufferANGLE(srcX0, srcY0, srcX1, srcY1, dstX0, dstY0, dstX1, dstY1, mask, filter); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5921,11 +6605,15 @@ void GLTrace_glRenderbufferStorageMultisampleANGLE(GLenum target, GLsizei sample arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRenderbufferStorageMultisampleANGLE(target, samples, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5966,11 +6654,15 @@ void GLTrace_glRenderbufferStorageMultisampleAPPLE(GLenum target, GLsizei sample arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRenderbufferStorageMultisampleAPPLE(target, samples, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -5981,11 +6673,15 @@ void GLTrace_glResolveMultisampleFramebufferAPPLE(void) { glmsg.set_function(GLMessage::glResolveMultisampleFramebufferAPPLE); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glResolveMultisampleFramebufferAPPLE(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6020,11 +6716,15 @@ void GLTrace_glLabelObjectEXT(GLenum type, GLuint object, GLsizei length, const arg_label->add_intvalue((int)label); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLabelObjectEXT(type, object, length, label); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6065,11 +6765,15 @@ void GLTrace_glGetObjectLabelEXT(GLenum type, GLuint object, GLsizei bufSize, GL arg_label->add_intvalue((int)label); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetObjectLabelEXT(type, object, bufSize, length, label); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6092,11 +6796,15 @@ void GLTrace_glInsertEventMarkerEXT(GLsizei length, const GLchar *marker) { arg_marker->add_intvalue((int)marker); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glInsertEventMarkerEXT(length, marker); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6119,11 +6827,15 @@ void GLTrace_glPushGroupMarkerEXT(GLsizei length, const GLchar *marker) { arg_marker->add_intvalue((int)marker); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPushGroupMarkerEXT(length, marker); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6134,11 +6846,15 @@ void GLTrace_glPopGroupMarkerEXT(void) { glmsg.set_function(GLMessage::glPopGroupMarkerEXT); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPopGroupMarkerEXT(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6167,11 +6883,15 @@ void GLTrace_glDiscardFramebufferEXT(GLenum target, GLsizei numAttachments, cons arg_attachments->add_intvalue((int)attachments); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDiscardFramebufferEXT(target, numAttachments, attachments); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6212,11 +6932,15 @@ void GLTrace_glRenderbufferStorageMultisampleEXT(GLenum target, GLsizei samples, arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRenderbufferStorageMultisampleEXT(target, samples, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6263,11 +6987,15 @@ void GLTrace_glFramebufferTexture2DMultisampleEXT(GLenum target, GLenum attachme arg_samples->add_intvalue(samples); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferTexture2DMultisampleEXT(target, attachment, textarget, texture, level, samples); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6302,11 +7030,15 @@ void GLTrace_glMultiDrawArraysEXT(GLenum mode, GLint *first, GLsizei *count, GLs arg_primcount->add_intvalue(primcount); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultiDrawArraysEXT(mode, first, count, primcount); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6347,11 +7079,15 @@ void GLTrace_glMultiDrawElementsEXT(GLenum mode, const GLsizei *count, GLenum ty arg_primcount->add_intvalue(primcount); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultiDrawElementsEXT(mode, count, type, indices, primcount); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6374,11 +7110,15 @@ void GLTrace_glGenQueriesEXT(GLsizei n, GLuint *ids) { arg_ids->add_intvalue((int)ids); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenQueriesEXT(n, ids); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6401,11 +7141,15 @@ void GLTrace_glDeleteQueriesEXT(GLsizei n, const GLuint *ids) { arg_ids->add_intvalue((int)ids); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteQueriesEXT(n, ids); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6422,9 +7166,11 @@ GLboolean GLTrace_glIsQueryEXT(GLuint id) { arg_id->add_intvalue(id); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsQueryEXT(id); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -6432,7 +7178,9 @@ GLboolean GLTrace_glIsQueryEXT(GLuint id) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -6457,11 +7205,15 @@ void GLTrace_glBeginQueryEXT(GLenum target, GLuint id) { arg_id->add_intvalue(id); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBeginQueryEXT(target, id); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6478,11 +7230,15 @@ void GLTrace_glEndQueryEXT(GLenum target) { arg_target->add_intvalue((int)target); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEndQueryEXT(target); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6511,11 +7267,15 @@ void GLTrace_glGetQueryivEXT(GLenum target, GLenum pname, GLint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetQueryivEXT(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6544,11 +7304,15 @@ void GLTrace_glGetQueryObjectuivEXT(GLuint id, GLenum pname, GLuint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetQueryObjectuivEXT(id, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6559,9 +7323,11 @@ GLenum GLTrace_glGetGraphicsResetStatusEXT(void) { glmsg.set_function(GLMessage::glGetGraphicsResetStatusEXT); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLenum retValue = glContext->hooks->gl.glGetGraphicsResetStatusEXT(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -6569,7 +7335,9 @@ GLenum GLTrace_glGetGraphicsResetStatusEXT(void) { rt->set_type(GLMessage::DataType::ENUM); rt->add_intvalue((int)retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -6630,11 +7398,15 @@ void GLTrace_glReadnPixelsEXT(GLint x, GLint y, GLsizei width, GLsizei height, G arg_data->add_intvalue((int)data); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glReadnPixelsEXT(x, y, width, height, format, type, bufSize, data); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6669,11 +7441,15 @@ void GLTrace_glGetnUniformfvEXT(GLuint program, GLint location, GLsizei bufSize, arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetnUniformfvEXT(program, location, bufSize, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6708,11 +7484,15 @@ void GLTrace_glGetnUniformivEXT(GLuint program, GLint location, GLsizei bufSize, arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetnUniformivEXT(program, location, bufSize, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6741,11 +7521,15 @@ void GLTrace_glUseProgramStagesEXT(GLuint pipeline, GLbitfield stages, GLuint pr arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glUseProgramStagesEXT(pipeline, stages, program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6768,11 +7552,15 @@ void GLTrace_glActiveShaderProgramEXT(GLuint pipeline, GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glActiveShaderProgramEXT(pipeline, program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6801,9 +7589,11 @@ GLuint GLTrace_glCreateShaderProgramvEXT(GLenum type, GLsizei count, const GLcha arg_strings->add_intvalue((int)strings); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLuint retValue = glContext->hooks->gl.glCreateShaderProgramvEXT(type, count, strings); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -6811,7 +7601,9 @@ GLuint GLTrace_glCreateShaderProgramvEXT(GLenum type, GLsizei count, const GLcha rt->set_type(GLMessage::DataType::INT); rt->add_intvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -6830,11 +7622,15 @@ void GLTrace_glBindProgramPipelineEXT(GLuint pipeline) { arg_pipeline->add_intvalue(pipeline); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindProgramPipelineEXT(pipeline); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6857,11 +7653,15 @@ void GLTrace_glDeleteProgramPipelinesEXT(GLsizei n, const GLuint *pipelines) { arg_pipelines->add_intvalue((int)pipelines); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteProgramPipelinesEXT(n, pipelines); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6884,11 +7684,15 @@ void GLTrace_glGenProgramPipelinesEXT(GLsizei n, GLuint *pipelines) { arg_pipelines->add_intvalue((int)pipelines); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenProgramPipelinesEXT(n, pipelines); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6905,9 +7709,11 @@ GLboolean GLTrace_glIsProgramPipelineEXT(GLuint pipeline) { arg_pipeline->add_intvalue(pipeline); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsProgramPipelineEXT(pipeline); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -6915,7 +7721,9 @@ GLboolean GLTrace_glIsProgramPipelineEXT(GLuint pipeline) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -6946,11 +7754,15 @@ void GLTrace_glProgramParameteriEXT(GLuint program, GLenum pname, GLint value) { arg_value->add_intvalue(value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramParameteriEXT(program, pname, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -6979,11 +7791,15 @@ void GLTrace_glGetProgramPipelineivEXT(GLuint pipeline, GLenum pname, GLint *par arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetProgramPipelineivEXT(pipeline, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7012,11 +7828,15 @@ void GLTrace_glProgramUniform1iEXT(GLuint program, GLint location, GLint x) { arg_x->add_intvalue(x); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform1iEXT(program, location, x); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7051,11 +7871,15 @@ void GLTrace_glProgramUniform2iEXT(GLuint program, GLint location, GLint x, GLin arg_y->add_intvalue(y); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform2iEXT(program, location, x, y); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7096,11 +7920,15 @@ void GLTrace_glProgramUniform3iEXT(GLuint program, GLint location, GLint x, GLin arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform3iEXT(program, location, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7147,11 +7975,15 @@ void GLTrace_glProgramUniform4iEXT(GLuint program, GLint location, GLint x, GLin arg_w->add_intvalue(w); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform4iEXT(program, location, x, y, z, w); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7180,11 +8012,15 @@ void GLTrace_glProgramUniform1fEXT(GLuint program, GLint location, GLfloat x) { arg_x->add_floatvalue(x); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform1fEXT(program, location, x); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7219,11 +8055,15 @@ void GLTrace_glProgramUniform2fEXT(GLuint program, GLint location, GLfloat x, GL arg_y->add_floatvalue(y); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform2fEXT(program, location, x, y); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7264,11 +8104,15 @@ void GLTrace_glProgramUniform3fEXT(GLuint program, GLint location, GLfloat x, GL arg_z->add_floatvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform3fEXT(program, location, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7315,11 +8159,15 @@ void GLTrace_glProgramUniform4fEXT(GLuint program, GLint location, GLfloat x, GL arg_w->add_floatvalue(w); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform4fEXT(program, location, x, y, z, w); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7354,11 +8202,15 @@ void GLTrace_glProgramUniform1ivEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform1ivEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7393,11 +8245,15 @@ void GLTrace_glProgramUniform2ivEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform2ivEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7432,11 +8288,15 @@ void GLTrace_glProgramUniform3ivEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform3ivEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7471,11 +8331,15 @@ void GLTrace_glProgramUniform4ivEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform4ivEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7510,11 +8374,15 @@ void GLTrace_glProgramUniform1fvEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform1fvEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7549,11 +8417,15 @@ void GLTrace_glProgramUniform2fvEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform2fvEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7588,11 +8460,15 @@ void GLTrace_glProgramUniform3fvEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform3fvEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7627,11 +8503,15 @@ void GLTrace_glProgramUniform4fvEXT(GLuint program, GLint location, GLsizei coun arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniform4fvEXT(program, location, count, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7672,11 +8552,15 @@ void GLTrace_glProgramUniformMatrix2fvEXT(GLuint program, GLint location, GLsize arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniformMatrix2fvEXT(program, location, count, transpose, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7717,11 +8601,15 @@ void GLTrace_glProgramUniformMatrix3fvEXT(GLuint program, GLint location, GLsize arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniformMatrix3fvEXT(program, location, count, transpose, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7762,11 +8650,15 @@ void GLTrace_glProgramUniformMatrix4fvEXT(GLuint program, GLint location, GLsize arg_value->add_intvalue((int)value); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glProgramUniformMatrix4fvEXT(program, location, count, transpose, value); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7783,11 +8675,15 @@ void GLTrace_glValidateProgramPipelineEXT(GLuint pipeline) { arg_pipeline->add_intvalue(pipeline); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glValidateProgramPipelineEXT(pipeline); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7822,11 +8718,15 @@ void GLTrace_glGetProgramPipelineInfoLogEXT(GLuint pipeline, GLsizei bufSize, GL arg_infoLog->add_intvalue((int)infoLog); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetProgramPipelineInfoLogEXT(pipeline, bufSize, length, infoLog); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7861,11 +8761,15 @@ void GLTrace_glTexStorage1DEXT(GLenum target, GLsizei levels, GLenum internalfor arg_width->add_intvalue(width); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexStorage1DEXT(target, levels, internalformat, width); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7906,11 +8810,15 @@ void GLTrace_glTexStorage2DEXT(GLenum target, GLsizei levels, GLenum internalfor arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexStorage2DEXT(target, levels, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -7957,11 +8865,15 @@ void GLTrace_glTexStorage3DEXT(GLenum target, GLsizei levels, GLenum internalfor arg_depth->add_intvalue(depth); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexStorage3DEXT(target, levels, internalformat, width, height, depth); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8002,11 +8914,15 @@ void GLTrace_glTextureStorage1DEXT(GLuint texture, GLenum target, GLsizei levels arg_width->add_intvalue(width); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTextureStorage1DEXT(texture, target, levels, internalformat, width); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8053,11 +8969,15 @@ void GLTrace_glTextureStorage2DEXT(GLuint texture, GLenum target, GLsizei levels arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTextureStorage2DEXT(texture, target, levels, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8110,11 +9030,15 @@ void GLTrace_glTextureStorage3DEXT(GLuint texture, GLenum target, GLsizei levels arg_depth->add_intvalue(depth); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTextureStorage3DEXT(texture, target, levels, internalformat, width, height, depth); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8155,11 +9079,15 @@ void GLTrace_glRenderbufferStorageMultisampleIMG(GLenum target, GLsizei samples, arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRenderbufferStorageMultisampleIMG(target, samples, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8206,11 +9134,15 @@ void GLTrace_glFramebufferTexture2DMultisampleIMG(GLenum target, GLenum attachme arg_samples->add_intvalue(samples); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferTexture2DMultisampleIMG(target, attachment, textarget, texture, level, samples); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8227,11 +9159,15 @@ void GLTrace_glCoverageMaskNV(GLboolean mask) { arg_mask->add_boolvalue(mask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCoverageMaskNV(mask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8248,11 +9184,15 @@ void GLTrace_glCoverageOperationNV(GLenum operation) { arg_operation->add_intvalue((int)operation); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCoverageOperationNV(operation); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8275,11 +9215,15 @@ void GLTrace_glDrawBuffersNV(GLsizei n, const GLenum *bufs) { arg_bufs->add_intvalue((int)bufs); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawBuffersNV(n, bufs); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8302,11 +9246,15 @@ void GLTrace_glDeleteFencesNV(GLsizei n, const GLuint *fences) { arg_fences->add_intvalue((int)fences); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteFencesNV(n, fences); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8329,11 +9277,15 @@ void GLTrace_glGenFencesNV(GLsizei n, GLuint *fences) { arg_fences->add_intvalue((int)fences); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenFencesNV(n, fences); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8350,9 +9302,11 @@ GLboolean GLTrace_glIsFenceNV(GLuint fence) { arg_fence->add_intvalue(fence); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsFenceNV(fence); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -8360,7 +9314,9 @@ GLboolean GLTrace_glIsFenceNV(GLuint fence) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -8379,9 +9335,11 @@ GLboolean GLTrace_glTestFenceNV(GLuint fence) { arg_fence->add_intvalue(fence); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glTestFenceNV(fence); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -8389,7 +9347,9 @@ GLboolean GLTrace_glTestFenceNV(GLuint fence) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -8420,11 +9380,15 @@ void GLTrace_glGetFenceivNV(GLuint fence, GLenum pname, GLint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetFenceivNV(fence, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8441,11 +9405,15 @@ void GLTrace_glFinishFenceNV(GLuint fence) { arg_fence->add_intvalue(fence); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFinishFenceNV(fence); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8468,11 +9436,15 @@ void GLTrace_glSetFenceNV(GLuint fence, GLenum condition) { arg_condition->add_intvalue((int)condition); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glSetFenceNV(fence, condition); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8489,11 +9461,15 @@ void GLTrace_glReadBufferNV(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glReadBufferNV(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8516,11 +9492,15 @@ void GLTrace_glAlphaFuncQCOM(GLenum func, GLclampf ref) { arg_ref->add_floatvalue(ref); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glAlphaFuncQCOM(func, ref); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8549,11 +9529,15 @@ void GLTrace_glGetDriverControlsQCOM(GLint *num, GLsizei size, GLuint *driverCon arg_driverControls->add_intvalue((int)driverControls); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetDriverControlsQCOM(num, size, driverControls); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8588,11 +9572,15 @@ void GLTrace_glGetDriverControlStringQCOM(GLuint driverControl, GLsizei bufSize, arg_driverControlString->add_intvalue((int)driverControlString); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetDriverControlStringQCOM(driverControl, bufSize, length, driverControlString); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8609,11 +9597,15 @@ void GLTrace_glEnableDriverControlQCOM(GLuint driverControl) { arg_driverControl->add_intvalue(driverControl); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEnableDriverControlQCOM(driverControl); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8630,11 +9622,15 @@ void GLTrace_glDisableDriverControlQCOM(GLuint driverControl) { arg_driverControl->add_intvalue(driverControl); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDisableDriverControlQCOM(driverControl); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8663,11 +9659,15 @@ void GLTrace_glExtGetTexturesQCOM(GLuint *textures, GLint maxTextures, GLint *nu arg_numTextures->add_intvalue((int)numTextures); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetTexturesQCOM(textures, maxTextures, numTextures); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8696,11 +9696,15 @@ void GLTrace_glExtGetBuffersQCOM(GLuint *buffers, GLint maxBuffers, GLint *numBu arg_numBuffers->add_intvalue((int)numBuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetBuffersQCOM(buffers, maxBuffers, numBuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8729,11 +9733,15 @@ void GLTrace_glExtGetRenderbuffersQCOM(GLuint *renderbuffers, GLint maxRenderbuf arg_numRenderbuffers->add_intvalue((int)numRenderbuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetRenderbuffersQCOM(renderbuffers, maxRenderbuffers, numRenderbuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8762,11 +9770,15 @@ void GLTrace_glExtGetFramebuffersQCOM(GLuint *framebuffers, GLint maxFramebuffer arg_numFramebuffers->add_intvalue((int)numFramebuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetFramebuffersQCOM(framebuffers, maxFramebuffers, numFramebuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8807,11 +9819,15 @@ void GLTrace_glExtGetTexLevelParameterivQCOM(GLuint texture, GLenum face, GLint arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetTexLevelParameterivQCOM(texture, face, level, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8840,11 +9856,15 @@ void GLTrace_glExtTexObjectStateOverrideiQCOM(GLenum target, GLenum pname, GLint arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtTexObjectStateOverrideiQCOM(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8921,11 +9941,15 @@ void GLTrace_glExtGetTexSubImageQCOM(GLenum target, GLint level, GLint xoffset, arg_texels->add_intvalue((int)texels); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetTexSubImageQCOM(target, level, xoffset, yoffset, zoffset, width, height, depth, format, type, texels); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8948,11 +9972,15 @@ void GLTrace_glExtGetBufferPointervQCOM(GLenum target, GLvoid **params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetBufferPointervQCOM(target, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -8981,11 +10009,15 @@ void GLTrace_glExtGetShadersQCOM(GLuint *shaders, GLint maxShaders, GLint *numSh arg_numShaders->add_intvalue((int)numShaders); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetShadersQCOM(shaders, maxShaders, numShaders); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9014,11 +10046,15 @@ void GLTrace_glExtGetProgramsQCOM(GLuint *programs, GLint maxPrograms, GLint *nu arg_numPrograms->add_intvalue((int)numPrograms); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetProgramsQCOM(programs, maxPrograms, numPrograms); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9035,9 +10071,11 @@ GLboolean GLTrace_glExtIsProgramBinaryQCOM(GLuint program) { arg_program->add_intvalue(program); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glExtIsProgramBinaryQCOM(program); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -9045,7 +10083,9 @@ GLboolean GLTrace_glExtIsProgramBinaryQCOM(GLuint program) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -9082,11 +10122,15 @@ void GLTrace_glExtGetProgramBinarySourceQCOM(GLuint program, GLenum shadertype, arg_length->add_intvalue((int)length); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glExtGetProgramBinarySourceQCOM(program, shadertype, source, length); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9127,11 +10171,15 @@ void GLTrace_glStartTilingQCOM(GLuint x, GLuint y, GLuint width, GLuint height, arg_preserveMask->add_intvalue(preserveMask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glStartTilingQCOM(x, y, width, height, preserveMask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9148,11 +10196,15 @@ void GLTrace_glEndTilingQCOM(GLbitfield preserveMask) { arg_preserveMask->add_intvalue(preserveMask); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEndTilingQCOM(preserveMask); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9178,11 +10230,15 @@ void GLTrace_glAlphaFunc(GLenum func, GLclampf ref) { arg_ref->add_floatvalue(ref); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glAlphaFunc(func, ref); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9205,11 +10261,15 @@ void GLTrace_glClipPlanef(GLenum plane, const GLfloat *equation) { arg_equation->add_intvalue((int)equation); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClipPlanef(plane, equation); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9244,11 +10304,15 @@ void GLTrace_glColor4f(GLfloat red, GLfloat green, GLfloat blue, GLfloat alpha) arg_alpha->add_floatvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glColor4f(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9271,11 +10335,15 @@ void GLTrace_glFogf(GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFogf(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9298,11 +10366,15 @@ void GLTrace_glFogfv(GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFogfv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9349,11 +10421,15 @@ void GLTrace_glFrustumf(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top arg_zFar->add_floatvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFrustumf(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9376,11 +10452,15 @@ void GLTrace_glGetClipPlanef(GLenum pname, GLfloat eqn[4]) { arg_eqn->add_intvalue((int)eqn); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetClipPlanef(pname, eqn); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9409,11 +10489,15 @@ void GLTrace_glGetLightfv(GLenum light, GLenum pname, GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetLightfv(light, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9442,11 +10526,15 @@ void GLTrace_glGetMaterialfv(GLenum face, GLenum pname, GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetMaterialfv(face, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9475,11 +10563,15 @@ void GLTrace_glGetTexEnvfv(GLenum env, GLenum pname, GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexEnvfv(env, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9502,11 +10594,15 @@ void GLTrace_glLightModelf(GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightModelf(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9529,11 +10625,15 @@ void GLTrace_glLightModelfv(GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightModelfv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9562,11 +10662,15 @@ void GLTrace_glLightf(GLenum light, GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightf(light, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9595,11 +10699,15 @@ void GLTrace_glLightfv(GLenum light, GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightfv(light, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9616,11 +10724,15 @@ void GLTrace_glLoadMatrixf(const GLfloat *m) { arg_m->add_intvalue((int)m); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLoadMatrixf(m); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9649,11 +10761,15 @@ void GLTrace_glMaterialf(GLenum face, GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMaterialf(face, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9682,11 +10798,15 @@ void GLTrace_glMaterialfv(GLenum face, GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMaterialfv(face, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9703,11 +10823,15 @@ void GLTrace_glMultMatrixf(const GLfloat *m) { arg_m->add_intvalue((int)m); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultMatrixf(m); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9748,11 +10872,15 @@ void GLTrace_glMultiTexCoord4f(GLenum target, GLfloat s, GLfloat t, GLfloat r, G arg_q->add_floatvalue(q); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultiTexCoord4f(target, s, t, r, q); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9781,11 +10909,15 @@ void GLTrace_glNormal3f(GLfloat nx, GLfloat ny, GLfloat nz) { arg_nz->add_floatvalue(nz); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glNormal3f(nx, ny, nz); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9832,11 +10964,15 @@ void GLTrace_glOrthof(GLfloat left, GLfloat right, GLfloat bottom, GLfloat top, arg_zFar->add_floatvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glOrthof(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9859,11 +10995,15 @@ void GLTrace_glPointParameterf(GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointParameterf(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9886,11 +11026,15 @@ void GLTrace_glPointParameterfv(GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointParameterfv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9907,11 +11051,15 @@ void GLTrace_glPointSize(GLfloat size) { arg_size->add_floatvalue(size); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointSize(size); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9946,11 +11094,15 @@ void GLTrace_glRotatef(GLfloat angle, GLfloat x, GLfloat y, GLfloat z) { arg_z->add_floatvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRotatef(angle, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -9979,11 +11131,15 @@ void GLTrace_glScalef(GLfloat x, GLfloat y, GLfloat z) { arg_z->add_floatvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glScalef(x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10012,11 +11168,15 @@ void GLTrace_glTexEnvf(GLenum target, GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvf(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10045,11 +11205,15 @@ void GLTrace_glTexEnvfv(GLenum target, GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvfv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10078,11 +11242,15 @@ void GLTrace_glTranslatef(GLfloat x, GLfloat y, GLfloat z) { arg_z->add_floatvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTranslatef(x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10105,11 +11273,15 @@ void GLTrace_glAlphaFuncx(GLenum func, GLclampx ref) { arg_ref->add_intvalue(ref); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glAlphaFuncx(func, ref); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10144,11 +11316,15 @@ void GLTrace_glClearColorx(GLclampx red, GLclampx green, GLclampx blue, GLclampx arg_alpha->add_intvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearColorx(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10165,11 +11341,15 @@ void GLTrace_glClearDepthx(GLclampx depth) { arg_depth->add_intvalue(depth); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearDepthx(depth); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10186,11 +11366,15 @@ void GLTrace_glClientActiveTexture(GLenum texture) { arg_texture->add_intvalue((int)texture); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClientActiveTexture(texture); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10213,11 +11397,15 @@ void GLTrace_glClipPlanex(GLenum plane, const GLfixed *equation) { arg_equation->add_intvalue((int)equation); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClipPlanex(plane, equation); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10252,11 +11440,15 @@ void GLTrace_glColor4ub(GLubyte red, GLubyte green, GLubyte blue, GLubyte alpha) arg_alpha->add_intvalue((int)alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glColor4ub(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10291,11 +11483,15 @@ void GLTrace_glColor4x(GLfixed red, GLfixed green, GLfixed blue, GLfixed alpha) arg_alpha->add_intvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glColor4x(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10330,11 +11526,15 @@ void GLTrace_glColorPointer(GLint size, GLenum type, GLsizei stride, const GLvoi arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glColorPointer(size, type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10357,11 +11557,15 @@ void GLTrace_glDepthRangex(GLclampx zNear, GLclampx zFar) { arg_zFar->add_intvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDepthRangex(zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10378,11 +11582,15 @@ void GLTrace_glDisableClientState(GLenum array) { arg_array->add_intvalue((int)array); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDisableClientState(array); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10399,11 +11607,15 @@ void GLTrace_glEnableClientState(GLenum array) { arg_array->add_intvalue((int)array); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glEnableClientState(array); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10426,11 +11638,15 @@ void GLTrace_glFogx(GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFogx(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10453,11 +11669,15 @@ void GLTrace_glFogxv(GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFogxv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10504,11 +11724,15 @@ void GLTrace_glFrustumx(GLfixed left, GLfixed right, GLfixed bottom, GLfixed top arg_zFar->add_intvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFrustumx(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10531,11 +11755,15 @@ void GLTrace_glGetClipPlanex(GLenum pname, GLfixed eqn[4]) { arg_eqn->add_intvalue((int)eqn); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetClipPlanex(pname, eqn); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10558,11 +11786,15 @@ void GLTrace_glGetFixedv(GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetFixedv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10591,11 +11823,15 @@ void GLTrace_glGetLightxv(GLenum light, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetLightxv(light, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10624,11 +11860,15 @@ void GLTrace_glGetMaterialxv(GLenum face, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetMaterialxv(face, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10651,11 +11891,15 @@ void GLTrace_glGetPointerv(GLenum pname, GLvoid **params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetPointerv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10684,11 +11928,15 @@ void GLTrace_glGetTexEnviv(GLenum env, GLenum pname, GLint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexEnviv(env, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10717,11 +11965,15 @@ void GLTrace_glGetTexEnvxv(GLenum env, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexEnvxv(env, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10750,11 +12002,15 @@ void GLTrace_glGetTexParameterxv(GLenum target, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexParameterxv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10777,11 +12033,15 @@ void GLTrace_glLightModelx(GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightModelx(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10804,11 +12064,15 @@ void GLTrace_glLightModelxv(GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightModelxv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10837,11 +12101,15 @@ void GLTrace_glLightx(GLenum light, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightx(light, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10870,11 +12138,15 @@ void GLTrace_glLightxv(GLenum light, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightxv(light, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10891,11 +12163,15 @@ void GLTrace_glLineWidthx(GLfixed width) { arg_width->add_intvalue(width); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLineWidthx(width); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10906,11 +12182,15 @@ void GLTrace_glLoadIdentity(void) { glmsg.set_function(GLMessage::glLoadIdentity); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLoadIdentity(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10927,11 +12207,15 @@ void GLTrace_glLoadMatrixx(const GLfixed *m) { arg_m->add_intvalue((int)m); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLoadMatrixx(m); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10948,11 +12232,15 @@ void GLTrace_glLogicOp(GLenum opcode) { arg_opcode->add_intvalue((int)opcode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLogicOp(opcode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -10981,11 +12269,15 @@ void GLTrace_glMaterialx(GLenum face, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMaterialx(face, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11014,11 +12306,15 @@ void GLTrace_glMaterialxv(GLenum face, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMaterialxv(face, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11035,11 +12331,15 @@ void GLTrace_glMatrixMode(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMatrixMode(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11056,11 +12356,15 @@ void GLTrace_glMultMatrixx(const GLfixed *m) { arg_m->add_intvalue((int)m); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultMatrixx(m); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11101,11 +12405,15 @@ void GLTrace_glMultiTexCoord4x(GLenum target, GLfixed s, GLfixed t, GLfixed r, G arg_q->add_intvalue(q); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultiTexCoord4x(target, s, t, r, q); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11134,11 +12442,15 @@ void GLTrace_glNormal3x(GLfixed nx, GLfixed ny, GLfixed nz) { arg_nz->add_intvalue(nz); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glNormal3x(nx, ny, nz); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11167,11 +12479,15 @@ void GLTrace_glNormalPointer(GLenum type, GLsizei stride, const GLvoid *pointer) arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glNormalPointer(type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11218,11 +12534,15 @@ void GLTrace_glOrthox(GLfixed left, GLfixed right, GLfixed bottom, GLfixed top, arg_zFar->add_intvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glOrthox(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11245,11 +12565,15 @@ void GLTrace_glPointParameterx(GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointParameterx(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11272,11 +12596,15 @@ void GLTrace_glPointParameterxv(GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointParameterxv(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11293,11 +12621,15 @@ void GLTrace_glPointSizex(GLfixed size) { arg_size->add_intvalue(size); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointSizex(size); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11320,11 +12652,15 @@ void GLTrace_glPolygonOffsetx(GLfixed factor, GLfixed units) { arg_units->add_intvalue(units); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPolygonOffsetx(factor, units); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11335,11 +12671,15 @@ void GLTrace_glPopMatrix(void) { glmsg.set_function(GLMessage::glPopMatrix); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPopMatrix(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11350,11 +12690,15 @@ void GLTrace_glPushMatrix(void) { glmsg.set_function(GLMessage::glPushMatrix); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPushMatrix(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11389,11 +12733,15 @@ void GLTrace_glRotatex(GLfixed angle, GLfixed x, GLfixed y, GLfixed z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRotatex(angle, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11416,11 +12764,15 @@ void GLTrace_glSampleCoveragex(GLclampx value, GLboolean invert) { arg_invert->add_boolvalue(invert); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glSampleCoveragex(value, invert); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11449,11 +12801,15 @@ void GLTrace_glScalex(GLfixed x, GLfixed y, GLfixed z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glScalex(x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11470,11 +12826,15 @@ void GLTrace_glShadeModel(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glShadeModel(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11509,11 +12869,15 @@ void GLTrace_glTexCoordPointer(GLint size, GLenum type, GLsizei stride, const GL arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexCoordPointer(size, type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11542,11 +12906,15 @@ void GLTrace_glTexEnvi(GLenum target, GLenum pname, GLint param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvi(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11575,11 +12943,15 @@ void GLTrace_glTexEnvx(GLenum target, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvx(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11608,11 +12980,15 @@ void GLTrace_glTexEnviv(GLenum target, GLenum pname, const GLint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnviv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11641,11 +13017,15 @@ void GLTrace_glTexEnvxv(GLenum target, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvxv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11674,11 +13054,15 @@ void GLTrace_glTexParameterx(GLenum target, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameterx(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11707,11 +13091,15 @@ void GLTrace_glTexParameterxv(GLenum target, GLenum pname, const GLfixed *params arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameterxv(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11740,11 +13128,15 @@ void GLTrace_glTranslatex(GLfixed x, GLfixed y, GLfixed z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTranslatex(x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11779,11 +13171,15 @@ void GLTrace_glVertexPointer(GLint size, GLenum type, GLsizei stride, const GLvo arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glVertexPointer(size, type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11812,11 +13208,15 @@ void GLTrace_glPointSizePointerOES(GLenum type, GLsizei stride, const GLvoid *po arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointSizePointerOES(type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11842,11 +13242,15 @@ void GLTrace_glBlendEquationSeparateOES(GLenum modeRGB, GLenum modeAlpha) { arg_modeAlpha->add_intvalue((int)modeAlpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendEquationSeparateOES(modeRGB, modeAlpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11881,11 +13285,15 @@ void GLTrace_glBlendFuncSeparateOES(GLenum srcRGB, GLenum dstRGB, GLenum srcAlph arg_dstAlpha->add_intvalue((int)dstAlpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendFuncSeparateOES(srcRGB, dstRGB, srcAlpha, dstAlpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11902,11 +13310,15 @@ void GLTrace_glBlendEquationOES(GLenum mode) { arg_mode->add_intvalue((int)mode); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBlendEquationOES(mode); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11947,11 +13359,15 @@ void GLTrace_glDrawTexsOES(GLshort x, GLshort y, GLshort z, GLshort width, GLsho arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexsOES(x, y, z, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -11992,11 +13408,15 @@ void GLTrace_glDrawTexiOES(GLint x, GLint y, GLint z, GLint width, GLint height) arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexiOES(x, y, z, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12037,11 +13457,15 @@ void GLTrace_glDrawTexxOES(GLfixed x, GLfixed y, GLfixed z, GLfixed width, GLfix arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexxOES(x, y, z, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12058,11 +13482,15 @@ void GLTrace_glDrawTexsvOES(const GLshort *coords) { arg_coords->add_intvalue((int)coords); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexsvOES(coords); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12079,11 +13507,15 @@ void GLTrace_glDrawTexivOES(const GLint *coords) { arg_coords->add_intvalue((int)coords); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexivOES(coords); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12100,11 +13532,15 @@ void GLTrace_glDrawTexxvOES(const GLfixed *coords) { arg_coords->add_intvalue((int)coords); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexxvOES(coords); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12145,11 +13581,15 @@ void GLTrace_glDrawTexfOES(GLfloat x, GLfloat y, GLfloat z, GLfloat width, GLflo arg_height->add_floatvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexfOES(x, y, z, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12166,11 +13606,15 @@ void GLTrace_glDrawTexfvOES(const GLfloat *coords) { arg_coords->add_intvalue((int)coords); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDrawTexfvOES(coords); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12193,11 +13637,15 @@ void GLTrace_glAlphaFuncxOES(GLenum func, GLclampx ref) { arg_ref->add_intvalue(ref); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glAlphaFuncxOES(func, ref); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12232,11 +13680,15 @@ void GLTrace_glClearColorxOES(GLclampx red, GLclampx green, GLclampx blue, GLcla arg_alpha->add_intvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearColorxOES(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12253,11 +13705,15 @@ void GLTrace_glClearDepthxOES(GLclampx depth) { arg_depth->add_intvalue(depth); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearDepthxOES(depth); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12280,11 +13736,15 @@ void GLTrace_glClipPlanexOES(GLenum plane, const GLfixed *equation) { arg_equation->add_intvalue((int)equation); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClipPlanexOES(plane, equation); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12319,11 +13779,15 @@ void GLTrace_glColor4xOES(GLfixed red, GLfixed green, GLfixed blue, GLfixed alph arg_alpha->add_intvalue(alpha); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glColor4xOES(red, green, blue, alpha); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12346,11 +13810,15 @@ void GLTrace_glDepthRangexOES(GLclampx zNear, GLclampx zFar) { arg_zFar->add_intvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDepthRangexOES(zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12373,11 +13841,15 @@ void GLTrace_glFogxOES(GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFogxOES(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12400,11 +13872,15 @@ void GLTrace_glFogxvOES(GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFogxvOES(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12451,11 +13927,15 @@ void GLTrace_glFrustumxOES(GLfixed left, GLfixed right, GLfixed bottom, GLfixed arg_zFar->add_intvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFrustumxOES(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12478,11 +13958,15 @@ void GLTrace_glGetClipPlanexOES(GLenum pname, GLfixed eqn[4]) { arg_eqn->add_intvalue((int)eqn); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetClipPlanexOES(pname, eqn); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12505,11 +13989,15 @@ void GLTrace_glGetFixedvOES(GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetFixedvOES(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12538,11 +14026,15 @@ void GLTrace_glGetLightxvOES(GLenum light, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetLightxvOES(light, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12571,11 +14063,15 @@ void GLTrace_glGetMaterialxvOES(GLenum face, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetMaterialxvOES(face, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12604,11 +14100,15 @@ void GLTrace_glGetTexEnvxvOES(GLenum env, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexEnvxvOES(env, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12637,11 +14137,15 @@ void GLTrace_glGetTexParameterxvOES(GLenum target, GLenum pname, GLfixed *params arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexParameterxvOES(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12664,11 +14168,15 @@ void GLTrace_glLightModelxOES(GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightModelxOES(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12691,11 +14199,15 @@ void GLTrace_glLightModelxvOES(GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightModelxvOES(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12724,11 +14236,15 @@ void GLTrace_glLightxOES(GLenum light, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightxOES(light, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12757,11 +14273,15 @@ void GLTrace_glLightxvOES(GLenum light, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLightxvOES(light, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12778,11 +14298,15 @@ void GLTrace_glLineWidthxOES(GLfixed width) { arg_width->add_intvalue(width); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLineWidthxOES(width); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12799,11 +14323,15 @@ void GLTrace_glLoadMatrixxOES(const GLfixed *m) { arg_m->add_intvalue((int)m); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLoadMatrixxOES(m); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12832,11 +14360,15 @@ void GLTrace_glMaterialxOES(GLenum face, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMaterialxOES(face, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12865,11 +14397,15 @@ void GLTrace_glMaterialxvOES(GLenum face, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMaterialxvOES(face, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12886,11 +14422,15 @@ void GLTrace_glMultMatrixxOES(const GLfixed *m) { arg_m->add_intvalue((int)m); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultMatrixxOES(m); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12931,11 +14471,15 @@ void GLTrace_glMultiTexCoord4xOES(GLenum target, GLfixed s, GLfixed t, GLfixed r arg_q->add_intvalue(q); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMultiTexCoord4xOES(target, s, t, r, q); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -12964,11 +14508,15 @@ void GLTrace_glNormal3xOES(GLfixed nx, GLfixed ny, GLfixed nz) { arg_nz->add_intvalue(nz); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glNormal3xOES(nx, ny, nz); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13015,11 +14563,15 @@ void GLTrace_glOrthoxOES(GLfixed left, GLfixed right, GLfixed bottom, GLfixed to arg_zFar->add_intvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glOrthoxOES(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13042,11 +14594,15 @@ void GLTrace_glPointParameterxOES(GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointParameterxOES(pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13069,11 +14625,15 @@ void GLTrace_glPointParameterxvOES(GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointParameterxvOES(pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13090,11 +14650,15 @@ void GLTrace_glPointSizexOES(GLfixed size) { arg_size->add_intvalue(size); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPointSizexOES(size); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13117,11 +14681,15 @@ void GLTrace_glPolygonOffsetxOES(GLfixed factor, GLfixed units) { arg_units->add_intvalue(units); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glPolygonOffsetxOES(factor, units); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13156,11 +14724,15 @@ void GLTrace_glRotatexOES(GLfixed angle, GLfixed x, GLfixed y, GLfixed z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRotatexOES(angle, x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13183,11 +14755,15 @@ void GLTrace_glSampleCoveragexOES(GLclampx value, GLboolean invert) { arg_invert->add_boolvalue(invert); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glSampleCoveragexOES(value, invert); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13216,11 +14792,15 @@ void GLTrace_glScalexOES(GLfixed x, GLfixed y, GLfixed z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glScalexOES(x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13249,11 +14829,15 @@ void GLTrace_glTexEnvxOES(GLenum target, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvxOES(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13282,11 +14866,15 @@ void GLTrace_glTexEnvxvOES(GLenum target, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexEnvxvOES(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13315,11 +14903,15 @@ void GLTrace_glTexParameterxOES(GLenum target, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameterxOES(target, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13348,11 +14940,15 @@ void GLTrace_glTexParameterxvOES(GLenum target, GLenum pname, const GLfixed *par arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexParameterxvOES(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13381,11 +14977,15 @@ void GLTrace_glTranslatexOES(GLfixed x, GLfixed y, GLfixed z) { arg_z->add_intvalue(z); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTranslatexOES(x, y, z); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13402,9 +15002,11 @@ GLboolean GLTrace_glIsRenderbufferOES(GLuint renderbuffer) { arg_renderbuffer->add_intvalue(renderbuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsRenderbufferOES(renderbuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -13412,7 +15014,9 @@ GLboolean GLTrace_glIsRenderbufferOES(GLuint renderbuffer) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -13437,11 +15041,15 @@ void GLTrace_glBindRenderbufferOES(GLenum target, GLuint renderbuffer) { arg_renderbuffer->add_intvalue(renderbuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindRenderbufferOES(target, renderbuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13464,11 +15072,15 @@ void GLTrace_glDeleteRenderbuffersOES(GLsizei n, const GLuint* renderbuffers) { arg_renderbuffers->add_intvalue((int)renderbuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteRenderbuffersOES(n, renderbuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13491,11 +15103,15 @@ void GLTrace_glGenRenderbuffersOES(GLsizei n, GLuint* renderbuffers) { arg_renderbuffers->add_intvalue((int)renderbuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenRenderbuffersOES(n, renderbuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13530,11 +15146,15 @@ void GLTrace_glRenderbufferStorageOES(GLenum target, GLenum internalformat, GLsi arg_height->add_intvalue(height); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glRenderbufferStorageOES(target, internalformat, width, height); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13563,11 +15183,15 @@ void GLTrace_glGetRenderbufferParameterivOES(GLenum target, GLenum pname, GLint* arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetRenderbufferParameterivOES(target, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13584,9 +15208,11 @@ GLboolean GLTrace_glIsFramebufferOES(GLuint framebuffer) { arg_framebuffer->add_intvalue(framebuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLboolean retValue = glContext->hooks->gl.glIsFramebufferOES(framebuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -13594,7 +15220,9 @@ GLboolean GLTrace_glIsFramebufferOES(GLuint framebuffer) { rt->set_type(GLMessage::DataType::BOOL); rt->add_boolvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -13619,11 +15247,15 @@ void GLTrace_glBindFramebufferOES(GLenum target, GLuint framebuffer) { arg_framebuffer->add_intvalue(framebuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glBindFramebufferOES(target, framebuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13646,11 +15278,15 @@ void GLTrace_glDeleteFramebuffersOES(GLsizei n, const GLuint* framebuffers) { arg_framebuffers->add_intvalue((int)framebuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDeleteFramebuffersOES(n, framebuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13673,11 +15309,15 @@ void GLTrace_glGenFramebuffersOES(GLsizei n, GLuint* framebuffers) { arg_framebuffers->add_intvalue((int)framebuffers); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenFramebuffersOES(n, framebuffers); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13694,9 +15334,11 @@ GLenum GLTrace_glCheckFramebufferStatusOES(GLenum target) { arg_target->add_intvalue((int)target); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLenum retValue = glContext->hooks->gl.glCheckFramebufferStatusOES(target); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -13704,7 +15346,9 @@ GLenum GLTrace_glCheckFramebufferStatusOES(GLenum target) { rt->set_type(GLMessage::DataType::ENUM); rt->add_intvalue((int)retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -13741,11 +15385,15 @@ void GLTrace_glFramebufferRenderbufferOES(GLenum target, GLenum attachment, GLen arg_renderbuffer->add_intvalue(renderbuffer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferRenderbufferOES(target, attachment, renderbuffertarget, renderbuffer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13786,11 +15434,15 @@ void GLTrace_glFramebufferTexture2DOES(GLenum target, GLenum attachment, GLenum arg_level->add_intvalue(level); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFramebufferTexture2DOES(target, attachment, textarget, texture, level); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13825,11 +15477,15 @@ void GLTrace_glGetFramebufferAttachmentParameterivOES(GLenum target, GLenum atta arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetFramebufferAttachmentParameterivOES(target, attachment, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13846,11 +15502,15 @@ void GLTrace_glGenerateMipmapOES(GLenum target) { arg_target->add_intvalue((int)target); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGenerateMipmapOES(target); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13867,11 +15527,15 @@ void GLTrace_glCurrentPaletteMatrixOES(GLuint matrixpaletteindex) { arg_matrixpaletteindex->add_intvalue(matrixpaletteindex); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glCurrentPaletteMatrixOES(matrixpaletteindex); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13882,11 +15546,15 @@ void GLTrace_glLoadPaletteFromModelViewMatrixOES(void) { glmsg.set_function(GLMessage::glLoadPaletteFromModelViewMatrixOES); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glLoadPaletteFromModelViewMatrixOES(); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13921,11 +15589,15 @@ void GLTrace_glMatrixIndexPointerOES(GLint size, GLenum type, GLsizei stride, co arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glMatrixIndexPointerOES(size, type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13960,11 +15632,15 @@ void GLTrace_glWeightPointerOES(GLint size, GLenum type, GLsizei stride, const G arg_pointer->add_intvalue((int)pointer); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glWeightPointerOES(size, type, stride, pointer); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -13987,9 +15663,11 @@ GLbitfield GLTrace_glQueryMatrixxOES(GLfixed mantissa[16], GLint exponent[16]) { arg_exponent->add_intvalue((int)exponent); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); GLbitfield retValue = glContext->hooks->gl.glQueryMatrixxOES(mantissa, exponent); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); // set return value GLMessage_DataType *rt = glmsg.mutable_returnvalue(); @@ -13997,7 +15675,9 @@ GLbitfield GLTrace_glQueryMatrixxOES(GLfixed mantissa[16], GLint exponent[16]) { rt->set_type(GLMessage::DataType::INT); rt->add_intvalue(retValue); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); return retValue; @@ -14022,11 +15702,15 @@ void GLTrace_glDepthRangefOES(GLclampf zNear, GLclampf zFar) { arg_zFar->add_floatvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glDepthRangefOES(zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14073,11 +15757,15 @@ void GLTrace_glFrustumfOES(GLfloat left, GLfloat right, GLfloat bottom, GLfloat arg_zFar->add_floatvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glFrustumfOES(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14124,11 +15812,15 @@ void GLTrace_glOrthofOES(GLfloat left, GLfloat right, GLfloat bottom, GLfloat to arg_zFar->add_floatvalue(zFar); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glOrthofOES(left, right, bottom, top, zNear, zFar); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14151,11 +15843,15 @@ void GLTrace_glClipPlanefOES(GLenum plane, const GLfloat *equation) { arg_equation->add_intvalue((int)equation); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClipPlanefOES(plane, equation); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14178,11 +15874,15 @@ void GLTrace_glGetClipPlanefOES(GLenum pname, GLfloat eqn[4]) { arg_eqn->add_intvalue((int)eqn); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetClipPlanefOES(pname, eqn); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14199,11 +15899,15 @@ void GLTrace_glClearDepthfOES(GLclampf depth) { arg_depth->add_floatvalue(depth); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClearDepthfOES(depth); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14232,11 +15936,15 @@ void GLTrace_glTexGenfOES(GLenum coord, GLenum pname, GLfloat param) { arg_param->add_floatvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexGenfOES(coord, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14265,11 +15973,15 @@ void GLTrace_glTexGenfvOES(GLenum coord, GLenum pname, const GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexGenfvOES(coord, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14298,11 +16010,15 @@ void GLTrace_glTexGeniOES(GLenum coord, GLenum pname, GLint param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexGeniOES(coord, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14331,11 +16047,15 @@ void GLTrace_glTexGenivOES(GLenum coord, GLenum pname, const GLint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexGenivOES(coord, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14364,11 +16084,15 @@ void GLTrace_glTexGenxOES(GLenum coord, GLenum pname, GLfixed param) { arg_param->add_intvalue(param); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexGenxOES(coord, pname, param); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14397,11 +16121,15 @@ void GLTrace_glTexGenxvOES(GLenum coord, GLenum pname, const GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glTexGenxvOES(coord, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14430,11 +16158,15 @@ void GLTrace_glGetTexGenfvOES(GLenum coord, GLenum pname, GLfloat *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexGenfvOES(coord, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14463,11 +16195,15 @@ void GLTrace_glGetTexGenivOES(GLenum coord, GLenum pname, GLint *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexGenivOES(coord, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14496,11 +16232,15 @@ void GLTrace_glGetTexGenxvOES(GLenum coord, GLenum pname, GLfixed *params) { arg_params->add_intvalue((int)params); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glGetTexGenxvOES(coord, pname, params); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14523,11 +16263,15 @@ void GLTrace_glClipPlanefIMG(GLenum p, const GLfloat *eqn) { arg_eqn->add_intvalue((int)eqn); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClipPlanefIMG(p, eqn); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } @@ -14550,11 +16294,15 @@ void GLTrace_glClipPlanexIMG(GLenum p, const GLfixed *eqn) { arg_eqn->add_intvalue((int)eqn); // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); glContext->hooks->gl.glClipPlanexIMG(p, eqn); - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); } diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp index 3e185bc80cf7..6c4feb5a56d7 100644 --- a/opengl/libs/GLES_trace/src/gltrace_fixup.cpp +++ b/opengl/libs/GLES_trace/src/gltrace_fixup.cpp @@ -379,13 +379,16 @@ void fixup_glGetActiveAttribOrUniform(GLMessage *glmsg, int location) { arg_location->add_intvalue(location); } -void fixupGLMessage(GLTraceContext *context, nsecs_t start, nsecs_t end, GLMessage *glmsg) { +void fixupGLMessage(GLTraceContext *context, nsecs_t wallStart, nsecs_t wallEnd, + nsecs_t threadStart, nsecs_t threadEnd, + GLMessage *glmsg) { // for all messages, set the current context id glmsg->set_context_id(context->getId()); // set start time and duration - glmsg->set_start_time(start); - glmsg->set_duration((unsigned)(end - start)); + glmsg->set_start_time(wallStart); + glmsg->set_duration((unsigned)(wallEnd - wallStart)); + glmsg->set_threadtime((unsigned)(threadEnd - threadStart)); // do any custom message dependent processing switch (glmsg->function()) { diff --git a/opengl/libs/GLES_trace/src/gltrace_fixup.h b/opengl/libs/GLES_trace/src/gltrace_fixup.h index 64f75458dfe4..f63b0569036e 100644 --- a/opengl/libs/GLES_trace/src/gltrace_fixup.h +++ b/opengl/libs/GLES_trace/src/gltrace_fixup.h @@ -25,7 +25,9 @@ namespace android { namespace gltrace { -void fixupGLMessage(GLTraceContext *curContext, nsecs_t start, nsecs_t end, GLMessage *message); +void fixupGLMessage(GLTraceContext *curContext, nsecs_t wallStart, nsecs_t wallEnd, + nsecs_t threadStart, nsecs_t threadEnd, + GLMessage *message); void fixup_addFBContents(GLTraceContext *curContext, GLMessage *message, FBBinding fbToRead); }; diff --git a/opengl/libs/GLES_trace/tools/genapi.py b/opengl/libs/GLES_trace/tools/genapi.py index 557e4076dba3..e1660be1aa3b 100755 --- a/opengl/libs/GLES_trace/tools/genapi.py +++ b/opengl/libs/GLES_trace/tools/genapi.py @@ -162,13 +162,15 @@ TRACE_CALL_TEMPLATE = pyratemp.Template( <!--(end)--> // call function - nsecs_t start_time = systemTime(); + nsecs_t wallStartTime = systemTime(SYSTEM_TIME_MONOTONIC); + nsecs_t threadStartTime = systemTime(SYSTEM_TIME_THREAD); <!--(if retType != "void")--> $!retType!$ retValue = glContext->hooks->gl.$!callsite!$; <!--(else)--> glContext->hooks->gl.$!callsite!$; <!--(end)--> - nsecs_t end_time = systemTime(); + nsecs_t threadEndTime = systemTime(SYSTEM_TIME_THREAD); + nsecs_t wallEndTime = systemTime(SYSTEM_TIME_MONOTONIC); <!--(if retType != "void")--> // set return value @@ -178,7 +180,9 @@ TRACE_CALL_TEMPLATE = pyratemp.Template( rt->$!retDataType.getProtobufCall()!$retValue); <!--(end)--> - fixupGLMessage(glContext, start_time, end_time, &glmsg); + fixupGLMessage(glContext, wallStartTime, wallEndTime, + threadStartTime, threadEndTime, + &glmsg); glContext->traceGLMessage(&glmsg); <!--(if retType != "void")--> diff --git a/packages/SettingsProvider/res/values/defaults.xml b/packages/SettingsProvider/res/values/defaults.xml index 3beaac998b75..3a8e3fc6fcff 100644 --- a/packages/SettingsProvider/res/values/defaults.xml +++ b/packages/SettingsProvider/res/values/defaults.xml @@ -148,4 +148,7 @@ <!-- Development settings --> <bool name="def_stay_on_while_plugged_in">false</bool> + <!-- Number of retries for connecting to DHCP. + Value here is the same as WifiStateMachine.DEFAULT_MAX_DHCP_RETRIES --> + <integer name="def_max_dhcp_retries">9</integer> </resources> diff --git a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java index 882aa6624a28..330a1899a0b0 100644 --- a/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java +++ b/packages/SettingsProvider/src/com/android/providers/settings/DatabaseHelper.java @@ -1602,6 +1602,9 @@ public class DatabaseHelper extends SQLiteOpenHelper { loadBooleanSetting(stmt, Settings.Secure.NETSTATS_ENABLED, R.bool.def_netstats_enabled); + + loadIntegerSetting(stmt, Settings.Secure.WIFI_MAX_DHCP_RETRY_COUNT, + R.integer.def_max_dhcp_retries); } finally { if (stmt != null) stmt.close(); } diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index b87b8c3dd7f6..301dbf51cbff 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -115,6 +115,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final TypedValue mMinWidthMajor = new TypedValue(); final TypedValue mMinWidthMinor = new TypedValue(); + TypedValue mFixedWidthMajor; + TypedValue mFixedWidthMinor; + TypedValue mFixedHeightMajor; + TypedValue mFixedHeightMinor; // This is the top-level view of the window, containing the window decor. private DecorView mDecor; @@ -2088,6 +2092,44 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { final boolean isPortrait = metrics.widthPixels < metrics.heightPixels; final int widthMode = getMode(widthMeasureSpec); + final int heightMode = getMode(heightMeasureSpec); + + boolean fixedWidth = false; + if (widthMode == AT_MOST) { + final TypedValue tvw = isPortrait ? mFixedWidthMinor : mFixedWidthMajor; + if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { + fixedWidth = true; + final int w; + if (tvw.type == TypedValue.TYPE_DIMENSION) { + w = (int) tvw.getDimension(metrics); + } else if (tvw.type == TypedValue.TYPE_FRACTION) { + w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); + } else { + w = 0; + } + + final int widthSize = MeasureSpec.getSize(widthMeasureSpec); + widthMeasureSpec = MeasureSpec.makeMeasureSpec(Math.min(w, widthSize), EXACTLY); + } + } + + if (heightMode == AT_MOST) { + final TypedValue tvh = isPortrait ? mFixedHeightMajor : mFixedHeightMinor; + if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { + final int h; + if (tvh.type == TypedValue.TYPE_DIMENSION) { + h = (int) tvh.getDimension(metrics); + } else if (tvh.type == TypedValue.TYPE_FRACTION) { + h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); + } else { + h = 0; + } + + final int heightSize = MeasureSpec.getSize(heightMeasureSpec); + heightMeasureSpec = + MeasureSpec.makeMeasureSpec(Math.min(h, heightSize), EXACTLY); + } + } super.onMeasure(widthMeasureSpec, heightMeasureSpec); @@ -2096,21 +2138,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); - final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; - - if (widthMode == AT_MOST && tv.type != TypedValue.TYPE_NULL) { - final int min; - if (tv.type == TypedValue.TYPE_DIMENSION) { - min = (int)tv.getDimension(metrics); - } else if (tv.type == TypedValue.TYPE_FRACTION) { - min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); - } else { - min = 0; - } + if (!fixedWidth && widthMode == AT_MOST) { + final TypedValue tv = isPortrait ? mMinWidthMinor : mMinWidthMajor; + if (tv.type != TypedValue.TYPE_NULL) { + final int min; + if (tv.type == TypedValue.TYPE_DIMENSION) { + min = (int)tv.getDimension(metrics); + } else if (tv.type == TypedValue.TYPE_FRACTION) { + min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels); + } else { + min = 0; + } - if (width < min) { - widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); - measure = true; + if (width < min) { + widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); + measure = true; + } } } @@ -2571,6 +2614,26 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMajor, mMinWidthMajor); a.getValue(com.android.internal.R.styleable.Window_windowMinWidthMinor, mMinWidthMinor); + if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor)) { + if (mFixedWidthMajor == null) mFixedWidthMajor = new TypedValue(); + a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMajor, + mFixedWidthMajor); + } + if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor)) { + if (mFixedWidthMinor == null) mFixedWidthMinor = new TypedValue(); + a.getValue(com.android.internal.R.styleable.Window_windowFixedWidthMinor, + mFixedWidthMinor); + } + if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor)) { + if (mFixedHeightMajor == null) mFixedHeightMajor = new TypedValue(); + a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMajor, + mFixedHeightMajor); + } + if (a.hasValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor)) { + if (mFixedHeightMinor == null) mFixedHeightMinor = new TypedValue(); + a.getValue(com.android.internal.R.styleable.Window_windowFixedHeightMinor, + mFixedHeightMinor); + } final Context context = getContext(); final int targetSdk = context.getApplicationInfo().targetSdkVersion; diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk index 157405a2168c..22fa7520ee1a 100644 --- a/services/audioflinger/Android.mk +++ b/services/audioflinger/Android.mk @@ -7,6 +7,7 @@ LOCAL_SRC_FILES:= \ AudioMixer.cpp.arm \ AudioResampler.cpp.arm \ AudioPolicyService.cpp \ + AudioBufferProvider.cpp \ ServiceUtilities.cpp # AudioResamplerSinc.cpp.arm # AudioResamplerCubic.cpp.arm @@ -17,6 +18,7 @@ LOCAL_C_INCLUDES := \ LOCAL_SHARED_LIBRARIES := \ libaudioutils \ + libcommon_time_client \ libcutils \ libutils \ libbinder \ diff --git a/services/audioflinger/AudioBufferProvider.cpp b/services/audioflinger/AudioBufferProvider.cpp new file mode 100644 index 000000000000..678fd58b4e47 --- /dev/null +++ b/services/audioflinger/AudioBufferProvider.cpp @@ -0,0 +1,28 @@ +/* + * 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. + */ + +#undef __STRICT_ANSI__ +#define __STDINT_LIMITS +#define __STDC_LIMIT_MACROS +#include <stdint.h> + +#include "AudioBufferProvider.h" + +namespace android { + +const int64_t AudioBufferProvider::kInvalidPTS = INT64_MAX; + +}; // namespace android diff --git a/services/audioflinger/AudioBufferProvider.h b/services/audioflinger/AudioBufferProvider.h index 81c5c3959930..62ad6bd727b8 100644 --- a/services/audioflinger/AudioBufferProvider.h +++ b/services/audioflinger/AudioBufferProvider.h @@ -38,8 +38,15 @@ public: }; virtual ~AudioBufferProvider() {} - - virtual status_t getNextBuffer(Buffer* buffer) = 0; + + // value representing an invalid presentation timestamp + static const int64_t kInvalidPTS; + + // pts is the local time when the next sample yielded by getNextBuffer + // will be rendered. + // Pass kInvalidPTS if the PTS is unknown or not applicable. + virtual status_t getNextBuffer(Buffer* buffer, int64_t pts) = 0; + virtual void releaseBuffer(Buffer* buffer) = 0; }; diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp index 024868729adb..62569512566c 100644 --- a/services/audioflinger/AudioFlinger.cpp +++ b/services/audioflinger/AudioFlinger.cpp @@ -61,6 +61,9 @@ #include <powermanager/PowerManager.h> // #define DEBUG_CPU_USAGE 10 // log statistics every n wall clock seconds +#include <common_time/cc_helper.h> +#include <common_time/local_clock.h> + // ---------------------------------------------------------------------------- @@ -69,7 +72,6 @@ namespace android { static const char kDeadlockedString[] = "AudioFlinger may be deadlocked\n"; static const char kHardwareLockedString[] = "Hardware lock is taken\n"; -//static const nsecs_t kStandbyTimeInNsecs = seconds(3); static const float MAX_GAIN = 4096.0f; static const uint32_t MAX_GAIN_INT = 0x1000; @@ -99,6 +101,7 @@ 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; +nsecs_t AudioFlinger::mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs; // ---------------------------------------------------------------------------- @@ -147,11 +150,14 @@ static const char * const audio_interfaces[] = { AudioFlinger::AudioFlinger() : BnAudioFlinger(), - mPrimaryHardwareDev(NULL), - mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef() - mMasterVolume(1.0f), mMasterMute(false), mNextUniqueId(1), - mMode(AUDIO_MODE_INVALID), - mBtNrecIsOff(false) + mPrimaryHardwareDev(NULL), + mHardwareStatus(AUDIO_HW_IDLE), // see also onFirstRef() + mMasterVolume(1.0f), + mMasterVolumeSupportLvl(MVS_NONE), + mMasterMute(false), + mNextUniqueId(1), + mMode(AUDIO_MODE_INVALID), + mBtNrecIsOff(false) { } @@ -162,6 +168,18 @@ void AudioFlinger::onFirstRef() Mutex::Autolock _l(mLock); /* TODO: move all this work into an Init() function */ + char val_str[PROPERTY_VALUE_MAX] = { 0 }; + if (property_get("ro.audio.flinger_standbytime_ms", val_str, NULL) >= 0) { + uint32_t int_val; + if (1 == sscanf(val_str, "%u", &int_val)) { + mStandbyTimeInNsecs = milliseconds(int_val); + ALOGI("Using %u mSec as standby time.", int_val); + } else { + mStandbyTimeInNsecs = kDefaultStandbyTimeInNsecs; + ALOGI("Using default %u mSec as standby time.", + (uint32_t)(mStandbyTimeInNsecs / 1000000)); + } + } for (size_t i = 0; i < ARRAY_SIZE(audio_interfaces); i++) { const hw_module_t *mod; @@ -193,6 +211,32 @@ void AudioFlinger::onFirstRef() AutoMutex lock(mHardwareLock); + // Determine the level of master volume support the primary audio HAL has, + // and set the initial master volume at the same time. + float initialVolume = 1.0; + mMasterVolumeSupportLvl = MVS_NONE; + if (0 == mPrimaryHardwareDev->init_check(mPrimaryHardwareDev)) { + audio_hw_device_t *dev = mPrimaryHardwareDev; + + mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; + if ((NULL != dev->get_master_volume) && + (NO_ERROR == dev->get_master_volume(dev, &initialVolume))) { + mMasterVolumeSupportLvl = MVS_FULL; + } else { + mMasterVolumeSupportLvl = MVS_SETONLY; + initialVolume = 1.0; + } + + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if ((NULL == dev->set_master_volume) || + (NO_ERROR != dev->set_master_volume(dev, initialVolume))) { + mMasterVolumeSupportLvl = MVS_NONE; + } + mHardwareStatus = AUDIO_HW_INIT; + } + + // Set the mode for each audio HAL, and try to set the initial volume (if + // supported) for all of the non-primary audio HALs. for (size_t i = 0; i < mAudioHwDevs.size(); i++) { audio_hw_device_t *dev = mAudioHwDevs[i]; @@ -203,11 +247,22 @@ void AudioFlinger::onFirstRef() mMode = AUDIO_MODE_NORMAL; // assigned multiple times with same value mHardwareStatus = AUDIO_HW_SET_MODE; dev->set_mode(dev, mMode); - mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; - dev->set_master_volume(dev, 1.0f); - mHardwareStatus = AUDIO_HW_IDLE; + + if ((dev != mPrimaryHardwareDev) && + (NULL != dev->set_master_volume)) { + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + dev->set_master_volume(dev, initialVolume); + } + + mHardwareStatus = AUDIO_HW_INIT; } } + + mMasterVolumeSW = (MVS_NONE == mMasterVolumeSupportLvl) + ? initialVolume + : 1.0; + mMasterVolume = initialVolume; + mHardwareStatus = AUDIO_HW_IDLE; } AudioFlinger::~AudioFlinger() @@ -273,7 +328,10 @@ status_t AudioFlinger::dumpInternals(int fd, const Vector<String16>& args) String8 result; hardware_call_state hardwareStatus = mHardwareStatus; - snprintf(buffer, SIZE, "Hardware status: %d\n", hardwareStatus); + snprintf(buffer, SIZE, "Hardware status: %d\n" + "Standby Time mSec: %u\n", + hardwareStatus, + (uint32_t)(mStandbyTimeInNsecs / 1000000)); result.append(buffer); write(fd, result.string(), result.size()); return NO_ERROR; @@ -377,6 +435,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( uint32_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, + bool isTimed, int *sessionId, status_t *status) { @@ -435,7 +494,7 @@ sp<IAudioTrack> AudioFlinger::createTrack( ALOGV("createTrack() lSessionId: %d", lSessionId); track = thread->createTrack_l(client, streamType, sampleRate, format, - channelMask, frameCount, sharedBuffer, lSessionId, &lStatus); + channelMask, frameCount, sharedBuffer, lSessionId, isTimed, &lStatus); // move effect chain to this output thread if an effect on same session was waiting // for a track to be created @@ -528,20 +587,29 @@ status_t AudioFlinger::setMasterVolume(float value) return PERMISSION_DENIED; } + float swmv = value; + // when hw supports master volume, don't scale in sw mixer - { // scope for the lock - AutoMutex lock(mHardwareLock); - mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; - if (mPrimaryHardwareDev->set_master_volume(mPrimaryHardwareDev, value) == NO_ERROR) { - value = 1.0f; + if (MVS_NONE != mMasterVolumeSupportLvl) { + for (size_t i = 0; i < mAudioHwDevs.size(); i++) { + AutoMutex lock(mHardwareLock); + audio_hw_device_t *dev = mAudioHwDevs[i]; + + mHardwareStatus = AUDIO_HW_SET_MASTER_VOLUME; + if (NULL != dev->set_master_volume) { + dev->set_master_volume(dev, value); + } + mHardwareStatus = AUDIO_HW_IDLE; } - mHardwareStatus = AUDIO_HW_IDLE; + + swmv = 1.0; } Mutex::Autolock _l(mLock); - mMasterVolume = value; + mMasterVolume = value; + mMasterVolumeSW = swmv; for (size_t i = 0; i < mPlaybackThreads.size(); i++) - mPlaybackThreads.valueAt(i)->setMasterVolume(value); + mPlaybackThreads.valueAt(i)->setMasterVolume(swmv); return NO_ERROR; } @@ -635,12 +703,36 @@ float AudioFlinger::masterVolume() const return masterVolume_l(); } +float AudioFlinger::masterVolumeSW() const +{ + Mutex::Autolock _l(mLock); + return masterVolumeSW_l(); +} + bool AudioFlinger::masterMute() const { Mutex::Autolock _l(mLock); return masterMute_l(); } +float AudioFlinger::masterVolume_l() const +{ + if (MVS_FULL == mMasterVolumeSupportLvl) { + float ret_val; + AutoMutex lock(mHardwareLock); + + mHardwareStatus = AUDIO_HW_GET_MASTER_VOLUME; + assert(NULL != mPrimaryHardwareDev); + assert(NULL != mPrimaryHardwareDev->get_master_volume); + + mPrimaryHardwareDev->get_master_volume(mPrimaryHardwareDev, &ret_val); + mHardwareStatus = AUDIO_HW_IDLE; + return ret_val; + } + + return mMasterVolume; +} + status_t AudioFlinger::setStreamVolume(audio_stream_type_t stream, float value, audio_io_handle_t output) { @@ -814,7 +906,7 @@ String8 AudioFlinger::getParameters(audio_io_handle_t ioHandle, const String8& k for (size_t i = 0; i < mAudioHwDevs.size(); i++) { audio_hw_device_t *dev = mAudioHwDevs[i]; char *s = dev->get_parameters(dev, keys.string()); - out_s8 += String8(s); + out_s8 += String8(s ? s : ""); free(s); } return out_s8; @@ -1367,7 +1459,7 @@ AudioFlinger::PlaybackThread::PlaybackThread(const sp<AudioFlinger>& audioFlinge mOutput(output), // Assumes constructor is called by AudioFlinger with it's mLock held, // but it would be safer to explicitly pass initial masterVolume as parameter - mMasterVolume(audioFlinger->masterVolume_l()), + mMasterVolume(audioFlinger->masterVolumeSW_l()), mLastWriteTime(0), mNumWrites(0), mNumDelayedWrites(0), mInWrite(false) { snprintf(mName, kNameLength, "AudioOut_%d", id); @@ -1485,6 +1577,7 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, + bool isTimed, status_t *status) { sp<Track> track; @@ -1535,9 +1628,14 @@ sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTra } } - track = new Track(this, client, streamType, sampleRate, format, - channelMask, frameCount, sharedBuffer, sessionId); - if (track->getCblk() == NULL || track->name() < 0) { + if (!isTimed) { + track = new Track(this, client, streamType, sampleRate, format, + channelMask, frameCount, sharedBuffer, sessionId); + } else { + track = TimedTrack::create(this, client, streamType, sampleRate, format, + channelMask, frameCount, sharedBuffer, sessionId); + } + if (track == NULL || track->getCblk() == NULL || track->name() < 0) { lStatus = NO_MEMORY; goto Exit; } @@ -1941,7 +2039,7 @@ bool AudioFlinger::MixerThread::threadLoop() } } - standbyTime = systemTime() + kStandbyTimeInNsecs; + standbyTime = systemTime() + mStandbyTimeInNsecs; sleepTime = idleSleepTime; sleepTimeShift = 0; continue; @@ -1957,8 +2055,21 @@ bool AudioFlinger::MixerThread::threadLoop() } if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) { + // obtain the presentation timestamp of the next output buffer + int64_t pts; + status_t status = INVALID_OPERATION; + + if (NULL != mOutput->stream->get_next_write_timestamp) { + status = mOutput->stream->get_next_write_timestamp( + mOutput->stream, &pts); + } + + if (status != NO_ERROR) { + pts = AudioBufferProvider::kInvalidPTS; + } + // mix buffers... - mAudioMixer->process(); + mAudioMixer->process(pts); // increase sleep time progressively when application underrun condition clears. // Only increase sleep time if the mixer is ready for two consecutive times to avoid // that a steady state of alternating ready/not ready conditions keeps the sleep time @@ -1967,7 +2078,7 @@ bool AudioFlinger::MixerThread::threadLoop() sleepTimeShift--; } sleepTime = 0; - standbyTime = systemTime() + kStandbyTimeInNsecs; + standbyTime = systemTime() + mStandbyTimeInNsecs; //TODO: delay standby when effects have a tail } else { // If no tracks are ready, sleep once for the duration of an output @@ -2114,7 +2225,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac ALOG_ASSERT(minFrames <= cblk->frameCount); } } - if ((cblk->framesReady() >= minFrames) && track->isReady() && + if ((track->framesReady() >= minFrames) && track->isReady() && !track->isPaused() && !track->isTerminated()) { //ALOGV("track %d u=%08x, s=%08x [OK] on thread %p", name, cblk->user, cblk->server, this); @@ -2184,7 +2295,7 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac uint16_t sendLevel = cblk->getSendLevel_U4_12(); // send level comes from shared memory and so may be corrupt - if (sendLevel >= MAX_GAIN_INT) { + if (sendLevel > MAX_GAIN_INT) { ALOGV("Track send level out of range: %04X", sendLevel); sendLevel = MAX_GAIN_INT; } @@ -2205,25 +2316,21 @@ AudioFlinger::PlaybackThread::mixer_state AudioFlinger::MixerThread::prepareTrac } // Convert volumes from 8.24 to 4.12 format - int16_t left, right, aux; // This additional clamping is needed in case chain->setVolume_l() overshot - uint32_t v_clamped = (vl + (1 << 11)) >> 12; - if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; - left = int16_t(v_clamped); - v_clamped = (vr + (1 << 11)) >> 12; - if (v_clamped > MAX_GAIN_INT) v_clamped = MAX_GAIN_INT; - right = int16_t(v_clamped); + vl = (vl + (1 << 11)) >> 12; + if (vl > MAX_GAIN_INT) vl = MAX_GAIN_INT; + vr = (vr + (1 << 11)) >> 12; + if (vr > MAX_GAIN_INT) vr = MAX_GAIN_INT; - if (va > MAX_GAIN_INT) va = MAX_GAIN_INT; - aux = int16_t(va); + if (va > MAX_GAIN_INT) va = MAX_GAIN_INT; // va is uint32_t, so no need to check for - // XXX: these things DON'T need to be done each time mAudioMixer->setBufferProvider(name, track); mAudioMixer->enable(name); - mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)left); - mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)right); - mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)aux); + mAudioMixer->setParameter(name, param, AudioMixer::VOLUME0, (void *)vl); + mAudioMixer->setParameter(name, param, AudioMixer::VOLUME1, (void *)vr); + mAudioMixer->setParameter(name, param, AudioMixer::AUXLEVEL, (void *)va); mAudioMixer->setParameter( name, AudioMixer::TRACK, @@ -2577,7 +2684,6 @@ bool AudioFlinger::DirectOutputThread::threadLoop() sp<Track> trackToRemove; sp<Track> activeTrack; nsecs_t standbyTime = systemTime(); - int8_t *curBuf; size_t mixBufferSize = mFrameCount*mFrameSize; uint32_t activeSleepTime = activeSleepTimeUs(); uint32_t idleSleepTime = idleSleepTimeUs(); @@ -2781,11 +2887,12 @@ bool AudioFlinger::DirectOutputThread::threadLoop() if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) { AudioBufferProvider::Buffer buffer; size_t frameCount = mFrameCount; - curBuf = (int8_t *)mMixBuffer; + int8_t *curBuf = (int8_t *)mMixBuffer; // output audio to hardware while (frameCount) { buffer.frameCount = frameCount; - activeTrack->getNextBuffer(&buffer); + activeTrack->getNextBuffer(&buffer, + AudioBufferProvider::kInvalidPTS); if (CC_UNLIKELY(buffer.raw == NULL)) { memset(curBuf, 0, frameCount * mFrameSize); break; @@ -3038,7 +3145,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() } } - standbyTime = systemTime() + kStandbyTimeInNsecs; + standbyTime = systemTime() + mStandbyTimeInNsecs; sleepTime = idleSleepTime; continue; } @@ -3055,7 +3162,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() if (CC_LIKELY(mixerStatus == MIXER_TRACKS_READY)) { // mix buffers... if (outputsReady(outputTracks)) { - mAudioMixer->process(); + mAudioMixer->process(AudioBufferProvider::kInvalidPTS); } else { memset(mMixBuffer, 0, mixBufferSize); } @@ -3092,7 +3199,7 @@ bool AudioFlinger::DuplicatingThread::threadLoop() // enable changes in effect chain unlockEffectChains(effectChains); - standbyTime = systemTime() + kStandbyTimeInNsecs; + standbyTime = systemTime() + mStandbyTimeInNsecs; for (size_t i = 0; i < outputTracks.size(); i++) { outputTracks[i]->write(mMixBuffer, writeFrames); } @@ -3124,7 +3231,7 @@ void AudioFlinger::DuplicatingThread::addOutputTrack(MixerThread *thread) { // FIXME explain this formula int frameCount = (3 * mFrameCount * mSampleRate) / thread->sampleRate(); - OutputTrack *outputTrack = new OutputTrack((ThreadBase *)thread, + OutputTrack *outputTrack = new OutputTrack(thread, this, mSampleRate, mFormat, @@ -3142,7 +3249,7 @@ void AudioFlinger::DuplicatingThread::removeOutputTrack(MixerThread *thread) { Mutex::Autolock _l(mLock); for (size_t i = 0; i < mOutputTracks.size(); i++) { - if (mOutputTracks[i]->thread() == (ThreadBase *)thread) { + if (mOutputTracks[i]->thread() == thread) { mOutputTracks[i]->destroy(); mOutputTracks.removeAt(i); updateWaitTime(); @@ -3193,7 +3300,7 @@ uint32_t AudioFlinger::DuplicatingThread::activeSleepTimeUs() // TrackBase constructor must be called with AudioFlinger::mLock held AudioFlinger::ThreadBase::TrackBase::TrackBase( - const wp<ThreadBase>& thread, + ThreadBase *thread, const sp<Client>& client, uint32_t sampleRate, audio_format_t format, @@ -3349,7 +3456,7 @@ void* AudioFlinger::ThreadBase::TrackBase::getBuffer(uint32_t offset, uint32_t f // Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held AudioFlinger::PlaybackThread::Track::Track( - const wp<ThreadBase>& thread, + PlaybackThread *thread, const sp<Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, @@ -3363,11 +3470,9 @@ AudioFlinger::PlaybackThread::Track::Track( mAuxEffectId(0), mHasVolumeController(false) { if (mCblk != NULL) { - sp<ThreadBase> baseThread = thread.promote(); - if (baseThread != 0) { - PlaybackThread *playbackThread = (PlaybackThread *)baseThread.get(); - mName = playbackThread->getTrackName_l(); - mMainBuffer = playbackThread->mixBuffer(); + if (thread != NULL) { + mName = thread->getTrackName_l(); + mMainBuffer = thread->mixBuffer(); } ALOGV("Track constructor name %d, calling pid %d", mName, IPCThreadState::self()->getCallingPid()); if (mName < 0) { @@ -3443,7 +3548,8 @@ void AudioFlinger::PlaybackThread::Track::dump(char* buffer, size_t size) (int)mAuxBuffer); } -status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::PlaybackThread::Track::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesReady; @@ -3484,10 +3590,14 @@ getNextBuffer_exit: return NOT_ENOUGH_DATA; } +uint32_t AudioFlinger::PlaybackThread::Track::framesReady() const{ + return mCblk->framesReady(); +} + bool AudioFlinger::PlaybackThread::Track::isReady() const { if (mFillingUpStatus != FS_FILLING || isStopped() || isPausing()) return true; - if (mCblk->framesReady() >= mCblk->frameCount || + if (framesReady() >= mCblk->frameCount || (mCblk->flags & CBLK_FORCEREADY_MSK)) { mFillingUpStatus = FS_FILLED; android_atomic_and(~CBLK_FORCEREADY_MSK, &mCblk->flags); @@ -3644,11 +3754,402 @@ void AudioFlinger::PlaybackThread::Track::setAuxBuffer(int EffectId, int32_t *bu mAuxBuffer = buffer; } +// timed audio tracks + +sp<AudioFlinger::PlaybackThread::TimedTrack> +AudioFlinger::PlaybackThread::TimedTrack::create( + PlaybackThread *thread, + const sp<Client>& client, + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + uint32_t channelMask, + int frameCount, + const sp<IMemory>& sharedBuffer, + int sessionId) { + if (!client->reserveTimedTrack()) + return NULL; + + sp<TimedTrack> track = new TimedTrack( + thread, client, streamType, sampleRate, format, channelMask, frameCount, + sharedBuffer, sessionId); + + if (track == NULL) { + client->releaseTimedTrack(); + return NULL; + } + + return track; +} + +AudioFlinger::PlaybackThread::TimedTrack::TimedTrack( + PlaybackThread *thread, + const sp<Client>& client, + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + uint32_t channelMask, + int frameCount, + const sp<IMemory>& sharedBuffer, + int sessionId) + : Track(thread, client, streamType, sampleRate, format, channelMask, + frameCount, sharedBuffer, sessionId), + mTimedSilenceBuffer(NULL), + mTimedSilenceBufferSize(0), + mTimedAudioOutputOnTime(false), + mMediaTimeTransformValid(false) +{ + LocalClock lc; + mLocalTimeFreq = lc.getLocalFreq(); + + mLocalTimeToSampleTransform.a_zero = 0; + mLocalTimeToSampleTransform.b_zero = 0; + mLocalTimeToSampleTransform.a_to_b_numer = sampleRate; + mLocalTimeToSampleTransform.a_to_b_denom = mLocalTimeFreq; + LinearTransform::reduce(&mLocalTimeToSampleTransform.a_to_b_numer, + &mLocalTimeToSampleTransform.a_to_b_denom); +} + +AudioFlinger::PlaybackThread::TimedTrack::~TimedTrack() { + mClient->releaseTimedTrack(); + delete [] mTimedSilenceBuffer; +} + +status_t AudioFlinger::PlaybackThread::TimedTrack::allocateTimedBuffer( + size_t size, sp<IMemory>* buffer) { + + Mutex::Autolock _l(mTimedBufferQueueLock); + + trimTimedBufferQueue_l(); + + // lazily initialize the shared memory heap for timed buffers + if (mTimedMemoryDealer == NULL) { + const int kTimedBufferHeapSize = 512 << 10; + + mTimedMemoryDealer = new MemoryDealer(kTimedBufferHeapSize, + "AudioFlingerTimed"); + if (mTimedMemoryDealer == NULL) + return NO_MEMORY; + } + + sp<IMemory> newBuffer = mTimedMemoryDealer->allocate(size); + if (newBuffer == NULL) { + newBuffer = mTimedMemoryDealer->allocate(size); + if (newBuffer == NULL) + return NO_MEMORY; + } + + *buffer = newBuffer; + return NO_ERROR; +} + +// caller must hold mTimedBufferQueueLock +void AudioFlinger::PlaybackThread::TimedTrack::trimTimedBufferQueue_l() { + int64_t mediaTimeNow; + { + Mutex::Autolock mttLock(mMediaTimeTransformLock); + if (!mMediaTimeTransformValid) + return; + + int64_t targetTimeNow; + status_t res = (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) + ? mCCHelper.getCommonTime(&targetTimeNow) + : mCCHelper.getLocalTime(&targetTimeNow); + + if (OK != res) + return; + + if (!mMediaTimeTransform.doReverseTransform(targetTimeNow, + &mediaTimeNow)) { + return; + } + } + + size_t trimIndex; + for (trimIndex = 0; trimIndex < mTimedBufferQueue.size(); trimIndex++) { + if (mTimedBufferQueue[trimIndex].pts() > mediaTimeNow) + break; + } + + if (trimIndex) { + mTimedBufferQueue.removeItemsAt(0, trimIndex); + } +} + +status_t AudioFlinger::PlaybackThread::TimedTrack::queueTimedBuffer( + const sp<IMemory>& buffer, int64_t pts) { + + { + Mutex::Autolock mttLock(mMediaTimeTransformLock); + if (!mMediaTimeTransformValid) + return INVALID_OPERATION; + } + + Mutex::Autolock _l(mTimedBufferQueueLock); + + mTimedBufferQueue.add(TimedBuffer(buffer, pts)); + + return NO_ERROR; +} + +status_t AudioFlinger::PlaybackThread::TimedTrack::setMediaTimeTransform( + const LinearTransform& xform, TimedAudioTrack::TargetTimeline target) { + + ALOGV("%s az=%lld bz=%lld n=%d d=%u tgt=%d", __PRETTY_FUNCTION__, + xform.a_zero, xform.b_zero, xform.a_to_b_numer, xform.a_to_b_denom, + target); + + if (!(target == TimedAudioTrack::LOCAL_TIME || + target == TimedAudioTrack::COMMON_TIME)) { + return BAD_VALUE; + } + + Mutex::Autolock lock(mMediaTimeTransformLock); + mMediaTimeTransform = xform; + mMediaTimeTransformTarget = target; + mMediaTimeTransformValid = true; + + return NO_ERROR; +} + +#define min(a, b) ((a) < (b) ? (a) : (b)) + +// implementation of getNextBuffer for tracks whose buffers have timestamps +status_t AudioFlinger::PlaybackThread::TimedTrack::getNextBuffer( + AudioBufferProvider::Buffer* buffer, int64_t pts) +{ + if (pts == AudioBufferProvider::kInvalidPTS) { + buffer->raw = 0; + buffer->frameCount = 0; + return INVALID_OPERATION; + } + + Mutex::Autolock _l(mTimedBufferQueueLock); + + while (true) { + + // if we have no timed buffers, then fail + if (mTimedBufferQueue.isEmpty()) { + buffer->raw = 0; + buffer->frameCount = 0; + return NOT_ENOUGH_DATA; + } + + TimedBuffer& head = mTimedBufferQueue.editItemAt(0); + + // calculate the PTS of the head of the timed buffer queue expressed in + // local time + int64_t headLocalPTS; + { + Mutex::Autolock mttLock(mMediaTimeTransformLock); + + assert(mMediaTimeTransformValid); + + if (mMediaTimeTransform.a_to_b_denom == 0) { + // the transform represents a pause, so yield silence + timedYieldSilence(buffer->frameCount, buffer); + return NO_ERROR; + } + + int64_t transformedPTS; + if (!mMediaTimeTransform.doForwardTransform(head.pts(), + &transformedPTS)) { + // the transform failed. this shouldn't happen, but if it does + // then just drop this buffer + ALOGW("timedGetNextBuffer transform failed"); + buffer->raw = 0; + buffer->frameCount = 0; + mTimedBufferQueue.removeAt(0); + return NO_ERROR; + } + + if (mMediaTimeTransformTarget == TimedAudioTrack::COMMON_TIME) { + if (OK != mCCHelper.commonTimeToLocalTime(transformedPTS, + &headLocalPTS)) { + buffer->raw = 0; + buffer->frameCount = 0; + return INVALID_OPERATION; + } + } else { + headLocalPTS = transformedPTS; + } + } + + // adjust the head buffer's PTS to reflect the portion of the head buffer + // that has already been consumed + int64_t effectivePTS = headLocalPTS + + ((head.position() / mCblk->frameSize) * mLocalTimeFreq / sampleRate()); + + // Calculate the delta in samples between the head of the input buffer + // queue and the start of the next output buffer that will be written. + // If the transformation fails because of over or underflow, it means + // that the sample's position in the output stream is so far out of + // whack that it should just be dropped. + int64_t sampleDelta; + if (llabs(effectivePTS - pts) >= (static_cast<int64_t>(1) << 31)) { + ALOGV("*** head buffer is too far from PTS: dropped buffer"); + mTimedBufferQueue.removeAt(0); + continue; + } + if (!mLocalTimeToSampleTransform.doForwardTransform( + (effectivePTS - pts) << 32, &sampleDelta)) { + ALOGV("*** too late during sample rate transform: dropped buffer"); + mTimedBufferQueue.removeAt(0); + continue; + } + + ALOGV("*** %s head.pts=%lld head.pos=%d pts=%lld sampleDelta=[%d.%08x]", + __PRETTY_FUNCTION__, head.pts(), head.position(), pts, + static_cast<int32_t>((sampleDelta >= 0 ? 0 : 1) + (sampleDelta >> 32)), + static_cast<uint32_t>(sampleDelta & 0xFFFFFFFF)); + + // if the delta between the ideal placement for the next input sample and + // the current output position is within this threshold, then we will + // concatenate the next input samples to the previous output + const int64_t kSampleContinuityThreshold = + (static_cast<int64_t>(sampleRate()) << 32) / 10; + + // if this is the first buffer of audio that we're emitting from this track + // then it should be almost exactly on time. + const int64_t kSampleStartupThreshold = 1LL << 32; + + if ((mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleContinuityThreshold) || + (!mTimedAudioOutputOnTime && llabs(sampleDelta) <= kSampleStartupThreshold)) { + // the next input is close enough to being on time, so concatenate it + // with the last output + timedYieldSamples(buffer); + + ALOGV("*** on time: head.pos=%d frameCount=%u", head.position(), buffer->frameCount); + return NO_ERROR; + } else if (sampleDelta > 0) { + // the gap between the current output position and the proper start of + // the next input sample is too big, so fill it with silence + uint32_t framesUntilNextInput = (sampleDelta + 0x80000000) >> 32; + + timedYieldSilence(framesUntilNextInput, buffer); + ALOGV("*** silence: frameCount=%u", buffer->frameCount); + return NO_ERROR; + } else { + // the next input sample is late + uint32_t lateFrames = static_cast<uint32_t>(-((sampleDelta + 0x80000000) >> 32)); + size_t onTimeSamplePosition = + head.position() + lateFrames * mCblk->frameSize; + + if (onTimeSamplePosition > head.buffer()->size()) { + // all the remaining samples in the head are too late, so + // drop it and move on + ALOGV("*** too late: dropped buffer"); + mTimedBufferQueue.removeAt(0); + continue; + } else { + // skip over the late samples + head.setPosition(onTimeSamplePosition); + + // yield the available samples + timedYieldSamples(buffer); + + ALOGV("*** late: head.pos=%d frameCount=%u", head.position(), buffer->frameCount); + return NO_ERROR; + } + } + } +} + +// Yield samples from the timed buffer queue head up to the given output +// buffer's capacity. +// +// Caller must hold mTimedBufferQueueLock +void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSamples( + AudioBufferProvider::Buffer* buffer) { + + const TimedBuffer& head = mTimedBufferQueue[0]; + + buffer->raw = (static_cast<uint8_t*>(head.buffer()->pointer()) + + head.position()); + + uint32_t framesLeftInHead = ((head.buffer()->size() - head.position()) / + mCblk->frameSize); + size_t framesRequested = buffer->frameCount; + buffer->frameCount = min(framesLeftInHead, framesRequested); + + mTimedAudioOutputOnTime = true; +} + +// Yield samples of silence up to the given output buffer's capacity +// +// Caller must hold mTimedBufferQueueLock +void AudioFlinger::PlaybackThread::TimedTrack::timedYieldSilence( + uint32_t numFrames, AudioBufferProvider::Buffer* buffer) { + + // lazily allocate a buffer filled with silence + if (mTimedSilenceBufferSize < numFrames * mCblk->frameSize) { + delete [] mTimedSilenceBuffer; + mTimedSilenceBufferSize = numFrames * mCblk->frameSize; + mTimedSilenceBuffer = new uint8_t[mTimedSilenceBufferSize]; + memset(mTimedSilenceBuffer, 0, mTimedSilenceBufferSize); + } + + buffer->raw = mTimedSilenceBuffer; + size_t framesRequested = buffer->frameCount; + buffer->frameCount = min(numFrames, framesRequested); + + mTimedAudioOutputOnTime = false; +} + +void AudioFlinger::PlaybackThread::TimedTrack::releaseBuffer( + AudioBufferProvider::Buffer* buffer) { + + Mutex::Autolock _l(mTimedBufferQueueLock); + + // If the buffer which was just released is part of the buffer at the head + // of the queue, be sure to update the amt of the buffer which has been + // consumed. If the buffer being returned is not part of the head of the + // queue, its either because the buffer is part of the silence buffer, or + // because the head of the timed queue was trimmed after the mixer called + // getNextBuffer but before the mixer called releaseBuffer. + if ((buffer->raw != mTimedSilenceBuffer) && mTimedBufferQueue.size()) { + TimedBuffer& head = mTimedBufferQueue.editItemAt(0); + + void* start = head.buffer()->pointer(); + void* end = (char *) head.buffer()->pointer() + head.buffer()->size(); + + if ((buffer->raw >= start) && (buffer->raw <= end)) { + head.setPosition(head.position() + + (buffer->frameCount * mCblk->frameSize)); + if (static_cast<size_t>(head.position()) >= head.buffer()->size()) { + mTimedBufferQueue.removeAt(0); + } + } + } + + buffer->raw = 0; + buffer->frameCount = 0; +} + +uint32_t AudioFlinger::PlaybackThread::TimedTrack::framesReady() const { + Mutex::Autolock _l(mTimedBufferQueueLock); + + uint32_t frames = 0; + for (size_t i = 0; i < mTimedBufferQueue.size(); i++) { + const TimedBuffer& tb = mTimedBufferQueue[i]; + frames += (tb.buffer()->size() - tb.position()) / mCblk->frameSize; + } + + return frames; +} + +AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer() + : mPTS(0), mPosition(0) {} + +AudioFlinger::PlaybackThread::TimedTrack::TimedBuffer::TimedBuffer( + const sp<IMemory>& buffer, int64_t pts) + : mBuffer(buffer), mPTS(pts), mPosition(0) {} + // ---------------------------------------------------------------------------- // RecordTrack constructor must be called with AudioFlinger::mLock held AudioFlinger::RecordThread::RecordTrack::RecordTrack( - const wp<ThreadBase>& thread, + RecordThread *thread, const sp<Client>& client, uint32_t sampleRate, audio_format_t format, @@ -3680,7 +4181,7 @@ AudioFlinger::RecordThread::RecordTrack::~RecordTrack() } } -status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::RecordThread::RecordTrack::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts) { audio_track_cblk_t* cblk = this->cblk(); uint32_t framesAvail; @@ -3761,17 +4262,16 @@ void AudioFlinger::RecordThread::RecordTrack::dump(char* buffer, size_t size) // ---------------------------------------------------------------------------- AudioFlinger::PlaybackThread::OutputTrack::OutputTrack( - const wp<ThreadBase>& thread, + PlaybackThread *playbackThread, DuplicatingThread *sourceThread, uint32_t sampleRate, audio_format_t format, uint32_t channelMask, int frameCount) - : Track(thread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0), + : Track(playbackThread, NULL, AUDIO_STREAM_CNT, sampleRate, format, channelMask, frameCount, NULL, 0), mActive(false), mSourceThread(sourceThread) { - PlaybackThread *playbackThread = (PlaybackThread *)thread.unsafe_get(); if (mCblk != NULL) { mCblk->flags |= CBLK_DIRECTION_OUT; mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t); @@ -3985,10 +4485,9 @@ status_t AudioFlinger::PlaybackThread::OutputTrack::obtainBuffer(AudioBufferProv void AudioFlinger::PlaybackThread::OutputTrack::clearBufferQueue() { size_t size = mBufferQueue.size(); - Buffer *pBuffer; for (size_t i = 0; i < size; i++) { - pBuffer = mBufferQueue.itemAt(i); + Buffer *pBuffer = mBufferQueue.itemAt(i); delete [] pBuffer->mBuffer; delete pBuffer; } @@ -4002,7 +4501,8 @@ AudioFlinger::Client::Client(const sp<AudioFlinger>& audioFlinger, pid_t pid) mAudioFlinger(audioFlinger), // FIXME should be a "k" constant not hard-coded, in .h or ro. property, see 4 lines below mMemoryDealer(new MemoryDealer(1024*1024, "AudioFlinger::Client")), - mPid(pid) + mPid(pid), + mTimedTrackCount(0) { // 1 MB of address space is good for 32 tracks, 8 buffers each, 4 KB/buffer } @@ -4018,6 +4518,31 @@ sp<MemoryDealer> AudioFlinger::Client::heap() const return mMemoryDealer; } +// Reserve one of the limited slots for a timed audio track associated +// with this client +bool AudioFlinger::Client::reserveTimedTrack() +{ + const int kMaxTimedTracksPerClient = 4; + + Mutex::Autolock _l(mTimedTrackLock); + + if (mTimedTrackCount >= kMaxTimedTracksPerClient) { + ALOGW("can not create timed track - pid %d has exceeded the limit", + mPid); + return false; + } + + mTimedTrackCount++; + return true; +} + +// Release a slot for a timed audio track +void AudioFlinger::Client::releaseTimedTrack() +{ + Mutex::Autolock _l(mTimedTrackLock); + mTimedTrackCount--; +} + // ---------------------------------------------------------------------------- AudioFlinger::NotificationClient::NotificationClient(const sp<AudioFlinger>& audioFlinger, @@ -4034,9 +4559,7 @@ AudioFlinger::NotificationClient::~NotificationClient() void AudioFlinger::NotificationClient::binderDied(const wp<IBinder>& who) { sp<NotificationClient> keep(this); - { - mAudioFlinger->removeNotificationClient(mPid); - } + mAudioFlinger->removeNotificationClient(mPid); } // ---------------------------------------------------------------------------- @@ -4084,6 +4607,38 @@ status_t AudioFlinger::TrackHandle::attachAuxEffect(int EffectId) return mTrack->attachAuxEffect(EffectId); } +status_t AudioFlinger::TrackHandle::allocateTimedBuffer(size_t size, + sp<IMemory>* buffer) { + if (!mTrack->isTimedTrack()) + return INVALID_OPERATION; + + PlaybackThread::TimedTrack* tt = + reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get()); + return tt->allocateTimedBuffer(size, buffer); +} + +status_t AudioFlinger::TrackHandle::queueTimedBuffer(const sp<IMemory>& buffer, + int64_t pts) { + if (!mTrack->isTimedTrack()) + return INVALID_OPERATION; + + PlaybackThread::TimedTrack* tt = + reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get()); + return tt->queueTimedBuffer(buffer, pts); +} + +status_t AudioFlinger::TrackHandle::setMediaTimeTransform( + const LinearTransform& xform, int target) { + + if (!mTrack->isTimedTrack()) + return INVALID_OPERATION; + + PlaybackThread::TimedTrack* tt = + reinterpret_cast<PlaybackThread::TimedTrack*>(mTrack.get()); + return tt->setMediaTimeTransform( + xform, static_cast<TimedAudioTrack::TargetTimeline>(target)); +} + status_t AudioFlinger::TrackHandle::onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags) { @@ -4313,7 +4868,8 @@ bool AudioFlinger::RecordThread::threadLoop() } buffer.frameCount = mFrameCount; - if (CC_LIKELY(mActiveTrack->getNextBuffer(&buffer) == NO_ERROR)) { + if (CC_LIKELY(mActiveTrack->getNextBuffer( + &buffer, AudioBufferProvider::kInvalidPTS) == NO_ERROR)) { size_t framesOut = buffer.frameCount; if (mResampler == NULL) { // no resampling @@ -4591,7 +5147,7 @@ status_t AudioFlinger::RecordThread::dump(int fd, const Vector<String16>& args) return NO_ERROR; } -status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer) +status_t AudioFlinger::RecordThread::getNextBuffer(AudioBufferProvider::Buffer* buffer, int64_t pts) { size_t framesReq = buffer->frameCount; size_t framesReady = mFrameCount - mRsmpInIndex; @@ -4979,8 +5535,7 @@ status_t AudioFlinger::closeOutput(audio_io_handle_t output) } } } - void *param2 = NULL; - audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, param2); + audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL); mPlaybackThreads.removeItem(output); } thread->exit(); @@ -5124,8 +5679,7 @@ status_t AudioFlinger::closeInput(audio_io_handle_t input) } ALOGV("closeInput() %d", input); - void *param2 = NULL; - audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, param2); + audioConfigChanged_l(AudioSystem::INPUT_CLOSED, input, NULL); mRecordThreads.removeItem(input); } thread->exit(); @@ -5157,8 +5711,7 @@ status_t AudioFlinger::setStreamOutput(audio_stream_type_t stream, audio_io_hand for (size_t i = 0; i < mPlaybackThreads.size(); i++) { PlaybackThread *thread = mPlaybackThreads.valueAt(i).get(); - if (thread != dstThread && - thread->type() != ThreadBase::DIRECT) { + if (thread != dstThread && thread->type() != ThreadBase::DIRECT) { MixerThread *srcThread = (MixerThread *)thread; srcThread->setStreamValid(stream, false); srcThread->invalidateTracks(stream); @@ -5281,33 +5834,20 @@ void AudioFlinger::purgeStaleEffects_l() { // checkPlaybackThread_l() must be called with AudioFlinger::mLock held AudioFlinger::PlaybackThread *AudioFlinger::checkPlaybackThread_l(audio_io_handle_t output) const { - PlaybackThread *thread = NULL; - if (mPlaybackThreads.indexOfKey(output) >= 0) { - thread = (PlaybackThread *)mPlaybackThreads.valueFor(output).get(); - } - return thread; + return mPlaybackThreads.valueFor(output).get(); } // checkMixerThread_l() must be called with AudioFlinger::mLock held AudioFlinger::MixerThread *AudioFlinger::checkMixerThread_l(audio_io_handle_t output) const { PlaybackThread *thread = checkPlaybackThread_l(output); - if (thread != NULL) { - if (thread->type() == ThreadBase::DIRECT) { - thread = NULL; - } - } - return (MixerThread *)thread; + return thread != NULL && thread->type() != ThreadBase::DIRECT ? (MixerThread *) thread : NULL; } // checkRecordThread_l() must be called with AudioFlinger::mLock held AudioFlinger::RecordThread *AudioFlinger::checkRecordThread_l(audio_io_handle_t input) const { - RecordThread *thread = NULL; - if (mRecordThreads.indexOfKey(input) >= 0) { - thread = (RecordThread *)mRecordThreads.valueFor(input).get(); - } - return thread; + return mRecordThreads.valueFor(input).get(); } uint32_t AudioFlinger::nextUniqueId() @@ -5670,10 +6210,7 @@ sp<AudioFlinger::EffectHandle> AudioFlinger::ThreadBase::createEffect_l( goto Exit; } // Only Pre processor effects are allowed on input threads and only on input threads - if ((mType == RECORD && - (desc->flags & EFFECT_FLAG_TYPE_MASK) != EFFECT_FLAG_TYPE_PRE_PROC) || - (mType != RECORD && - (desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) { + if ((mType == RECORD) != ((desc->flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_PRE_PROC)) { ALOGW("createEffect_l() effect %s (flags %08x) created on wrong thread type %d", desc->name, desc->flags, mType); lStatus = BAD_VALUE; @@ -6046,18 +6583,17 @@ size_t AudioFlinger::RecordThread::removeEffectChain_l(const sp<EffectChain>& ch #undef LOG_TAG #define LOG_TAG "AudioFlinger::EffectModule" -AudioFlinger::EffectModule::EffectModule(const wp<ThreadBase>& wThread, +AudioFlinger::EffectModule::EffectModule(ThreadBase *thread, const wp<AudioFlinger::EffectChain>& chain, effect_descriptor_t *desc, int id, int sessionId) - : mThread(wThread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL), + : mThread(thread), mChain(chain), mId(id), mSessionId(sessionId), mEffectInterface(NULL), mStatus(NO_INIT), mState(IDLE), mSuspended(false) { ALOGV("Constructor %p", this); int lStatus; - sp<ThreadBase> thread = mThread.promote(); - if (thread == 0) { + if (thread == NULL) { return; } @@ -7027,15 +7563,14 @@ void AudioFlinger::EffectHandle::dump(char* buffer, size_t size) #undef LOG_TAG #define LOG_TAG "AudioFlinger::EffectChain" -AudioFlinger::EffectChain::EffectChain(const wp<ThreadBase>& wThread, +AudioFlinger::EffectChain::EffectChain(ThreadBase *thread, int sessionId) - : mThread(wThread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0), + : mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0), mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX), mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX) { mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC); - sp<ThreadBase> thread = mThread.promote(); - if (thread == 0) { + if (thread == NULL) { return; } mMaxTailBuffers = ((kProcessTailDurationMs * thread->sampleRate()) / 1000) / diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h index aa0b8f8575ac..1a52de5b67d7 100644 --- a/services/audioflinger/AudioFlinger.h +++ b/services/audioflinger/AudioFlinger.h @@ -22,11 +22,14 @@ #include <sys/types.h> #include <limits.h> +#include <common_time/cc_helper.h> + #include <media/IAudioFlinger.h> #include <media/IAudioFlingerClient.h> #include <media/IAudioTrack.h> #include <media/IAudioRecord.h> #include <media/AudioSystem.h> +#include <media/AudioTrack.h> #include <utils/Atomic.h> #include <utils/Errors.h> @@ -55,7 +58,7 @@ class AudioResampler; // ---------------------------------------------------------------------------- -static const nsecs_t kStandbyTimeInNsecs = seconds(3); +static const nsecs_t kDefaultStandbyTimeInNsecs = seconds(3); class AudioFlinger : public BinderService<AudioFlinger>, @@ -78,6 +81,7 @@ public: uint32_t flags, const sp<IMemory>& sharedBuffer, audio_io_handle_t output, + bool isTimed, int *sessionId, status_t *status); @@ -102,6 +106,7 @@ public: virtual status_t setMasterMute(bool muted); virtual float masterVolume() const; + virtual float masterVolumeSW() const; virtual bool masterMute() const; virtual status_t setStreamVolume(audio_stream_type_t stream, float value, @@ -206,6 +211,8 @@ private: audio_hw_device_t* findSuitableHwDev_l(uint32_t devices); void purgeStaleEffects_l(); + static nsecs_t mStandbyTimeInNsecs; + // Internal dump utilites. status_t dumpPermissionDenial(int fd, const Vector<String16>& args); status_t dumpClients(int fd, const Vector<String16>& args); @@ -220,12 +227,18 @@ private: pid_t pid() const { return mPid; } sp<AudioFlinger> audioFlinger() const { return mAudioFlinger; } + bool reserveTimedTrack(); + void releaseTimedTrack(); + private: Client(const Client&); Client& operator = (const Client&); const sp<AudioFlinger> mAudioFlinger; const sp<MemoryDealer> mMemoryDealer; const pid_t mPid; + + Mutex mTimedTrackLock; + int mTimedTrackCount; }; // --- Notification Client --- @@ -305,7 +318,7 @@ private: // The upper 16 bits are used for track-specific flags. }; - TrackBase(const wp<ThreadBase>& thread, + TrackBase(ThreadBase *thread, const sp<Client>& client, uint32_t sampleRate, audio_format_t format, @@ -333,7 +346,9 @@ private: TrackBase(const TrackBase&); TrackBase& operator = (const TrackBase&); - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer) = 0; + virtual status_t getNextBuffer( + AudioBufferProvider::Buffer* buffer, + int64_t pts) = 0; virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); audio_format_t format() const { @@ -576,7 +591,7 @@ private: // playback track class Track : public TrackBase { public: - Track( const wp<ThreadBase>& thread, + Track( PlaybackThread *thread, const sp<Client>& client, audio_stream_type_t streamType, uint32_t sampleRate, @@ -609,7 +624,6 @@ private: int16_t *mainBuffer() const { return mMainBuffer; } int auxEffectId() const { return mAuxEffectId; } - protected: friend class ThreadBase; friend class TrackHandle; @@ -620,7 +634,11 @@ private: Track(const Track&); Track& operator = (const Track&); - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + virtual status_t getNextBuffer( + AudioBufferProvider::Buffer* buffer, + int64_t pts); + virtual uint32_t framesReady() const; + bool isMuted() const { return mMute; } bool isPausing() const { return mState == PAUSING; @@ -636,6 +654,8 @@ private: return (mStreamType == AUDIO_STREAM_CNT); } + virtual bool isTimedTrack() const { return false; } + // we don't really need a lock for these volatile bool mMute; // FILLED state is used for suppressing volume ramp at begin of playing @@ -652,6 +672,79 @@ private: bool mHasVolumeController; }; // end of Track + class TimedTrack : public Track { + public: + static sp<TimedTrack> create(PlaybackThread *thread, + const sp<Client>& client, + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + uint32_t channelMask, + int frameCount, + const sp<IMemory>& sharedBuffer, + int sessionId); + ~TimedTrack(); + + class TimedBuffer { + public: + TimedBuffer(); + TimedBuffer(const sp<IMemory>& buffer, int64_t pts); + const sp<IMemory>& buffer() const { return mBuffer; } + int64_t pts() const { return mPTS; } + int position() const { return mPosition; } + void setPosition(int pos) { mPosition = pos; } + private: + sp<IMemory> mBuffer; + int64_t mPTS; + int mPosition; + }; + + virtual bool isTimedTrack() const { return true; } + + virtual uint32_t framesReady() const; + + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, + int64_t pts); + virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); + void timedYieldSamples(AudioBufferProvider::Buffer* buffer); + void timedYieldSilence(uint32_t numFrames, + AudioBufferProvider::Buffer* buffer); + + status_t allocateTimedBuffer(size_t size, + sp<IMemory>* buffer); + status_t queueTimedBuffer(const sp<IMemory>& buffer, + int64_t pts); + status_t setMediaTimeTransform(const LinearTransform& xform, + TimedAudioTrack::TargetTimeline target); + void trimTimedBufferQueue_l(); + + private: + TimedTrack(PlaybackThread *thread, + const sp<Client>& client, + audio_stream_type_t streamType, + uint32_t sampleRate, + audio_format_t format, + uint32_t channelMask, + int frameCount, + const sp<IMemory>& sharedBuffer, + int sessionId); + + uint64_t mLocalTimeFreq; + LinearTransform mLocalTimeToSampleTransform; + sp<MemoryDealer> mTimedMemoryDealer; + Vector<TimedBuffer> mTimedBufferQueue; + uint8_t* mTimedSilenceBuffer; + uint32_t mTimedSilenceBufferSize; + mutable Mutex mTimedBufferQueueLock; + bool mTimedAudioOutputOnTime; + CCHelper mCCHelper; + + Mutex mMediaTimeTransformLock; + LinearTransform mMediaTimeTransform; + bool mMediaTimeTransformValid; + TimedAudioTrack::TargetTimeline mMediaTimeTransformTarget; + }; + // playback track class OutputTrack : public Track { @@ -662,7 +755,7 @@ private: int16_t *mBuffer; }; - OutputTrack( const wp<ThreadBase>& thread, + OutputTrack(PlaybackThread *thread, DuplicatingThread *sourceThread, uint32_t sampleRate, audio_format_t format, @@ -673,7 +766,7 @@ private: virtual status_t start(pid_t tid); virtual void stop(); bool write(int16_t* data, uint32_t frames); - bool bufferQueueEmpty() const { return (mBufferQueue.size() == 0) ? true : false; } + bool bufferQueueEmpty() const { return mBufferQueue.size() == 0; } bool isActive() const { return mActive; } const wp<ThreadBase>& thread() const { return mThread; } @@ -726,6 +819,7 @@ private: int frameCount, const sp<IMemory>& sharedBuffer, int sessionId, + bool isTimed, status_t *status); AudioStreamOut* getOutput() const; @@ -900,8 +994,8 @@ private: uint32_t nextUniqueId(); status_t moveEffectChain_l(int sessionId, - AudioFlinger::PlaybackThread *srcThread, - AudioFlinger::PlaybackThread *dstThread, + PlaybackThread *srcThread, + PlaybackThread *dstThread, bool reRegister); PlaybackThread *primaryPlaybackThread_l(); uint32_t primaryOutputDevice_l(); @@ -920,6 +1014,12 @@ private: virtual void mute(bool); virtual void pause(); virtual status_t attachAuxEffect(int effectId); + virtual status_t allocateTimedBuffer(size_t size, + sp<IMemory>* buffer); + virtual status_t queueTimedBuffer(const sp<IMemory>& buffer, + int64_t pts); + virtual status_t setMediaTimeTransform(const LinearTransform& xform, + int target); virtual status_t onTransact( uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags); private: @@ -942,7 +1042,7 @@ private: // record track class RecordTrack : public TrackBase { public: - RecordTrack(const wp<ThreadBase>& thread, + RecordTrack(RecordThread *thread, const sp<Client>& client, uint32_t sampleRate, audio_format_t format, @@ -967,7 +1067,9 @@ private: RecordTrack(const RecordTrack&); RecordTrack& operator = (const RecordTrack&); - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + virtual status_t getNextBuffer( + AudioBufferProvider::Buffer* buffer, + int64_t pts); bool mOverflow; }; @@ -1004,7 +1106,8 @@ private: AudioStreamIn* clearInput(); virtual audio_stream_t* stream(); - virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer); + virtual status_t getNextBuffer(AudioBufferProvider::Buffer* buffer, + int64_t pts); virtual void releaseBuffer(AudioBufferProvider::Buffer* buffer); virtual bool checkForNewParameters_l(); virtual String8 getParameters(const String8& keys); @@ -1065,7 +1168,7 @@ private: // the attached track(s) to accumulate their auxiliary channel. class EffectModule: public RefBase { public: - EffectModule(const wp<ThreadBase>& wThread, + EffectModule(ThreadBase *thread, const wp<AudioFlinger::EffectChain>& chain, effect_descriptor_t *desc, int id, @@ -1250,6 +1353,7 @@ mutable Mutex mLock; // mutex for process, commands and handl class EffectChain: public RefBase { public: EffectChain(const wp<ThreadBase>& wThread, int sessionId); + EffectChain(ThreadBase *thread, int sessionId); virtual ~EffectChain(); // special key used for an entry in mSuspendedEffects keyed vector @@ -1401,6 +1505,28 @@ mutable Mutex mLock; // mutex for process, commands and handl friend class RecordThread; friend class PlaybackThread; + enum master_volume_support { + // MVS_NONE: + // Audio HAL has no support for master volume, either setting or + // getting. All master volume control must be implemented in SW by the + // AudioFlinger mixing core. + MVS_NONE, + + // MVS_SETONLY: + // Audio HAL has support for setting master volume, but not for getting + // master volume (original HAL design did not include a getter). + // AudioFlinger needs to keep track of the last set master volume in + // addition to needing to set an initial, default, master volume at HAL + // load time. + MVS_SETONLY, + + // MVS_FULL: + // Audio HAL has support both for setting and getting master volume. + // AudioFlinger should send all set and get master volume requests + // directly to the HAL. + MVS_FULL, + }; + mutable Mutex mLock; DefaultKeyedVector< pid_t, wp<Client> > mClients; // see ~Client() @@ -1429,6 +1555,7 @@ mutable Mutex mLock; // mutex for process, commands and handl AUDIO_SET_VOICE_VOLUME, AUDIO_SET_PARAMETER, AUDIO_HW_GET_INPUT_BUFFER_SIZE, + AUDIO_HW_GET_MASTER_VOLUME, }; mutable hardware_call_state mHardwareStatus; // for dump only @@ -1439,6 +1566,8 @@ mutable Mutex mLock; // mutex for process, commands and handl // both are protected by mLock float mMasterVolume; + float mMasterVolumeSW; + master_volume_support mMasterVolumeSupportLvl; bool mMasterMute; DefaultKeyedVector< audio_io_handle_t, sp<RecordThread> > mRecordThreads; @@ -1451,7 +1580,8 @@ mutable Mutex mLock; // mutex for process, commands and handl // protected by mLock Vector<AudioSessionRef*> mAudioSessionRefs; - float masterVolume_l() const { return mMasterVolume; } + float masterVolume_l() const; + float masterVolumeSW_l() const { return mMasterVolumeSW; } bool masterMute_l() const { return mMasterMute; } private: diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp index cb7678b23e71..020d62afd99b 100644 --- a/services/audioflinger/AudioMixer.cpp +++ b/services/audioflinger/AudioMixer.cpp @@ -33,6 +33,8 @@ #include <system/audio.h> #include <audio_utils/primitives.h> +#include <common_time/local_clock.h> +#include <common_time/cc_helper.h> #include "AudioMixer.h" @@ -45,6 +47,9 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) { // AudioMixer is not yet capable of multi-channel beyond stereo assert(2 == MAX_NUM_CHANNELS); + + LocalClock lc; + mState.enabledTracks= 0; mState.needsChanged = 0; mState.frameCount = frameCount; @@ -80,6 +85,7 @@ AudioMixer::AudioMixer(size_t frameCount, uint32_t sampleRate) t->sampleRate = mSampleRate; t->mainBuffer = NULL; t->auxBuffer = NULL; + t->localTimeFreq = lc.getLocalFreq(); t++; } } @@ -251,6 +257,7 @@ void AudioMixer::setParameter(int name, int target, int param, void *value) } break; case AUXLEVEL: + //assert(0 <= valueInt && valueInt <= MAX_GAIN_INT); if (track.auxLevel != valueInt) { ALOGV("setParameter(VOLUME, AUXLEVEL: %04x)", valueInt); track.prevAuxLevel = track.auxLevel << 16; @@ -289,6 +296,7 @@ bool AudioMixer::track_t::setResampler(uint32_t value, uint32_t devSampleRate) if (resampler == NULL) { resampler = AudioResampler::create( format, channelCount, devSampleRate); + resampler->setLocalTimeFreq(localTimeFreq); } return true; } @@ -333,13 +341,13 @@ void AudioMixer::setBufferProvider(int name, AudioBufferProvider* buffer) -void AudioMixer::process() +void AudioMixer::process(int64_t pts) { - mState.hook(&mState); + mState.hook(&mState, pts); } -void AudioMixer::process__validate(state_t* state) +void AudioMixer::process__validate(state_t* state, int64_t pts) { ALOGW_IF(!state->needsChanged, "in process__validate() but nothing's invalid"); @@ -443,7 +451,7 @@ void AudioMixer::process__validate(state_t* state) countActiveTracks, state->enabledTracks, all16BitsStereoNoResample, resampling, volumeRamp); - state->hook(state); + state->hook(state, pts); // Now that the volume ramp has been done, set optimal state and // track hooks for subsequent mixer process @@ -549,7 +557,7 @@ void AudioMixer::volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, i } t->prevVolume[0] = vl; t->prevVolume[1] = vr; - t->adjustVolumeRamp((aux != NULL)); + t->adjustVolumeRamp(aux != NULL); } void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux) @@ -558,7 +566,7 @@ void AudioMixer::volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32 const int16_t vr = t->volume[1]; if (CC_UNLIKELY(aux != NULL)) { - const int16_t va = (int16_t)t->auxLevel; + const int16_t va = t->auxLevel; do { int16_t l = (int16_t)(*temp++ >> 12); int16_t r = (int16_t)(*temp++ >> 12); @@ -757,7 +765,7 @@ void AudioMixer::track__16BitsMono(track_t* t, int32_t* out, size_t frameCount, } // no-op case -void AudioMixer::process__nop(state_t* state) +void AudioMixer::process__nop(state_t* state, int64_t pts) { uint32_t e0 = state->enabledTracks; size_t bufSize = state->frameCount * sizeof(int16_t) * MAX_NUM_CHANNELS; @@ -787,7 +795,9 @@ void AudioMixer::process__nop(state_t* state) size_t outFrames = state->frameCount; while (outFrames) { t1.buffer.frameCount = outFrames; - t1.bufferProvider->getNextBuffer(&t1.buffer); + int64_t outputPTS = calculateOutputPTS( + t1, pts, state->frameCount - outFrames); + t1.bufferProvider->getNextBuffer(&t1.buffer, outputPTS); if (t1.buffer.raw == NULL) break; outFrames -= t1.buffer.frameCount; t1.bufferProvider->releaseBuffer(&t1.buffer); @@ -797,7 +807,7 @@ void AudioMixer::process__nop(state_t* state) } // generic code without resampling -void AudioMixer::process__genericNoResampling(state_t* state) +void AudioMixer::process__genericNoResampling(state_t* state, int64_t pts) { int32_t outTemp[BLOCKSIZE * MAX_NUM_CHANNELS] __attribute__((aligned(32))); @@ -809,7 +819,7 @@ void AudioMixer::process__genericNoResampling(state_t* state) e0 &= ~(1<<i); track_t& t = state->tracks[i]; t.buffer.frameCount = state->frameCount; - t.bufferProvider->getNextBuffer(&t.buffer); + t.bufferProvider->getNextBuffer(&t.buffer, pts); t.frameCount = t.buffer.frameCount; t.in = t.buffer.raw; // t.in == NULL can happen if the track was flushed just after having @@ -853,7 +863,7 @@ void AudioMixer::process__genericNoResampling(state_t* state) while (outFrames) { size_t inFrames = (t.frameCount > outFrames)?outFrames:t.frameCount; if (inFrames) { - (t.hook)(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux); + t.hook(&t, outTemp + (BLOCKSIZE-outFrames)*MAX_NUM_CHANNELS, inFrames, state->resampleTemp, aux); t.frameCount -= inFrames; outFrames -= inFrames; if (CC_UNLIKELY(aux != NULL)) { @@ -863,7 +873,9 @@ void AudioMixer::process__genericNoResampling(state_t* state) if (t.frameCount == 0 && outFrames) { t.bufferProvider->releaseBuffer(&t.buffer); t.buffer.frameCount = (state->frameCount - numFrames) - (BLOCKSIZE - outFrames); - t.bufferProvider->getNextBuffer(&t.buffer); + int64_t outputPTS = calculateOutputPTS( + t, pts, numFrames + (BLOCKSIZE - outFrames)); + t.bufferProvider->getNextBuffer(&t.buffer, outputPTS); t.in = t.buffer.raw; if (t.in == NULL) { enabledTracks &= ~(1<<i); @@ -892,7 +904,7 @@ void AudioMixer::process__genericNoResampling(state_t* state) // generic code with resampling -void AudioMixer::process__genericResampling(state_t* state) +void AudioMixer::process__genericResampling(state_t* state, int64_t pts) { // this const just means that local variable outTemp doesn't change int32_t* const outTemp = state->outputTemp; @@ -932,14 +944,16 @@ void AudioMixer::process__genericResampling(state_t* state) // acquire/release the buffers because it's done by // the resampler. if ((t.needs & NEEDS_RESAMPLE__MASK) == NEEDS_RESAMPLE_ENABLED) { - (t.hook)(&t, outTemp, numFrames, state->resampleTemp, aux); + t.resampler->setPTS(pts); + t.hook(&t, outTemp, numFrames, state->resampleTemp, aux); } else { size_t outFrames = 0; while (outFrames < numFrames) { t.buffer.frameCount = numFrames - outFrames; - t.bufferProvider->getNextBuffer(&t.buffer); + int64_t outputPTS = calculateOutputPTS(t, pts, outFrames); + t.bufferProvider->getNextBuffer(&t.buffer, outputPTS); t.in = t.buffer.raw; // t.in == NULL can happen if the track was flushed just after having // been enabled for mixing. @@ -948,7 +962,7 @@ void AudioMixer::process__genericResampling(state_t* state) if (CC_UNLIKELY(aux != NULL)) { aux += outFrames; } - (t.hook)(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux); + t.hook(&t, outTemp + outFrames*MAX_NUM_CHANNELS, t.buffer.frameCount, state->resampleTemp, aux); outFrames += t.buffer.frameCount; t.bufferProvider->releaseBuffer(&t.buffer); } @@ -959,7 +973,8 @@ void AudioMixer::process__genericResampling(state_t* state) } // one track, 16 bits stereo without resampling is the most common case -void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state) +void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state, + int64_t pts) { // This method is only called when state->enabledTracks has exactly // one bit set. The asserts below would verify this, but are commented out @@ -979,7 +994,8 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state) const uint32_t vrl = t.volumeRL; while (numFrames) { b.frameCount = numFrames; - t.bufferProvider->getNextBuffer(&b); + int64_t outputPTS = calculateOutputPTS(t, pts, out - t.mainBuffer); + t.bufferProvider->getNextBuffer(&b, outputPTS); const int16_t *in = b.i16; // in == NULL can happen if the track was flushed just after having @@ -1023,7 +1039,8 @@ void AudioMixer::process__OneTrack16BitsStereoNoResampling(state_t* state) // 2 tracks is also a common case // NEVER used in current implementation of process__validate() // only use if the 2 tracks have the same output buffer -void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state) +void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state, + int64_t pts) { int i; uint32_t en = state->enabledTracks; @@ -1057,7 +1074,9 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state) if (frameCount0 == 0) { b0.frameCount = numFrames; - t0.bufferProvider->getNextBuffer(&b0); + int64_t outputPTS = calculateOutputPTS(t0, pts, + out - t0.mainBuffer); + t0.bufferProvider->getNextBuffer(&b0, outputPTS); if (b0.i16 == NULL) { if (buff == NULL) { buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; @@ -1071,7 +1090,9 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state) } if (frameCount1 == 0) { b1.frameCount = numFrames; - t1.bufferProvider->getNextBuffer(&b1); + int64_t outputPTS = calculateOutputPTS(t1, pts, + out - t0.mainBuffer); + t1.bufferProvider->getNextBuffer(&b1, outputPTS); if (b1.i16 == NULL) { if (buff == NULL) { buff = new int16_t[MAX_NUM_CHANNELS * state->frameCount]; @@ -1117,5 +1138,14 @@ void AudioMixer::process__TwoTracks16BitsStereoNoResampling(state_t* state) } #endif +int64_t AudioMixer::calculateOutputPTS(const track_t& t, int64_t basePTS, + int outputFrameIndex) +{ + if (AudioBufferProvider::kInvalidPTS == basePTS) + return AudioBufferProvider::kInvalidPTS; + + return basePTS + ((outputFrameIndex * t.localTimeFreq) / t.sampleRate); +} + // ---------------------------------------------------------------------------- }; // namespace android diff --git a/services/audioflinger/AudioMixer.h b/services/audioflinger/AudioMixer.h index c95691865c8c..b2102123646e 100644 --- a/services/audioflinger/AudioMixer.h +++ b/services/audioflinger/AudioMixer.h @@ -79,7 +79,7 @@ public: void setParameter(int name, int target, int param, void *value); void setBufferProvider(int name, AudioBufferProvider* bufferProvider); - void process(); + void process(int64_t pts); uint32_t trackNames() const { return mTrackNames; } @@ -114,7 +114,6 @@ private: struct state_t; struct track_t; - typedef void (*mix_t)(state_t* state); typedef void (*hook_t)(track_t* t, int32_t* output, size_t numOutFrames, int32_t* temp, int32_t* aux); static const int BLOCKSIZE = 16; // 4 cache lines @@ -128,30 +127,46 @@ private: int32_t prevVolume[MAX_NUM_CHANNELS]; + // 16-byte boundary + int32_t volumeInc[MAX_NUM_CHANNELS]; - int32_t auxLevel; int32_t auxInc; int32_t prevAuxLevel; + // 16-byte boundary + + int16_t auxLevel; // 0 <= auxLevel <= MAX_GAIN_INT, but signed for mul performance uint16_t frameCount; - uint8_t channelCount : 4; - uint8_t enabled : 1; - uint8_t reserved0 : 3; - uint8_t format; - uint32_t channelMask; + uint8_t channelCount; // 1 or 2, redundant with (needs & NEEDS_CHANNEL_COUNT__MASK) + uint8_t format; // always 16 + uint16_t enabled; // actually bool + uint32_t channelMask; // currently under-used AudioBufferProvider* bufferProvider; - mutable AudioBufferProvider::Buffer buffer; + + // 16-byte boundary + + mutable AudioBufferProvider::Buffer buffer; // 8 bytes hook_t hook; const void* in; // current location in buffer + // 16-byte boundary + AudioResampler* resampler; uint32_t sampleRate; int32_t* mainBuffer; int32_t* auxBuffer; + // 16-byte boundary + + uint64_t localTimeFreq; + + int64_t padding; + + // 16-byte boundary + bool setResampler(uint32_t sampleRate, uint32_t devSampleRate); bool doesResample() const { return resampler != NULL; } void resetResampler() { if (resampler != NULL) resampler->reset(); } @@ -165,7 +180,7 @@ private: uint32_t enabledTracks; uint32_t needsChanged; size_t frameCount; - mix_t hook; + void (*hook)(state_t* state, int64_t pts); // one of process__*, never NULL int32_t *outputTemp; int32_t *resampleTemp; int32_t reserved[2]; @@ -187,14 +202,19 @@ private: static void volumeRampStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux); static void volumeStereo(track_t* t, int32_t* out, size_t frameCount, int32_t* temp, int32_t* aux); - static void process__validate(state_t* state); - static void process__nop(state_t* state); - static void process__genericNoResampling(state_t* state); - static void process__genericResampling(state_t* state); - static void process__OneTrack16BitsStereoNoResampling(state_t* state); + static void process__validate(state_t* state, int64_t pts); + static void process__nop(state_t* state, int64_t pts); + static void process__genericNoResampling(state_t* state, int64_t pts); + static void process__genericResampling(state_t* state, int64_t pts); + static void process__OneTrack16BitsStereoNoResampling(state_t* state, + int64_t pts); #if 0 - static void process__TwoTracks16BitsStereoNoResampling(state_t* state); + static void process__TwoTracks16BitsStereoNoResampling(state_t* state, + int64_t pts); #endif + + static int64_t calculateOutputPTS(const track_t& t, int64_t basePTS, + int outputFrameIndex); }; // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioPolicyService.cpp b/services/audioflinger/AudioPolicyService.cpp index 041b5a869b0a..987b039f820c 100644 --- a/services/audioflinger/AudioPolicyService.cpp +++ b/services/audioflinger/AudioPolicyService.cpp @@ -116,19 +116,7 @@ AudioPolicyService::~AudioPolicyService() // release audio pre processing resources for (size_t i = 0; i < mInputSources.size(); i++) { - InputSourceDesc *source = mInputSources.valueAt(i); - Vector <EffectDesc *> effects = source->mEffects; - for (size_t j = 0; j < effects.size(); j++) { - delete effects[j]->mName; - Vector <effect_param_t *> params = effects[j]->mParams; - for (size_t k = 0; k < params.size(); k++) { - delete params[k]; - } - params.clear(); - delete effects[j]; - } - effects.clear(); - delete source; + delete mInputSources.valueAt(i); } mInputSources.clear(); @@ -616,8 +604,7 @@ void AudioPolicyService::setPreProcessorEnabled(InputDesc *inputDesc, bool enabl { Vector<sp<AudioEffect> > fxVector = inputDesc->mEffects; for (size_t i = 0; i < fxVector.size(); i++) { - sp<AudioEffect> fx = fxVector.itemAt(i); - fx->setEnabled(enabled); + fxVector.itemAt(i)->setEnabled(enabled); } } @@ -1243,7 +1230,7 @@ AudioPolicyService::InputSourceDesc *AudioPolicyService::loadInputSource( node = node->next; continue; } - EffectDesc *effect = new EffectDesc(*effects[i]); + EffectDesc *effect = new EffectDesc(*effects[i]); // deep copy loadEffectParameters(node, effect->mParams); ALOGV("loadInputSource() adding effect %s uuid %08x", effect->mName, effect->mUuid.timeLow); source->mEffects.add(effect); @@ -1294,11 +1281,7 @@ AudioPolicyService::EffectDesc *AudioPolicyService::loadEffect(cnode *root) ALOGW("loadEffect() invalid uuid %s", node->value); return NULL; } - EffectDesc *effect = new EffectDesc(); - effect->mName = strdup(root->name); - memcpy(&effect->mUuid, &uuid, sizeof(effect_uuid_t)); - - return effect; + return new EffectDesc(root->name, uuid); } status_t AudioPolicyService::loadEffects(cnode *root, Vector <EffectDesc *>& effects) diff --git a/services/audioflinger/AudioPolicyService.h b/services/audioflinger/AudioPolicyService.h index fdaf57644e40..679fd30df02a 100644 --- a/services/audioflinger/AudioPolicyService.h +++ b/services/audioflinger/AudioPolicyService.h @@ -233,8 +233,33 @@ private: class EffectDesc { public: - EffectDesc() {} - virtual ~EffectDesc() {} + EffectDesc(const char *name, const effect_uuid_t& uuid) : + mName(strdup(name)), + mUuid(uuid) { } + EffectDesc(const EffectDesc& orig) : + mName(strdup(orig.mName)), + mUuid(orig.mUuid) { + // deep copy mParams + for (size_t k = 0; k < orig.mParams.size(); k++) { + effect_param_t *origParam = orig.mParams[k]; + // psize and vsize are rounded up to an int boundary for allocation + size_t origSize = sizeof(effect_param_t) + + ((origParam->psize + 3) & ~3) + + ((origParam->vsize + 3) & ~3); + effect_param_t *dupParam = (effect_param_t *) malloc(origSize); + memcpy(dupParam, origParam, origSize); + // This works because the param buffer allocation is also done by + // multiples of 4 bytes originally. In theory we should memcpy only + // the actual param size, that is without rounding vsize. + mParams.add(dupParam); + } + } + /*virtual*/ ~EffectDesc() { + free(mName); + for (size_t k = 0; k < mParams.size(); k++) { + free(mParams[k]); + } + } char *mName; effect_uuid_t mUuid; Vector <effect_param_t *> mParams; @@ -243,7 +268,11 @@ private: class InputSourceDesc { public: InputSourceDesc() {} - virtual ~InputSourceDesc() {} + /*virtual*/ ~InputSourceDesc() { + for (size_t j = 0; j < mEffects.size(); j++) { + delete mEffects[j]; + } + } Vector <EffectDesc *> mEffects; }; diff --git a/services/audioflinger/AudioResampler.cpp b/services/audioflinger/AudioResampler.cpp index 9486b9c9efe0..398ba0b6004d 100644 --- a/services/audioflinger/AudioResampler.cpp +++ b/services/audioflinger/AudioResampler.cpp @@ -122,7 +122,8 @@ AudioResampler::AudioResampler(int bitDepth, int inChannelCount, int32_t sampleRate) : mBitDepth(bitDepth), mChannelCount(inChannelCount), mSampleRate(sampleRate), mInSampleRate(sampleRate), mInputIndex(0), - mPhaseFraction(0) { + mPhaseFraction(0), mLocalTimeFreq(0), + mPTS(AudioBufferProvider::kInvalidPTS) { // sanity check on format if ((bitDepth != 16) ||(inChannelCount < 1) || (inChannelCount > 2)) { ALOGE("Unsupported sample format, %d bits, %d channels", bitDepth, @@ -150,6 +151,23 @@ void AudioResampler::setVolume(int16_t left, int16_t right) { mVolume[1] = right; } +void AudioResampler::setLocalTimeFreq(uint64_t freq) { + mLocalTimeFreq = freq; +} + +void AudioResampler::setPTS(int64_t pts) { + mPTS = pts; +} + +int64_t AudioResampler::calculateOutputPTS(int outputFrameIndex) { + + if (mPTS == AudioBufferProvider::kInvalidPTS) { + return AudioBufferProvider::kInvalidPTS; + } else { + return mPTS + ((outputFrameIndex * mLocalTimeFreq) / mSampleRate); + } +} + void AudioResampler::reset() { mInputIndex = 0; mPhaseFraction = 0; @@ -196,7 +214,8 @@ void AudioResamplerOrder1::resampleStereo16(int32_t* out, size_t outFrameCount, // buffer is empty, fetch a new one while (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, + calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { goto resampleStereo16_exit; } @@ -290,7 +309,8 @@ void AudioResamplerOrder1::resampleMono16(int32_t* out, size_t outFrameCount, // buffer is empty, fetch a new one while (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, + calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { mInputIndex = inputIndex; mPhaseFraction = phaseFraction; diff --git a/services/audioflinger/AudioResampler.h b/services/audioflinger/AudioResampler.h index c23016e02ca5..9deb7963d215 100644 --- a/services/audioflinger/AudioResampler.h +++ b/services/audioflinger/AudioResampler.h @@ -49,6 +49,10 @@ public: virtual void init() = 0; virtual void setSampleRate(int32_t inSampleRate); virtual void setVolume(int16_t left, int16_t right); + virtual void setLocalTimeFreq(uint64_t freq); + + // set the PTS of the next buffer output by the resampler + virtual void setPTS(int64_t pts); virtual void resample(int32_t* out, size_t outFrameCount, AudioBufferProvider* provider) = 0; @@ -72,6 +76,8 @@ protected: AudioResampler(const AudioResampler&); AudioResampler& operator=(const AudioResampler&); + int64_t calculateOutputPTS(int outputFrameIndex); + const int32_t mBitDepth; const int32_t mChannelCount; const int32_t mSampleRate; @@ -85,6 +91,8 @@ protected: size_t mInputIndex; int32_t mPhaseIncrement; uint32_t mPhaseFraction; + uint64_t mLocalTimeFreq; + int64_t mPTS; }; // ---------------------------------------------------------------------------- diff --git a/services/audioflinger/AudioResamplerCubic.cpp b/services/audioflinger/AudioResamplerCubic.cpp index c0e760e643ce..18e59e93ee2e 100644 --- a/services/audioflinger/AudioResamplerCubic.cpp +++ b/services/audioflinger/AudioResamplerCubic.cpp @@ -65,7 +65,7 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, // fetch first buffer if (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, mPTS); if (mBuffer.raw == NULL) return; // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); @@ -95,7 +95,8 @@ void AudioResamplerCubic::resampleStereo16(int32_t* out, size_t outFrameCount, inputIndex = 0; provider->releaseBuffer(&mBuffer); mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, + calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) goto save_state; // ugly, but efficient in = mBuffer.i16; @@ -130,7 +131,7 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, // fetch first buffer if (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, mPTS); if (mBuffer.raw == NULL) return; // ALOGW("New buffer: offset=%p, frames=%d", mBuffer.raw, mBuffer.frameCount); @@ -160,7 +161,8 @@ void AudioResamplerCubic::resampleMono16(int32_t* out, size_t outFrameCount, inputIndex = 0; provider->releaseBuffer(&mBuffer); mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, + calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) goto save_state; // ugly, but efficient // ALOGW("New buffer: offset=%p, frames=%dn", mBuffer.raw, mBuffer.frameCount); @@ -181,4 +183,3 @@ save_state: // ---------------------------------------------------------------------------- } ; // namespace android - diff --git a/services/audioflinger/AudioResamplerSinc.cpp b/services/audioflinger/AudioResamplerSinc.cpp index 7a27b810afd0..d373c08385b0 100644 --- a/services/audioflinger/AudioResamplerSinc.cpp +++ b/services/audioflinger/AudioResamplerSinc.cpp @@ -203,7 +203,8 @@ void AudioResamplerSinc::resample(int32_t* out, size_t outFrameCount, // buffer is empty, fetch a new one while (mBuffer.frameCount == 0) { mBuffer.frameCount = inFrameCount; - provider->getNextBuffer(&mBuffer); + provider->getNextBuffer(&mBuffer, + calculateOutputPTS(outputIndex / 2)); if (mBuffer.raw == NULL) { goto resample_exit; } @@ -354,4 +355,3 @@ void AudioResamplerSinc::interpolate( // ---------------------------------------------------------------------------- }; // namespace android - diff --git a/services/common_time/Android.mk b/services/common_time/Android.mk new file mode 100644 index 000000000000..e534d4987c45 --- /dev/null +++ b/services/common_time/Android.mk @@ -0,0 +1,34 @@ +LOCAL_PATH:= $(call my-dir) + +# +# common_time_service +# + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := \ + common_clock_service.cpp \ + common_time_config_service.cpp \ + common_time_server.cpp \ + common_time_server_api.cpp \ + common_time_server_packets.cpp \ + clock_recovery.cpp \ + common_clock.cpp \ + main.cpp + +# Uncomment to enable vesbose logging and debug service. +#TIME_SERVICE_DEBUG=true +ifeq ($(TIME_SERVICE_DEBUG), true) +LOCAL_SRC_FILES += diag_thread.cpp +LOCAL_CFLAGS += -DTIME_SERVICE_DEBUG +endif + +LOCAL_SHARED_LIBRARIES := \ + libbinder \ + libcommon_time_client \ + libutils + +LOCAL_MODULE_TAGS := optional +LOCAL_MODULE := common_time + +include $(BUILD_EXECUTABLE) diff --git a/services/common_time/clock_recovery.cpp b/services/common_time/clock_recovery.cpp new file mode 100644 index 000000000000..6c98d32a7109 --- /dev/null +++ b/services/common_time/clock_recovery.cpp @@ -0,0 +1,321 @@ +/* + * 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. + */ + +/* + * A service that exchanges time synchronization information between + * a master that defines a timeline and clients that follow the timeline. + */ + +#define __STDC_LIMIT_MACROS +#define LOG_TAG "common_time" +#include <utils/Log.h> +#include <stdint.h> + +#include <common_time/local_clock.h> +#include <assert.h> + +#include "clock_recovery.h" +#include "common_clock.h" +#ifdef TIME_SERVICE_DEBUG +#include "diag_thread.h" +#endif + +// Define log macro so we can make LOGV into LOGE when we are exclusively +// debugging this code. +#ifdef TIME_SERVICE_DEBUG +#define LOG_TS ALOGE +#else +#define LOG_TS ALOGV +#endif + +namespace android { + +ClockRecoveryLoop::ClockRecoveryLoop(LocalClock* local_clock, + CommonClock* common_clock) { + assert(NULL != local_clock); + assert(NULL != common_clock); + + local_clock_ = local_clock; + common_clock_ = common_clock; + + local_clock_can_slew_ = local_clock_->initCheck() && + (local_clock_->setLocalSlew(0) == OK); + + reset(true, true); + +#ifdef TIME_SERVICE_DEBUG + diag_thread_ = new DiagThread(common_clock_, local_clock_); + if (diag_thread_ != NULL) { + status_t res = diag_thread_->startWorkThread(); + if (res != OK) + ALOGW("Failed to start A@H clock recovery diagnostic thread."); + } else + ALOGW("Failed to allocate diagnostic thread."); +#endif +} + +ClockRecoveryLoop::~ClockRecoveryLoop() { +#ifdef TIME_SERVICE_DEBUG + diag_thread_->stopWorkThread(); +#endif +} + +// Constants. +const float ClockRecoveryLoop::dT = 1.0; +const float ClockRecoveryLoop::Kc = 1.0f; +const float ClockRecoveryLoop::Ti = 15.0f; +const float ClockRecoveryLoop::Tf = 0.05; +const float ClockRecoveryLoop::bias_Fc = 0.01; +const float ClockRecoveryLoop::bias_RC = (dT / (2 * 3.14159f * bias_Fc)); +const float ClockRecoveryLoop::bias_Alpha = (dT / (bias_RC + dT)); +const int64_t ClockRecoveryLoop::panic_thresh_ = 50000; +const int64_t ClockRecoveryLoop::control_thresh_ = 10000; +const float ClockRecoveryLoop::COmin = -100.0f; +const float ClockRecoveryLoop::COmax = 100.0f; + +void ClockRecoveryLoop::reset(bool position, bool frequency) { + Mutex::Autolock lock(&lock_); + reset_l(position, frequency); +} + +uint32_t ClockRecoveryLoop::findMinRTTNdx(DisciplineDataPoint* data, + uint32_t count) { + uint32_t min_rtt = 0; + for (uint32_t i = 1; i < count; ++i) + if (data[min_rtt].rtt > data[i].rtt) + min_rtt = i; + + return min_rtt; +} + +bool ClockRecoveryLoop::pushDisciplineEvent(int64_t local_time, + int64_t nominal_common_time, + int64_t rtt) { + Mutex::Autolock lock(&lock_); + + int64_t local_common_time = 0; + common_clock_->localToCommon(local_time, &local_common_time); + int64_t raw_delta = nominal_common_time - local_common_time; + +#ifdef TIME_SERVICE_DEBUG + ALOGE("local=%lld, common=%lld, delta=%lld, rtt=%lld\n", + local_common_time, nominal_common_time, + raw_delta, rtt); +#endif + + // If we have not defined a basis for common time, then we need to use these + // initial points to do so. In order to avoid significant initial error + // from a particularly bad startup data point, we collect the first N data + // points and choose the best of them before moving on. + if (!common_clock_->isValid()) { + if (startup_filter_wr_ < kStartupFilterSize) { + DisciplineDataPoint& d = startup_filter_data_[startup_filter_wr_]; + d.local_time = local_time; + d.nominal_common_time = nominal_common_time; + d.rtt = rtt; + startup_filter_wr_++; + } + + if (startup_filter_wr_ == kStartupFilterSize) { + uint32_t min_rtt = findMinRTTNdx(startup_filter_data_, + kStartupFilterSize); + + common_clock_->setBasis( + startup_filter_data_[min_rtt].local_time, + startup_filter_data_[min_rtt].nominal_common_time); + } + + return true; + } + + int64_t observed_common; + int64_t delta; + float delta_f, dCO; + int32_t correction_cur; + + if (OK != common_clock_->localToCommon(local_time, &observed_common)) { + // Since we just checked to make certain that this conversion was valid, + // and no one else in the system should be messing with it, if this + // conversion is suddenly invalid, it is a good reason to panic. + ALOGE("Failed to convert local time to common time in %s:%d", + __PRETTY_FUNCTION__, __LINE__); + return false; + } + + // Implement a filter which should match NTP filtering behavior when a + // client is associated with only one peer of lower stratum. Basically, + // always use the best of the N last data points, where best is defined as + // lowest round trip time. NTP uses an N of 8; we use a value of 6. + // + // TODO(johngro) : experiment with other filter strategies. The goal here + // is to mitigate the effects of high RTT data points which typically have + // large asymmetries in the TX/RX legs. Downside of the existing NTP + // approach (particularly because of the PID controller we are using to + // produce the control signal from the filtered data) are that the rate at + // which discipline events are actually acted upon becomes irregular and can + // become drawn out (the time between actionable event can go way up). If + // the system receives a strong high quality data point, the proportional + // component of the controller can produce a strong correction which is left + // in place for too long causing overshoot. In addition, the integral + // component of the system currently is an approximation based on the + // assumption of a more or less homogeneous sampling of the error. Its + // unclear what the effect of undermining this assumption would be right + // now. + + // Two ideas which come to mind immediately would be to... + // 1) Keep a history of more data points (32 or so) and ignore data points + // whose RTT is more than a certain number of standard deviations outside + // of the norm. + // 2) Eliminate the PID controller portion of this system entirely. + // Instead, move to a system which uses a very wide filter (128 data + // points or more) with a sum-of-least-squares line fitting approach to + // tracking the long term drift. This would take the place of the I + // component in the current PID controller. Also use a much more narrow + // outlier-rejector filter (as described in #1) to drive a short term + // correction factor similar to the P component of the PID controller. + assert(filter_wr_ < kFilterSize); + filter_data_[filter_wr_].local_time = local_time; + filter_data_[filter_wr_].observed_common_time = observed_common; + filter_data_[filter_wr_].nominal_common_time = nominal_common_time; + filter_data_[filter_wr_].rtt = rtt; + filter_data_[filter_wr_].point_used = false; + uint32_t current_point = filter_wr_; + filter_wr_ = (filter_wr_ + 1) % kFilterSize; + if (!filter_wr_) + filter_full_ = true; + + uint32_t scan_end = filter_full_ ? kFilterSize : filter_wr_; + uint32_t min_rtt = findMinRTTNdx(filter_data_, scan_end); + // We only use packets with low RTTs for control. If the packet RTT + // is less than the panic threshold, we can probably eat the jitter with the + // control loop. Otherwise, take the packet only if it better than all + // of the packets we have in the history. That way we try to track + // something, even if it is noisy. + if (current_point == min_rtt || rtt < control_thresh_) { + delta_f = delta = nominal_common_time - observed_common; + + // Compute the error then clamp to the panic threshold. If we ever + // exceed this amt of error, its time to panic and reset the system. + // Given that the error in the measurement of the error could be as + // high as the RTT of the data point, we don't actually panic until + // the implied error (delta) is greater than the absolute panic + // threashold plus the RTT. IOW - we don't panic until we are + // absoluely sure that our best case sync is worse than the absolute + // panic threshold. + int64_t effective_panic_thresh = panic_thresh_ + rtt; + if ((delta > effective_panic_thresh) || + (delta < -effective_panic_thresh)) { + // PANIC!!! + reset_l(false, true); + return false; + } + + } else { + // We do not have a good packet to look at, but we also do not want to + // free-run the clock at some crazy slew rate. So we guess the + // trajectory of the clock based on the last controller output and the + // estimated bias of our clock against the master. + // The net effect of this is that CO == CObias after some extended + // period of no feedback. + delta_f = last_delta_f_ - dT*(CO - CObias); + delta = delta_f; + } + + // Velocity form PI control equation. + dCO = Kc * (1.0f + dT/Ti) * delta_f - Kc * last_delta_f_; + CO += dCO * Tf; // Filter CO by applying gain <1 here. + + // Save error terms for later. + last_delta_f_ = delta_f; + last_delta_ = delta; + + // Clamp CO to +/- 100ppm. + if (CO < COmin) + CO = COmin; + else if (CO > COmax) + CO = COmax; + + // Update the controller bias. + CObias = bias_Alpha * CO + (1.0f - bias_Alpha) * lastCObias; + lastCObias = CObias; + + // Convert PPM to 16-bit int range. Add some guard band (-0.01) so we + // don't get fp weirdness. + correction_cur = CO * 327.66; + + // If there was a change in the amt of correction to use, update the + // system. + if (correction_cur_ != correction_cur) { + correction_cur_ = correction_cur; + applySlew(); + } + + LOG_TS("clock_loop %lld %f %f %f %d\n", raw_delta, delta_f, CO, CObias, correction_cur); + +#ifdef TIME_SERVICE_DEBUG + diag_thread_->pushDisciplineEvent( + local_time, + observed_common, + nominal_common_time, + correction_cur, + rtt); +#endif + + return true; +} + +int32_t ClockRecoveryLoop::getLastErrorEstimate() { + Mutex::Autolock lock(&lock_); + + if (last_delta_valid_) + return last_delta_; + else + return ICommonClock::kErrorEstimateUnknown; +} + +void ClockRecoveryLoop::reset_l(bool position, bool frequency) { + assert(NULL != common_clock_); + + if (position) { + common_clock_->resetBasis(); + startup_filter_wr_ = 0; + } + + if (frequency) { + last_delta_valid_ = false; + last_delta_ = 0; + last_delta_f_ = 0.0; + correction_cur_ = 0x0; + CO = 0.0f; + lastCObias = CObias = 0.0f; + applySlew(); + } + + filter_wr_ = 0; + filter_full_ = false; +} + +void ClockRecoveryLoop::applySlew() { + if (local_clock_can_slew_) { + local_clock_->setLocalSlew(correction_cur_); + } else { + // The SW clock recovery implemented by the common clock class expects + // values expressed in PPM. CO is in ppm. + common_clock_->setSlew(local_clock_->getLocalTime(), CO); + } +} + +} // namespace android diff --git a/services/common_time/clock_recovery.h b/services/common_time/clock_recovery.h new file mode 100644 index 000000000000..b7362be33b14 --- /dev/null +++ b/services/common_time/clock_recovery.h @@ -0,0 +1,138 @@ +/* + * 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 __CLOCK_RECOVERY_H__ +#define __CLOCK_RECOVERY_H__ + +#include <stdint.h> +#include <common_time/ICommonClock.h> +#include <utils/LinearTransform.h> +#include <utils/threads.h> + +#ifdef TIME_SERVICE_DEBUG +#include "diag_thread.h" +#endif + +namespace android { + +class CommonClock; +class LocalClock; + +class ClockRecoveryLoop { + public: + ClockRecoveryLoop(LocalClock* local_clock, CommonClock* common_clock); + ~ClockRecoveryLoop(); + + void reset(bool position, bool frequency); + bool pushDisciplineEvent(int64_t local_time, + int64_t nominal_common_time, + int64_t data_point_rtt); + int32_t getLastErrorEstimate(); + + private: + + // Tuned using the "Good Gain" method. + // See: + // http://techteach.no/publications/books/dynamics_and_control/tuning_pid_controller.pdf + + // Controller period (1Hz for now). + static const float dT; + + // Controller gain, positive and unitless. Larger values converge faster, + // but can cause instability. + static const float Kc; + + // Integral reset time. Smaller values cause loop to track faster, but can + // also cause instability. + static const float Ti; + + // Controller output filter time constant. Range (0-1). Smaller values make + // output smoother, but slow convergence. + static const float Tf; + + // Low-pass filter for bias tracker. + static const float bias_Fc; // HZ + static const float bias_RC; // Computed in constructor. + static const float bias_Alpha; // Computed inconstructor. + + // The maximum allowed error (as indicated by a pushDisciplineEvent) before + // we panic. + static const int64_t panic_thresh_; + + // The maximum allowed error rtt time for packets to be used for control + // feedback, unless the packet is the best in recent memory. + static const int64_t control_thresh_; + + typedef struct { + int64_t local_time; + int64_t observed_common_time; + int64_t nominal_common_time; + int64_t rtt; + bool point_used; + } DisciplineDataPoint; + + static uint32_t findMinRTTNdx(DisciplineDataPoint* data, uint32_t count); + + void reset_l(bool position, bool frequency); + void applySlew(); + + // The local clock HW abstraction we use as the basis for common time. + LocalClock* local_clock_; + bool local_clock_can_slew_; + + // The common clock we end up controlling along with the lock used to + // serialize operations. + CommonClock* common_clock_; + Mutex lock_; + + // parameters maintained while running and reset during a reset + // of the frequency correction. + bool last_delta_valid_; + int32_t last_delta_; + float last_delta_f_; + int32_t integrated_error_; + int32_t correction_cur_; + + // Contoller Output. + float CO; + + // Bias tracking for trajectory estimation. + float CObias; + float lastCObias; + + // Controller output bounds. The controller will not try to + // slew faster that +/-100ppm offset from center per interation. + static const float COmin; + static const float COmax; + + // State kept for filtering the discipline data. + static const uint32_t kFilterSize = 16; + DisciplineDataPoint filter_data_[kFilterSize]; + uint32_t filter_wr_; + bool filter_full_; + + static const uint32_t kStartupFilterSize = 4; + DisciplineDataPoint startup_filter_data_[kStartupFilterSize]; + uint32_t startup_filter_wr_; + +#ifdef TIME_SERVICE_DEBUG + sp<DiagThread> diag_thread_; +#endif +}; + +} // namespace android + +#endif // __CLOCK_RECOVERY_H__ diff --git a/services/common_time/common_clock.cpp b/services/common_time/common_clock.cpp new file mode 100644 index 000000000000..c9eb3884440d --- /dev/null +++ b/services/common_time/common_clock.cpp @@ -0,0 +1,150 @@ +/* + * Copyright (C) 2012 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 __STDC_LIMIT_MACROS + +#define LOG_TAG "common_time" +#include <utils/Log.h> + +#include <stdint.h> + +#include <utils/Errors.h> +#include <utils/LinearTransform.h> + +#include "common_clock.h" + +namespace android { + +CommonClock::CommonClock() { + cur_slew_ = 0; + cur_trans_valid_ = false; + + cur_trans_.a_zero = 0; + cur_trans_.b_zero = 0; + cur_trans_.a_to_b_numer = local_to_common_freq_numer_ = 1; + cur_trans_.a_to_b_denom = local_to_common_freq_denom_ = 1; + duration_trans_ = cur_trans_; +} + +bool CommonClock::init(uint64_t local_freq) { + Mutex::Autolock lock(&lock_); + + if (!local_freq) + return false; + + uint64_t numer = kCommonFreq; + uint64_t denom = local_freq; + + LinearTransform::reduce(&numer, &denom); + if ((numer > UINT32_MAX) || (denom > UINT32_MAX)) { + ALOGE("Overflow in CommonClock::init while trying to reduce %lld/%lld", + kCommonFreq, local_freq); + return false; + } + + cur_trans_.a_to_b_numer = local_to_common_freq_numer_ = + static_cast<uint32_t>(numer); + cur_trans_.a_to_b_denom = local_to_common_freq_denom_ = + static_cast<uint32_t>(denom); + duration_trans_ = cur_trans_; + + return true; +} + +status_t CommonClock::localToCommon(int64_t local, int64_t *common_out) const { + Mutex::Autolock lock(&lock_); + + if (!cur_trans_valid_) + return INVALID_OPERATION; + + if (!cur_trans_.doForwardTransform(local, common_out)) + return INVALID_OPERATION; + + return OK; +} + +status_t CommonClock::commonToLocal(int64_t common, int64_t *local_out) const { + Mutex::Autolock lock(&lock_); + + if (!cur_trans_valid_) + return INVALID_OPERATION; + + if (!cur_trans_.doReverseTransform(common, local_out)) + return INVALID_OPERATION; + + return OK; +} + +int64_t CommonClock::localDurationToCommonDuration(int64_t localDur) const { + int64_t ret; + duration_trans_.doForwardTransform(localDur, &ret); + return ret; +} + +void CommonClock::setBasis(int64_t local, int64_t common) { + Mutex::Autolock lock(&lock_); + + cur_trans_.a_zero = local; + cur_trans_.b_zero = common; + cur_trans_valid_ = true; +} + +void CommonClock::resetBasis() { + Mutex::Autolock lock(&lock_); + + cur_trans_.a_zero = 0; + cur_trans_.b_zero = 0; + cur_trans_valid_ = false; +} + +status_t CommonClock::setSlew(int64_t change_time, int32_t ppm) { + Mutex::Autolock lock(&lock_); + + int64_t new_local_basis; + int64_t new_common_basis; + + if (cur_trans_valid_) { + new_local_basis = change_time; + if (!cur_trans_.doForwardTransform(change_time, &new_common_basis)) { + ALOGE("Overflow when attempting to set slew rate to %d", ppm); + return INVALID_OPERATION; + } + } else { + new_local_basis = 0; + new_common_basis = 0; + } + + cur_slew_ = ppm; + uint32_t n1 = local_to_common_freq_numer_; + uint32_t n2 = 1000000 + cur_slew_; + + uint32_t d1 = local_to_common_freq_denom_; + uint32_t d2 = 1000000; + + // n1/d1 has already been reduced, no need to do so here. + LinearTransform::reduce(&n1, &d2); + LinearTransform::reduce(&n2, &d1); + LinearTransform::reduce(&n2, &d2); + + cur_trans_.a_zero = new_local_basis; + cur_trans_.b_zero = new_common_basis; + cur_trans_.a_to_b_numer = n1 * n2; + cur_trans_.a_to_b_denom = d1 * d2; + + return OK; +} + +} // namespace android diff --git a/services/common_time/common_clock.h b/services/common_time/common_clock.h new file mode 100644 index 000000000000..b786fdceba5f --- /dev/null +++ b/services/common_time/common_clock.h @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2012 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 __COMMON_CLOCK_H__ +#define __COMMON_CLOCK_H__ + +#include <stdint.h> + +#include <utils/Errors.h> +#include <utils/LinearTransform.h> +#include <utils/threads.h> + +namespace android { + +class CommonClock { + public: + CommonClock(); + + bool init(uint64_t local_freq); + + status_t localToCommon(int64_t local, int64_t *common_out) const; + status_t commonToLocal(int64_t common, int64_t *local_out) const; + int64_t localDurationToCommonDuration(int64_t localDur) const; + uint64_t getCommonFreq() const { return kCommonFreq; } + bool isValid() const { return cur_trans_valid_; } + status_t setSlew(int64_t change_time, int32_t ppm); + void setBasis(int64_t local, int64_t common); + void resetBasis(); + private: + mutable Mutex lock_; + + int32_t cur_slew_; + uint32_t local_to_common_freq_numer_; + uint32_t local_to_common_freq_denom_; + + LinearTransform duration_trans_; + LinearTransform cur_trans_; + bool cur_trans_valid_; + + static const uint64_t kCommonFreq = 1000000ull; +}; + +} // namespace android +#endif // __COMMON_CLOCK_H__ diff --git a/services/common_time/common_clock_service.cpp b/services/common_time/common_clock_service.cpp new file mode 100644 index 000000000000..9ca6f3523084 --- /dev/null +++ b/services/common_time/common_clock_service.cpp @@ -0,0 +1,157 @@ +/* + * 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. + */ + +#include <common_time/local_clock.h> +#include <utils/String8.h> + +#include "common_clock_service.h" +#include "common_clock.h" +#include "common_time_server.h" + +namespace android { + +sp<CommonClockService> CommonClockService::instantiate( + CommonTimeServer& timeServer) { + sp<CommonClockService> tcc = new CommonClockService(timeServer); + if (tcc == NULL) + return NULL; + + defaultServiceManager()->addService(ICommonClock::kServiceName, tcc); + return tcc; +} + +status_t CommonClockService::dump(int fd, const Vector<String16>& args) { + Mutex::Autolock lock(mRegistrationLock); + return mTimeServer.dumpClockInterface(fd, args, mListeners.size()); +} + +status_t CommonClockService::isCommonTimeValid(bool* valid, + uint32_t* timelineID) { + return mTimeServer.isCommonTimeValid(valid, timelineID); +} + +status_t CommonClockService::commonTimeToLocalTime(int64_t commonTime, + int64_t* localTime) { + return mTimeServer.getCommonClock().commonToLocal(commonTime, localTime); +} + +status_t CommonClockService::localTimeToCommonTime(int64_t localTime, + int64_t* commonTime) { + return mTimeServer.getCommonClock().localToCommon(localTime, commonTime); +} + +status_t CommonClockService::getCommonTime(int64_t* commonTime) { + return localTimeToCommonTime(mTimeServer.getLocalClock().getLocalTime(), commonTime); +} + +status_t CommonClockService::getCommonFreq(uint64_t* freq) { + *freq = mTimeServer.getCommonClock().getCommonFreq(); + return OK; +} + +status_t CommonClockService::getLocalTime(int64_t* localTime) { + *localTime = mTimeServer.getLocalClock().getLocalTime(); + return OK; +} + +status_t CommonClockService::getLocalFreq(uint64_t* freq) { + *freq = mTimeServer.getLocalClock().getLocalFreq(); + return OK; +} + +status_t CommonClockService::getEstimatedError(int32_t* estimate) { + *estimate = mTimeServer.getEstimatedError(); + return OK; +} + +status_t CommonClockService::getTimelineID(uint64_t* id) { + *id = mTimeServer.getTimelineID(); + return OK; +} + +status_t CommonClockService::getState(State* state) { + *state = mTimeServer.getState(); + return OK; +} + +status_t CommonClockService::getMasterAddr(struct sockaddr_storage* addr) { + return mTimeServer.getMasterAddr(addr); +} + +status_t CommonClockService::registerListener( + const sp<ICommonClockListener>& listener) { + Mutex::Autolock lock(mRegistrationLock); + + { // scoping for autolock pattern + Mutex::Autolock lock(mCallbackLock); + // check whether this is a duplicate + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == listener->asBinder()) + return ALREADY_EXISTS; + } + } + + mListeners.add(listener); + mTimeServer.reevaluateAutoDisableState(0 != mListeners.size()); + return listener->asBinder()->linkToDeath(this); +} + +status_t CommonClockService::unregisterListener( + const sp<ICommonClockListener>& listener) { + Mutex::Autolock lock(mRegistrationLock); + status_t ret_val = NAME_NOT_FOUND; + + { // scoping for autolock pattern + Mutex::Autolock lock(mCallbackLock); + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == listener->asBinder()) { + mListeners[i]->asBinder()->unlinkToDeath(this); + mListeners.removeAt(i); + ret_val = OK; + break; + } + } + } + + mTimeServer.reevaluateAutoDisableState(0 != mListeners.size()); + return ret_val; +} + +void CommonClockService::binderDied(const wp<IBinder>& who) { + Mutex::Autolock lock(mRegistrationLock); + + { // scoping for autolock pattern + Mutex::Autolock lock(mCallbackLock); + for (size_t i = 0; i < mListeners.size(); i++) { + if (mListeners[i]->asBinder() == who) { + mListeners.removeAt(i); + break; + } + } + } + + mTimeServer.reevaluateAutoDisableState(0 != mListeners.size()); +} + +void CommonClockService::notifyOnTimelineChanged(uint64_t timelineID) { + Mutex::Autolock lock(mCallbackLock); + + for (size_t i = 0; i < mListeners.size(); i++) { + mListeners[i]->onTimelineChanged(timelineID); + } +} + +}; // namespace android diff --git a/services/common_time/common_clock_service.h b/services/common_time/common_clock_service.h new file mode 100644 index 000000000000..a65e398d276f --- /dev/null +++ b/services/common_time/common_clock_service.h @@ -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. + */ + +#include <common_time/ICommonClock.h> + +#ifndef ANDROID_COMMON_CLOCK_SERVICE_H +#define ANDROID_COMMON_CLOCK_SERVICE_H + +namespace android { + +class CommonTimeServer; + +class CommonClockService : public BnCommonClock, + public android::IBinder::DeathRecipient { + public: + static sp<CommonClockService> instantiate(CommonTimeServer& timeServer); + + virtual status_t dump(int fd, const Vector<String16>& args); + + virtual status_t isCommonTimeValid(bool* valid, uint32_t *timelineID); + virtual status_t commonTimeToLocalTime(int64_t common_time, + int64_t* local_time); + virtual status_t localTimeToCommonTime(int64_t local_time, + int64_t* common_time); + virtual status_t getCommonTime(int64_t* common_time); + virtual status_t getCommonFreq(uint64_t* freq); + virtual status_t getLocalTime(int64_t* local_time); + virtual status_t getLocalFreq(uint64_t* freq); + virtual status_t getEstimatedError(int32_t* estimate); + virtual status_t getTimelineID(uint64_t* id); + virtual status_t getState(ICommonClock::State* state); + virtual status_t getMasterAddr(struct sockaddr_storage* addr); + + virtual status_t registerListener( + const sp<ICommonClockListener>& listener); + virtual status_t unregisterListener( + const sp<ICommonClockListener>& listener); + + void notifyOnTimelineChanged(uint64_t timelineID); + + private: + CommonClockService(CommonTimeServer& timeServer) + : mTimeServer(timeServer) { }; + + virtual void binderDied(const wp<IBinder>& who); + + CommonTimeServer& mTimeServer; + + // locks used to synchronize access to the list of registered listeners. + // The callback lock is held whenever the list is used to perform callbacks + // or while the list is being modified. The registration lock used to + // serialize access across registerListener, unregisterListener, and + // binderDied. + // + // The reason for two locks is that registerListener, unregisterListener, + // and binderDied each call into the core service and obtain the core + // service thread lock when they call reevaluateAutoDisableState. The core + // service thread obtains the main thread lock whenever its thread is + // running, and sometimes needs to call notifyOnTimelineChanged which then + // obtains the callback lock. If callers of registration functions were + // holding the callback lock when they called into the core service, we + // would have a classic A/B, B/A ordering deadlock. To avoid this, the + // registration functions hold the registration lock for the duration of + // their call, but hold the callback lock only while they mutate the list. + // This way, the list's size cannot change (because of the registration + // lock) during the call into reevaluateAutoDisableState, but the core work + // thread can still safely call notifyOnTimelineChanged while holding the + // main thread lock. + Mutex mCallbackLock; + Mutex mRegistrationLock; + + Vector<sp<ICommonClockListener> > mListeners; +}; + +}; // namespace android + +#endif // ANDROID_COMMON_CLOCK_SERVICE_H diff --git a/services/common_time/common_time_config_service.cpp b/services/common_time/common_time_config_service.cpp new file mode 100644 index 000000000000..958561818423 --- /dev/null +++ b/services/common_time/common_time_config_service.cpp @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2012 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 <utils/String8.h> + +#include "common_time_config_service.h" +#include "common_time_server.h" + +namespace android { + +sp<CommonTimeConfigService> CommonTimeConfigService::instantiate( + CommonTimeServer& timeServer) { + sp<CommonTimeConfigService> ctcs = new CommonTimeConfigService(timeServer); + if (ctcs == NULL) + return NULL; + + defaultServiceManager()->addService(ICommonTimeConfig::kServiceName, ctcs); + return ctcs; +} + +status_t CommonTimeConfigService::dump(int fd, const Vector<String16>& args) { + return mTimeServer.dumpConfigInterface(fd, args); +} + +status_t CommonTimeConfigService::getMasterElectionPriority(uint8_t *priority) { + return mTimeServer.getMasterElectionPriority(priority); +} + +status_t CommonTimeConfigService::setMasterElectionPriority(uint8_t priority) { + return mTimeServer.setMasterElectionPriority(priority); +} + +status_t CommonTimeConfigService::getMasterElectionEndpoint( + struct sockaddr_storage *addr) { + return mTimeServer.getMasterElectionEndpoint(addr); +} + +status_t CommonTimeConfigService::setMasterElectionEndpoint( + const struct sockaddr_storage *addr) { + return mTimeServer.setMasterElectionEndpoint(addr); +} + +status_t CommonTimeConfigService::getMasterElectionGroupId(uint64_t *id) { + return mTimeServer.getMasterElectionGroupId(id); +} + +status_t CommonTimeConfigService::setMasterElectionGroupId(uint64_t id) { + return mTimeServer.setMasterElectionGroupId(id); +} + +status_t CommonTimeConfigService::getInterfaceBinding(String16& ifaceName) { + String8 tmp; + status_t ret = mTimeServer.getInterfaceBinding(tmp); + ifaceName = String16(tmp); + return ret; +} + +status_t CommonTimeConfigService::setInterfaceBinding(const String16& ifaceName) { + String8 tmp(ifaceName); + return mTimeServer.setInterfaceBinding(tmp); +} + +status_t CommonTimeConfigService::getMasterAnnounceInterval(int *interval) { + return mTimeServer.getMasterAnnounceInterval(interval); +} + +status_t CommonTimeConfigService::setMasterAnnounceInterval(int interval) { + return mTimeServer.setMasterAnnounceInterval(interval); +} + +status_t CommonTimeConfigService::getClientSyncInterval(int *interval) { + return mTimeServer.getClientSyncInterval(interval); +} + +status_t CommonTimeConfigService::setClientSyncInterval(int interval) { + return mTimeServer.setClientSyncInterval(interval); +} + +status_t CommonTimeConfigService::getPanicThreshold(int *threshold) { + return mTimeServer.getPanicThreshold(threshold); +} + +status_t CommonTimeConfigService::setPanicThreshold(int threshold) { + return mTimeServer.setPanicThreshold(threshold); +} + +status_t CommonTimeConfigService::getAutoDisable(bool *autoDisable) { + return mTimeServer.getAutoDisable(autoDisable); +} + +status_t CommonTimeConfigService::setAutoDisable(bool autoDisable) { + return mTimeServer.setAutoDisable(autoDisable); +} + +status_t CommonTimeConfigService::forceNetworklessMasterMode() { + return mTimeServer.forceNetworklessMasterMode(); +} + +}; // namespace android diff --git a/services/common_time/common_time_config_service.h b/services/common_time/common_time_config_service.h new file mode 100644 index 000000000000..8283c24e0f4c --- /dev/null +++ b/services/common_time/common_time_config_service.h @@ -0,0 +1,59 @@ +/* * Copyright (C) 2012 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 <common_time/ICommonTimeConfig.h> + +#ifndef ANDROID_COMMON_TIME_CONFIG_SERVICE_H +#define ANDROID_COMMON_TIME_CONFIG_SERVICE_H + +namespace android { + +class String16; +class CommonTimeServer; + +class CommonTimeConfigService : public BnCommonTimeConfig { + public: + static sp<CommonTimeConfigService> instantiate(CommonTimeServer& timeServer); + + virtual status_t dump(int fd, const Vector<String16>& args); + + virtual status_t getMasterElectionPriority(uint8_t *priority); + virtual status_t setMasterElectionPriority(uint8_t priority); + virtual status_t getMasterElectionEndpoint(struct sockaddr_storage *addr); + virtual status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr); + virtual status_t getMasterElectionGroupId(uint64_t *id); + virtual status_t setMasterElectionGroupId(uint64_t id); + virtual status_t getInterfaceBinding(String16& ifaceName); + virtual status_t setInterfaceBinding(const String16& ifaceName); + virtual status_t getMasterAnnounceInterval(int *interval); + virtual status_t setMasterAnnounceInterval(int interval); + virtual status_t getClientSyncInterval(int *interval); + virtual status_t setClientSyncInterval(int interval); + virtual status_t getPanicThreshold(int *threshold); + virtual status_t setPanicThreshold(int threshold); + virtual status_t getAutoDisable(bool *autoDisable); + virtual status_t setAutoDisable(bool autoDisable); + virtual status_t forceNetworklessMasterMode(); + + private: + CommonTimeConfigService(CommonTimeServer& timeServer) + : mTimeServer(timeServer) { } + CommonTimeServer& mTimeServer; + +}; + +}; // namespace android + +#endif // ANDROID_COMMON_TIME_CONFIG_SERVICE_H diff --git a/services/common_time/common_time_server.cpp b/services/common_time/common_time_server.cpp new file mode 100644 index 000000000000..ef7fa1682575 --- /dev/null +++ b/services/common_time/common_time_server.cpp @@ -0,0 +1,1380 @@ +/* + * Copyright (C) 2012 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. + */ + +/* + * A service that exchanges time synchronization information between + * a master that defines a timeline and clients that follow the timeline. + */ + +#define LOG_TAG "common_time" +#include <utils/Log.h> + +#include <arpa/inet.h> +#include <assert.h> +#include <fcntl.h> +#include <linux/if_ether.h> +#include <net/if.h> +#include <net/if_arp.h> +#include <netinet/ip.h> +#include <poll.h> +#include <stdio.h> +#include <sys/eventfd.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <common_time/local_clock.h> +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> +#include <utils/Timers.h> + +#include "common_clock_service.h" +#include "common_time_config_service.h" +#include "common_time_server.h" +#include "common_time_server_packets.h" +#include "clock_recovery.h" +#include "common_clock.h" + +#define MAX_INT ((int)0x7FFFFFFF) + +namespace android { + +const char* CommonTimeServer::kDefaultMasterElectionAddr = "239.195.128.88"; +const uint16_t CommonTimeServer::kDefaultMasterElectionPort = 8887; +const uint64_t CommonTimeServer::kDefaultSyncGroupID = 0; +const uint8_t CommonTimeServer::kDefaultMasterPriority = 1; +const uint32_t CommonTimeServer::kDefaultMasterAnnounceIntervalMs = 10000; +const uint32_t CommonTimeServer::kDefaultSyncRequestIntervalMs = 1000; +const uint32_t CommonTimeServer::kDefaultPanicThresholdUsec = 50000; +const bool CommonTimeServer::kDefaultAutoDisable = true; +const int CommonTimeServer::kSetupRetryTimeoutMs = 30000; +const int64_t CommonTimeServer::kNoGoodDataPanicThresholdUsec = 600000000ll; +const uint32_t CommonTimeServer::kRTTDiscardPanicThreshMultiplier = 5; + +// timeout value representing an infinite timeout +const int CommonTimeServer::kInfiniteTimeout = -1; + +/*** Initial state constants ***/ + +// number of WhoIsMaster attempts sent before giving up +const int CommonTimeServer::kInitial_NumWhoIsMasterRetries = 6; + +// timeout used when waiting for a response to a WhoIsMaster request +const int CommonTimeServer::kInitial_WhoIsMasterTimeoutMs = 500; + +/*** Client state constants ***/ + +// number of sync requests that can fail before a client assumes its master +// is dead +const int CommonTimeServer::kClient_NumSyncRequestRetries = 5; + +/*** Master state constants ***/ + +/*** Ronin state constants ***/ + +// number of WhoIsMaster attempts sent before declaring ourselves master +const int CommonTimeServer::kRonin_NumWhoIsMasterRetries = 4; + +// timeout used when waiting for a response to a WhoIsMaster request +const int CommonTimeServer::kRonin_WhoIsMasterTimeoutMs = 500; + +/*** WaitForElection state constants ***/ + +// how long do we wait for an announcement from a master before +// trying another election? +const int CommonTimeServer::kWaitForElection_TimeoutMs = 5000; + +CommonTimeServer::CommonTimeServer() + : Thread(false) + , mState(ICommonClock::STATE_INITIAL) + , mClockRecovery(&mLocalClock, &mCommonClock) + , mSocket(-1) + , mLastPacketRxLocalTime(0) + , mTimelineID(ICommonClock::kInvalidTimelineID) + , mClockSynced(false) + , mCommonClockHasClients(false) + , mInitial_WhoIsMasterRequestTimeouts(0) + , mClient_MasterDeviceID(0) + , mClient_MasterDevicePriority(0) + , mRonin_WhoIsMasterRequestTimeouts(0) { + // zero out sync stats + resetSyncStats(); + + // Setup the master election endpoint to use the default. + struct sockaddr_in* meep = + reinterpret_cast<struct sockaddr_in*>(&mMasterElectionEP); + memset(&mMasterElectionEP, 0, sizeof(mMasterElectionEP)); + inet_aton(kDefaultMasterElectionAddr, &meep->sin_addr); + meep->sin_family = AF_INET; + meep->sin_port = htons(kDefaultMasterElectionPort); + + // Zero out the master endpoint. + memset(&mMasterEP, 0, sizeof(mMasterEP)); + mMasterEPValid = false; + mBindIfaceValid = false; + setForceLowPriority(false); + + // Set all remaining configuration parameters to their defaults. + mDeviceID = 0; + mSyncGroupID = kDefaultSyncGroupID; + mMasterPriority = kDefaultMasterPriority; + mMasterAnnounceIntervalMs = kDefaultMasterAnnounceIntervalMs; + mSyncRequestIntervalMs = kDefaultSyncRequestIntervalMs; + mPanicThresholdUsec = kDefaultPanicThresholdUsec; + mAutoDisable = kDefaultAutoDisable; + + // Create the eventfd we will use to signal our thread to wake up when + // needed. + mWakeupThreadFD = eventfd(0, EFD_NONBLOCK); + + // seed the random number generator (used to generated timeline IDs) + srand48(static_cast<unsigned int>(systemTime())); +} + +CommonTimeServer::~CommonTimeServer() { + shutdownThread(); + + // No need to grab the lock here. We are in the destructor; if the the user + // has a thread in any of the APIs while the destructor is being called, + // there is a threading problem a the application level we cannot reasonably + // do anything about. + cleanupSocket_l(); + + if (mWakeupThreadFD >= 0) { + close(mWakeupThreadFD); + mWakeupThreadFD = -1; + } +} + +bool CommonTimeServer::startServices() { + // start the ICommonClock service + mICommonClock = CommonClockService::instantiate(*this); + if (mICommonClock == NULL) + return false; + + // start the ICommonTimeConfig service + mICommonTimeConfig = CommonTimeConfigService::instantiate(*this); + if (mICommonTimeConfig == NULL) + return false; + + return true; +} + +bool CommonTimeServer::threadLoop() { + // Register our service interfaces. + if (!startServices()) + return false; + + // Hold the lock while we are in the main thread loop. It will release the + // lock when it blocks, and hold the lock at all other times. + mLock.lock(); + runStateMachine_l(); + mLock.unlock(); + + IPCThreadState::self()->stopProcess(); + return false; +} + +bool CommonTimeServer::runStateMachine_l() { + if (!mLocalClock.initCheck()) + return false; + + if (!mCommonClock.init(mLocalClock.getLocalFreq())) + return false; + + // Enter the initial state. + becomeInitial("startup"); + + // run the state machine + while (!exitPending()) { + struct pollfd pfds[2]; + int rc; + int eventCnt = 0; + int64_t wakeupTime; + + // We are always interested in our wakeup FD. + pfds[eventCnt].fd = mWakeupThreadFD; + pfds[eventCnt].events = POLLIN; + pfds[eventCnt].revents = 0; + eventCnt++; + + // If we have a valid socket, then we are interested in what it has to + // say as well. + if (mSocket >= 0) { + pfds[eventCnt].fd = mSocket; + pfds[eventCnt].events = POLLIN; + pfds[eventCnt].revents = 0; + eventCnt++; + } + + // Note, we were holding mLock when this function was called. We + // release it only while we are blocking and hold it at all other times. + mLock.unlock(); + rc = poll(pfds, eventCnt, mCurTimeout.msecTillTimeout()); + wakeupTime = mLocalClock.getLocalTime(); + mLock.lock(); + + // Is it time to shutdown? If so, don't hesitate... just do it. + if (exitPending()) + break; + + // Did the poll fail? This should never happen and is fatal if it does. + if (rc < 0) { + ALOGE("%s:%d poll failed", __PRETTY_FUNCTION__, __LINE__); + return false; + } + + if (rc == 0) + mCurTimeout.setTimeout(kInfiniteTimeout); + + // Were we woken up on purpose? If so, clear the eventfd with a read. + if (pfds[0].revents) + clearPendingWakeupEvents_l(); + + // Is out bind address dirty? If so, clean up our socket (if any). + // Alternatively, do we have an active socket but should be auto + // disabled? If so, release the socket and enter the proper sync state. + bool droppedSocket = false; + if (mBindIfaceDirty || ((mSocket >= 0) && shouldAutoDisable())) { + cleanupSocket_l(); + mBindIfaceDirty = false; + droppedSocket = true; + } + + // Do we not have a socket but should have one? If so, try to set one + // up. + if ((mSocket < 0) && mBindIfaceValid && !shouldAutoDisable()) { + if (setupSocket_l()) { + // Success! We are now joining a new network (either coming + // from no network, or coming from a potentially different + // network). Force our priority to be lower so that we defer to + // any other masters which may already be on the network we are + // joining. Later, when we enter either the client or the + // master state, we will clear this flag and go back to our + // normal election priority. + setForceLowPriority(true); + switch (mState) { + // If we were in initial (whether we had a immediately + // before this network or not) we want to simply reset the + // system and start again. Forcing a transition from + // INITIAL to INITIAL should do the job. + case CommonClockService::STATE_INITIAL: + becomeInitial("bound interface"); + break; + + // If we were in the master state, then either we were the + // master in a no-network situation, or we were the master + // of a different network and have moved to a new interface. + // In either case, immediately send out a master + // announcement at low priority. + case CommonClockService::STATE_MASTER: + sendMasterAnnouncement(); + break; + + // If we were in any other state (CLIENT, RONIN, or + // WAIT_FOR_ELECTION) then we must be moving from one + // network to another. We have lost our old master; + // transition to RONIN in an attempt to find a new master. + // If there are none out there, we will just assume + // responsibility for the timeline we used to be a client + // of. + default: + becomeRonin("bound interface"); + break; + } + } else { + // That's odd... we failed to set up our socket. This could be + // due to some transient network change which will work itself + // out shortly; schedule a retry attempt in the near future. + mCurTimeout.setTimeout(kSetupRetryTimeoutMs); + } + + // One way or the other, we don't have any data to process at this + // point (since we just tried to bulid a new socket). Loop back + // around and wait for the next thing to do. + continue; + } else if (droppedSocket) { + // We just lost our socket, and for whatever reason (either no + // config, or auto disable engaged) we are not supposed to rebuild + // one at this time. We are not going to rebuild our socket until + // something about our config/auto-disabled status changes, so we + // are basically in network-less mode. If we are already in either + // INITIAL or MASTER, just stay there until something changes. If + // we are in any other state (CLIENT, RONIN or WAIT_FOR_ELECTION), + // then transition to either INITIAL or MASTER depending on whether + // or not our timeline is valid. + ALOGI("Entering networkless mode interface is %s, " + "shouldAutoDisable = %s", + mBindIfaceValid ? "valid" : "invalid", + shouldAutoDisable() ? "true" : "false"); + if ((mState != ICommonClock::STATE_INITIAL) && + (mState != ICommonClock::STATE_MASTER)) { + if (mTimelineID == ICommonClock::kInvalidTimelineID) + becomeInitial("network-less mode"); + else + becomeMaster("network-less mode"); + } + + continue; + } + + // Did we wakeup with no signalled events across all of our FDs? If so, + // we must have hit our timeout. + if (rc == 0) { + if (!handleTimeout()) + ALOGE("handleTimeout failed"); + continue; + } + + // Does our socket have data for us (assuming we still have one, we + // may have RXed a packet at the same time as a config change telling us + // to shut our socket down)? If so, process its data. + if ((mSocket >= 0) && (eventCnt > 1) && (pfds[1].revents)) { + mLastPacketRxLocalTime = wakeupTime; + if (!handlePacket()) + ALOGE("handlePacket failed"); + } + } + + cleanupSocket_l(); + return true; +} + +void CommonTimeServer::clearPendingWakeupEvents_l() { + int64_t tmp; + read(mWakeupThreadFD, &tmp, sizeof(tmp)); +} + +void CommonTimeServer::wakeupThread_l() { + int64_t tmp = 1; + write(mWakeupThreadFD, &tmp, sizeof(tmp)); +} + +void CommonTimeServer::cleanupSocket_l() { + if (mSocket >= 0) { + close(mSocket); + mSocket = -1; + } +} + +void CommonTimeServer::shutdownThread() { + // Flag the work thread for shutdown. + this->requestExit(); + + // Signal the thread in case its sleeping. + mLock.lock(); + wakeupThread_l(); + mLock.unlock(); + + // Wait for the thread to exit. + this->join(); +} + +bool CommonTimeServer::setupSocket_l() { + int rc; + bool ret_val = false; + struct sockaddr_in* ipv4_addr = NULL; + char masterElectionEPStr[64]; + const int one = 1; + + // This should never be needed, but if we happened to have an old socket + // lying around, be sure to not leak it before proceeding. + cleanupSocket_l(); + + // If we don't have a valid endpoint to bind to, then how did we get here in + // the first place? Regardless, we know that we are going to fail to bind, + // so don't even try. + if (!mBindIfaceValid) + return false; + + sockaddrToString(mMasterElectionEP, true, masterElectionEPStr, + sizeof(masterElectionEPStr)); + ALOGI("Building socket :: bind = %s master election = %s", + mBindIface.string(), masterElectionEPStr); + + // TODO: add proper support for IPv6. Right now, we block IPv6 addresses at + // the configuration interface level. + if (AF_INET != mMasterElectionEP.ss_family) { + ALOGW("TODO: add proper IPv6 support"); + goto bailout; + } + + // open a UDP socket for the timeline serivce + mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP); + if (mSocket < 0) { + ALOGE("Failed to create socket (errno = %d)", errno); + goto bailout; + } + + // Bind to the selected interface using Linux's spiffy SO_BINDTODEVICE. + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%s", mBindIface.string()); + ifr.ifr_name[sizeof(ifr.ifr_name) - 1] = 0; + rc = setsockopt(mSocket, SOL_SOCKET, SO_BINDTODEVICE, + (void *)&ifr, sizeof(ifr)); + if (rc) { + ALOGE("Failed to bind socket at to interface %s (errno = %d)", + ifr.ifr_name, errno); + goto bailout; + } + + // Bind our socket to INADDR_ANY and the master election port. The + // interface binding we made using SO_BINDTODEVICE should limit us to + // traffic only on the interface we are interested in. We need to bind to + // INADDR_ANY and the specific master election port in order to be able to + // receive both unicast traffic and master election multicast traffic with + // just a single socket. + struct sockaddr_in bindAddr; + ipv4_addr = reinterpret_cast<struct sockaddr_in*>(&mMasterElectionEP); + memcpy(&bindAddr, ipv4_addr, sizeof(bindAddr)); + bindAddr.sin_addr.s_addr = INADDR_ANY; + rc = bind(mSocket, + reinterpret_cast<const sockaddr *>(&bindAddr), + sizeof(bindAddr)); + if (rc) { + ALOGE("Failed to bind socket to port %hu (errno = %d)", + ntohs(bindAddr.sin_port), errno); + goto bailout; + } + + if (0xE0000000 == (ntohl(ipv4_addr->sin_addr.s_addr) & 0xF0000000)) { + // If our master election endpoint is a multicast address, be sure to join + // the multicast group. + struct ip_mreq mreq; + mreq.imr_multiaddr = ipv4_addr->sin_addr; + mreq.imr_interface.s_addr = htonl(INADDR_ANY); + rc = setsockopt(mSocket, IPPROTO_IP, IP_ADD_MEMBERSHIP, + &mreq, sizeof(mreq)); + if (rc == -1) { + ALOGE("Failed to join multicast group at %s. (errno = %d)", + masterElectionEPStr, errno); + goto bailout; + } + + // disable loopback of multicast packets + const int zero = 0; + rc = setsockopt(mSocket, IPPROTO_IP, IP_MULTICAST_LOOP, + &zero, sizeof(zero)); + if (rc == -1) { + ALOGE("Failed to disable multicast loopback (errno = %d)", errno); + goto bailout; + } + } else + if (ntohl(ipv4_addr->sin_addr.s_addr) != 0xFFFFFFFF) { + // If the master election address is neither broadcast, nor multicast, + // then we are misconfigured. The config API layer should prevent this + // from ever happening. + goto bailout; + } + + // Set the TTL of sent packets to 1. (Time protocol sync should never leave + // the local subnet) + rc = setsockopt(mSocket, IPPROTO_IP, IP_TTL, &one, sizeof(one)); + if (rc == -1) { + ALOGE("Failed to set TTL to %d (errno = %d)", one, errno); + goto bailout; + } + + // get the device's unique ID + if (!assignDeviceID()) + goto bailout; + + ret_val = true; + +bailout: + if (!ret_val) + cleanupSocket_l(); + return ret_val; +} + +// generate a unique device ID that can be used for arbitration +bool CommonTimeServer::assignDeviceID() { + if (!mBindIfaceValid) + return false; + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + ifr.ifr_addr.sa_family = AF_INET; + strlcpy(ifr.ifr_name, mBindIface.string(), IFNAMSIZ); + + int rc = ioctl(mSocket, SIOCGIFHWADDR, &ifr); + if (rc) { + ALOGE("%s:%d ioctl failed", __PRETTY_FUNCTION__, __LINE__); + return false; + } + + if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) { + ALOGE("%s:%d got non-Ethernet address", __PRETTY_FUNCTION__, __LINE__); + return false; + } + + mDeviceID = 0; + for (int i = 0; i < ETH_ALEN; i++) { + mDeviceID = (mDeviceID << 8) | ifr.ifr_hwaddr.sa_data[i]; + } + + return true; +} + +// generate a new timeline ID +void CommonTimeServer::assignTimelineID() { + do { + mTimelineID = (static_cast<uint64_t>(lrand48()) << 32) + | static_cast<uint64_t>(lrand48()); + } while (mTimelineID == ICommonClock::kInvalidTimelineID); +} + +// Select a preference between the device IDs of two potential masters. +// Returns true if the first ID wins, or false if the second ID wins. +bool CommonTimeServer::arbitrateMaster( + uint64_t deviceID1, uint8_t devicePrio1, + uint64_t deviceID2, uint8_t devicePrio2) { + return ((devicePrio1 > devicePrio2) || + ((devicePrio1 == devicePrio2) && (deviceID1 > deviceID2))); +} + +bool CommonTimeServer::handlePacket() { + uint8_t buf[256]; + struct sockaddr_storage srcAddr; + socklen_t srcAddrLen = sizeof(srcAddr); + + ssize_t recvBytes = recvfrom( + mSocket, buf, sizeof(buf), 0, + reinterpret_cast<const sockaddr *>(&srcAddr), &srcAddrLen); + + if (recvBytes < 0) { + ALOGE("%s:%d recvfrom failed", __PRETTY_FUNCTION__, __LINE__); + return false; + } + + UniversalTimeServicePacket pkt; + recvBytes = pkt.deserializePacket(buf, recvBytes, mSyncGroupID); + if (recvBytes < 0) + return false; + + bool result; + switch (pkt.packetType) { + case TIME_PACKET_WHO_IS_MASTER_REQUEST: + result = handleWhoIsMasterRequest(&pkt.p.who_is_master_request, + srcAddr); + break; + + case TIME_PACKET_WHO_IS_MASTER_RESPONSE: + result = handleWhoIsMasterResponse(&pkt.p.who_is_master_response, + srcAddr); + break; + + case TIME_PACKET_SYNC_REQUEST: + result = handleSyncRequest(&pkt.p.sync_request, srcAddr); + break; + + case TIME_PACKET_SYNC_RESPONSE: + result = handleSyncResponse(&pkt.p.sync_response, srcAddr); + break; + + case TIME_PACKET_MASTER_ANNOUNCEMENT: + result = handleMasterAnnouncement(&pkt.p.master_announcement, + srcAddr); + break; + + default: { + ALOGD("%s:%d unknown packet type(%d)", + __PRETTY_FUNCTION__, __LINE__, pkt.packetType); + result = false; + } break; + } + + return result; +} + +bool CommonTimeServer::handleTimeout() { + // If we have no socket, then this must be a timeout to retry socket setup. + if (mSocket < 0) + return true; + + switch (mState) { + case ICommonClock::STATE_INITIAL: + return handleTimeoutInitial(); + case ICommonClock::STATE_CLIENT: + return handleTimeoutClient(); + case ICommonClock::STATE_MASTER: + return handleTimeoutMaster(); + case ICommonClock::STATE_RONIN: + return handleTimeoutRonin(); + case ICommonClock::STATE_WAIT_FOR_ELECTION: + return handleTimeoutWaitForElection(); + } + + return false; +} + +bool CommonTimeServer::handleTimeoutInitial() { + if (++mInitial_WhoIsMasterRequestTimeouts == + kInitial_NumWhoIsMasterRetries) { + // none of our attempts to discover a master succeeded, so make + // this device the master + return becomeMaster("initial timeout"); + } else { + // retry the WhoIsMaster request + return sendWhoIsMasterRequest(); + } +} + +bool CommonTimeServer::handleTimeoutClient() { + if (shouldPanicNotGettingGoodData()) + return becomeInitial("timeout panic, no good data"); + + if (mClient_SyncRequestPending) { + mClient_SyncRequestPending = false; + + if (++mClient_SyncRequestTimeouts < kClient_NumSyncRequestRetries) { + // a sync request has timed out, so retry + return sendSyncRequest(); + } else { + // The master has failed to respond to a sync request for too many + // times in a row. Assume the master is dead and start electing + // a new master. + return becomeRonin("master not responding"); + } + } else { + // initiate the next sync request + return sendSyncRequest(); + } +} + +bool CommonTimeServer::handleTimeoutMaster() { + // send another announcement from the master + return sendMasterAnnouncement(); +} + +bool CommonTimeServer::handleTimeoutRonin() { + if (++mRonin_WhoIsMasterRequestTimeouts == kRonin_NumWhoIsMasterRetries) { + // no other master is out there, so we won the election + return becomeMaster("no better masters detected"); + } else { + return sendWhoIsMasterRequest(); + } +} + +bool CommonTimeServer::handleTimeoutWaitForElection() { + return becomeRonin("timeout waiting for election conclusion"); +} + +bool CommonTimeServer::handleWhoIsMasterRequest( + const WhoIsMasterRequestPacket* request, + const sockaddr_storage& srcAddr) { + if (mState == ICommonClock::STATE_MASTER) { + // is this request related to this master's timeline? + if (request->timelineID != ICommonClock::kInvalidTimelineID && + request->timelineID != mTimelineID) + return true; + + WhoIsMasterResponsePacket pkt; + pkt.initHeader(mTimelineID, mSyncGroupID); + pkt.deviceID = mDeviceID; + pkt.devicePriority = effectivePriority(); + + uint8_t buf[256]; + ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); + if (bufSz < 0) + return false; + + ssize_t sendBytes = sendto( + mSocket, buf, bufSz, 0, + reinterpret_cast<const sockaddr *>(&srcAddr), + sizeof(srcAddr)); + if (sendBytes == -1) { + ALOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__); + return false; + } + } else if (mState == ICommonClock::STATE_RONIN) { + // if we hear a WhoIsMaster request from another device following + // the same timeline and that device wins arbitration, then we will stop + // trying to elect ourselves master and will instead wait for an + // announcement from the election winner + if (request->timelineID != mTimelineID) + return true; + + if (arbitrateMaster(request->senderDeviceID, + request->senderDevicePriority, + mDeviceID, + effectivePriority())) + return becomeWaitForElection("would lose election"); + + return true; + } else if (mState == ICommonClock::STATE_INITIAL) { + // If a group of devices booted simultaneously (e.g. after a power + // outage) and all of them are in the initial state and there is no + // master, then each device may time out and declare itself master at + // the same time. To avoid this, listen for + // WhoIsMaster(InvalidTimeline) requests from peers. If we would lose + // arbitration against that peer, reset our timeout count so that the + // peer has a chance to become master before we time out. + if (request->timelineID == ICommonClock::kInvalidTimelineID && + arbitrateMaster(request->senderDeviceID, + request->senderDevicePriority, + mDeviceID, + effectivePriority())) { + mInitial_WhoIsMasterRequestTimeouts = 0; + } + } + + return true; +} + +bool CommonTimeServer::handleWhoIsMasterResponse( + const WhoIsMasterResponsePacket* response, + const sockaddr_storage& srcAddr) { + if (mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN) { + return becomeClient(srcAddr, + response->deviceID, + response->devicePriority, + response->timelineID, + "heard whois response"); + } else if (mState == ICommonClock::STATE_CLIENT) { + // if we get multiple responses because there are multiple devices + // who believe that they are master, then follow the master that + // wins arbitration + if (arbitrateMaster(response->deviceID, + response->devicePriority, + mClient_MasterDeviceID, + mClient_MasterDevicePriority)) { + return becomeClient(srcAddr, + response->deviceID, + response->devicePriority, + response->timelineID, + "heard whois response"); + } + } + + return true; +} + +bool CommonTimeServer::handleSyncRequest(const SyncRequestPacket* request, + const sockaddr_storage& srcAddr) { + SyncResponsePacket pkt; + pkt.initHeader(mTimelineID, mSyncGroupID); + + if ((mState == ICommonClock::STATE_MASTER) && + (mTimelineID == request->timelineID)) { + int64_t rxLocalTime = mLastPacketRxLocalTime; + int64_t rxCommonTime; + + // If we are master on an actual network and have actual clients, then + // we are no longer low priority. + setForceLowPriority(false); + + if (OK != mCommonClock.localToCommon(rxLocalTime, &rxCommonTime)) { + return false; + } + + int64_t txLocalTime = mLocalClock.getLocalTime();; + int64_t txCommonTime; + if (OK != mCommonClock.localToCommon(txLocalTime, &txCommonTime)) { + return false; + } + + pkt.nak = 0; + pkt.clientTxLocalTime = request->clientTxLocalTime; + pkt.masterRxCommonTime = rxCommonTime; + pkt.masterTxCommonTime = txCommonTime; + } else { + pkt.nak = 1; + pkt.clientTxLocalTime = 0; + pkt.masterRxCommonTime = 0; + pkt.masterTxCommonTime = 0; + } + + uint8_t buf[256]; + ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); + if (bufSz < 0) + return false; + + ssize_t sendBytes = sendto( + mSocket, &buf, bufSz, 0, + reinterpret_cast<const sockaddr *>(&srcAddr), + sizeof(srcAddr)); + if (sendBytes == -1) { + ALOGE("%s:%d sendto failed", __PRETTY_FUNCTION__, __LINE__); + return false; + } + + return true; +} + +bool CommonTimeServer::handleSyncResponse( + const SyncResponsePacket* response, + const sockaddr_storage& srcAddr) { + if (mState != ICommonClock::STATE_CLIENT) + return true; + + assert(mMasterEPValid); + if (!sockaddrMatch(srcAddr, mMasterEP, true)) { + char srcEP[64], expectedEP[64]; + sockaddrToString(srcAddr, true, srcEP, sizeof(srcEP)); + sockaddrToString(mMasterEP, true, expectedEP, sizeof(expectedEP)); + ALOGI("Dropping sync response from unexpected address." + " Expected %s Got %s", expectedEP, srcEP); + return true; + } + + if (response->nak) { + // if our master is no longer accepting requests, then we need to find + // a new master + return becomeRonin("master NAK'ed"); + } + + mClient_SyncRequestPending = 0; + mClient_SyncRequestTimeouts = 0; + mClient_PacketRTTLog.logRX(response->clientTxLocalTime, + mLastPacketRxLocalTime); + + bool result; + if (!(mClient_SyncRespsRXedFromCurMaster++)) { + // the first request/response exchange between a client and a master + // may take unusually long due to ARP, so discard it. + result = true; + } else { + int64_t clientTxLocalTime = response->clientTxLocalTime; + int64_t clientRxLocalTime = mLastPacketRxLocalTime; + int64_t masterTxCommonTime = response->masterTxCommonTime; + int64_t masterRxCommonTime = response->masterRxCommonTime; + + int64_t rtt = (clientRxLocalTime - clientTxLocalTime); + int64_t avgLocal = (clientTxLocalTime + clientRxLocalTime) >> 1; + int64_t avgCommon = (masterTxCommonTime + masterRxCommonTime) >> 1; + + // if the RTT of the packet is significantly larger than the panic + // threshold, we should simply discard it. Its better to do nothing + // than to take cues from a packet like that. + int rttCommon = mCommonClock.localDurationToCommonDuration(rtt); + if (rttCommon > (static_cast<int64_t>(mPanicThresholdUsec) * + kRTTDiscardPanicThreshMultiplier)) { + ALOGV("Dropping sync response with RTT of %lld uSec", rttCommon); + mClient_ExpiredSyncRespsRXedFromCurMaster++; + if (shouldPanicNotGettingGoodData()) + return becomeInitial("RX panic, no good data"); + } else { + result = mClockRecovery.pushDisciplineEvent(avgLocal, avgCommon, rttCommon); + mClient_LastGoodSyncRX = clientRxLocalTime; + + if (result) { + // indicate to listeners that we've synced to the common timeline + notifyClockSync(); + } else { + ALOGE("Panic! Observed clock sync error is too high to tolerate," + " resetting state machine and starting over."); + notifyClockSyncLoss(); + return becomeInitial("panic"); + } + } + } + + mCurTimeout.setTimeout(mSyncRequestIntervalMs); + return result; +} + +bool CommonTimeServer::handleMasterAnnouncement( + const MasterAnnouncementPacket* packet, + const sockaddr_storage& srcAddr) { + uint64_t newDeviceID = packet->deviceID; + uint8_t newDevicePrio = packet->devicePriority; + uint64_t newTimelineID = packet->timelineID; + + if (mState == ICommonClock::STATE_INITIAL || + mState == ICommonClock::STATE_RONIN || + mState == ICommonClock::STATE_WAIT_FOR_ELECTION) { + // if we aren't currently following a master, then start following + // this new master + return becomeClient(srcAddr, + newDeviceID, + newDevicePrio, + newTimelineID, + "heard master announcement"); + } else if (mState == ICommonClock::STATE_CLIENT) { + // if the new master wins arbitration against our current master, + // then become a client of the new master + if (arbitrateMaster(newDeviceID, + newDevicePrio, + mClient_MasterDeviceID, + mClient_MasterDevicePriority)) + return becomeClient(srcAddr, + newDeviceID, + newDevicePrio, + newTimelineID, + "heard master announcement"); + } else if (mState == ICommonClock::STATE_MASTER) { + // two masters are competing - if the new one wins arbitration, then + // cease acting as master + if (arbitrateMaster(newDeviceID, newDevicePrio, + mDeviceID, effectivePriority())) + return becomeClient(srcAddr, newDeviceID, + newDevicePrio, newTimelineID, + "heard master announcement"); + } + + return true; +} + +bool CommonTimeServer::sendWhoIsMasterRequest() { + assert(mState == ICommonClock::STATE_INITIAL || mState == ICommonClock::STATE_RONIN); + + // If we have no socket, then we must be in the unconfigured initial state. + // Don't report any errors, just don't try to send the initial who-is-master + // query. Eventually, our network will either become configured, or we will + // be forced into network-less master mode by higher level code. + if (mSocket < 0) { + assert(mState == ICommonClock::STATE_INITIAL); + return true; + } + + bool ret = false; + WhoIsMasterRequestPacket pkt; + pkt.initHeader(mSyncGroupID); + pkt.senderDeviceID = mDeviceID; + pkt.senderDevicePriority = effectivePriority(); + + uint8_t buf[256]; + ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); + if (bufSz >= 0) { + ssize_t sendBytes = sendto( + mSocket, buf, bufSz, 0, + reinterpret_cast<const sockaddr *>(&mMasterElectionEP), + sizeof(mMasterElectionEP)); + if (sendBytes < 0) + ALOGE("WhoIsMaster sendto failed (errno %d)", errno); + ret = true; + } + + if (mState == ICommonClock::STATE_INITIAL) { + mCurTimeout.setTimeout(kInitial_WhoIsMasterTimeoutMs); + } else { + mCurTimeout.setTimeout(kRonin_WhoIsMasterTimeoutMs); + } + + return ret; +} + +bool CommonTimeServer::sendSyncRequest() { + // If we are sending sync requests, then we must be in the client state and + // we must have a socket (when we have no network, we are only supposed to + // be in INITIAL or MASTER) + assert(mState == ICommonClock::STATE_CLIENT); + assert(mSocket >= 0); + + bool ret = false; + SyncRequestPacket pkt; + pkt.initHeader(mTimelineID, mSyncGroupID); + pkt.clientTxLocalTime = mLocalClock.getLocalTime(); + + if (!mClient_FirstSyncTX) + mClient_FirstSyncTX = pkt.clientTxLocalTime; + + mClient_PacketRTTLog.logTX(pkt.clientTxLocalTime); + + uint8_t buf[256]; + ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); + if (bufSz >= 0) { + ssize_t sendBytes = sendto( + mSocket, buf, bufSz, 0, + reinterpret_cast<const sockaddr *>(&mMasterEP), + sizeof(mMasterEP)); + if (sendBytes < 0) + ALOGE("SyncRequest sendto failed (errno %d)", errno); + ret = true; + } + + mClient_SyncsSentToCurMaster++; + mCurTimeout.setTimeout(mSyncRequestIntervalMs); + mClient_SyncRequestPending = true; + + return ret; +} + +bool CommonTimeServer::sendMasterAnnouncement() { + bool ret = false; + assert(mState == ICommonClock::STATE_MASTER); + + // If we are being asked to send a master announcement, but we have no + // socket, we must be in network-less master mode. Don't bother to send the + // announcement, and don't bother to schedule a timeout. When the network + // comes up, the work thread will get poked and start the process of + // figuring out who the current master should be. + if (mSocket < 0) { + mCurTimeout.setTimeout(kInfiniteTimeout); + return true; + } + + MasterAnnouncementPacket pkt; + pkt.initHeader(mTimelineID, mSyncGroupID); + pkt.deviceID = mDeviceID; + pkt.devicePriority = effectivePriority(); + + uint8_t buf[256]; + ssize_t bufSz = pkt.serializePacket(buf, sizeof(buf)); + if (bufSz >= 0) { + ssize_t sendBytes = sendto( + mSocket, buf, bufSz, 0, + reinterpret_cast<const sockaddr *>(&mMasterElectionEP), + sizeof(mMasterElectionEP)); + if (sendBytes < 0) + ALOGE("MasterAnnouncement sendto failed (errno %d)", errno); + ret = true; + } + + mCurTimeout.setTimeout(mMasterAnnounceIntervalMs); + return ret; +} + +bool CommonTimeServer::becomeClient(const sockaddr_storage& masterEP, + uint64_t masterDeviceID, + uint8_t masterDevicePriority, + uint64_t timelineID, + const char* cause) { + char newEPStr[64], oldEPStr[64]; + sockaddrToString(masterEP, true, newEPStr, sizeof(newEPStr)); + sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr)); + + ALOGI("%s --> CLIENT (%s) :%s" + " OldMaster: %02x-%014llx::%016llx::%s" + " NewMaster: %02x-%014llx::%016llx::%s", + stateToString(mState), cause, + (mTimelineID != timelineID) ? " (new timeline)" : "", + mClient_MasterDevicePriority, mClient_MasterDeviceID, + mTimelineID, oldEPStr, + masterDevicePriority, masterDeviceID, + timelineID, newEPStr); + + if (mTimelineID != timelineID) { + // start following a new timeline + mTimelineID = timelineID; + mClockRecovery.reset(true, true); + notifyClockSyncLoss(); + } else { + // start following a new master on the existing timeline + mClockRecovery.reset(false, true); + } + + mMasterEP = masterEP; + mMasterEPValid = true; + setForceLowPriority(false); + + mClient_MasterDeviceID = masterDeviceID; + mClient_MasterDevicePriority = masterDevicePriority; + resetSyncStats(); + + setState(ICommonClock::STATE_CLIENT); + + // add some jitter to when the various clients send their requests + // in order to reduce the likelihood that a group of clients overload + // the master after receiving a master announcement + usleep((lrand48() % 100) * 1000); + + return sendSyncRequest(); +} + +bool CommonTimeServer::becomeMaster(const char* cause) { + uint64_t oldTimelineID = mTimelineID; + if (mTimelineID == ICommonClock::kInvalidTimelineID) { + // this device has not been following any existing timeline, + // so it will create a new timeline and declare itself master + assert(!mCommonClock.isValid()); + + // set the common time basis + mCommonClock.setBasis(mLocalClock.getLocalTime(), 0); + + // assign an arbitrary timeline iD + assignTimelineID(); + + // notify listeners that we've created a common timeline + notifyClockSync(); + } + + ALOGI("%s --> MASTER (%s) : %s timeline %016llx", + stateToString(mState), cause, + (oldTimelineID == mTimelineID) ? "taking ownership of" + : "creating new", + mTimelineID); + + memset(&mMasterEP, 0, sizeof(mMasterEP)); + mMasterEPValid = false; + setForceLowPriority(false); + mClient_MasterDevicePriority = effectivePriority(); + mClient_MasterDeviceID = mDeviceID; + mClockRecovery.reset(false, true); + resetSyncStats(); + + setState(ICommonClock::STATE_MASTER); + return sendMasterAnnouncement(); +} + +bool CommonTimeServer::becomeRonin(const char* cause) { + // If we were the client of a given timeline, but had never received even a + // single time sync packet, then we transition back to Initial instead of + // Ronin. If we transition to Ronin and end up becoming the new Master, we + // will be unable to service requests for other clients because we never + // actually knew what time it was. By going to initial, we ensure that + // other clients who know what time it is, but would lose master arbitration + // in the Ronin case, will step up and become the proper new master of the + // old timeline. + + char oldEPStr[64]; + sockaddrToString(mMasterEP, mMasterEPValid, oldEPStr, sizeof(oldEPStr)); + memset(&mMasterEP, 0, sizeof(mMasterEP)); + mMasterEPValid = false; + + if (mCommonClock.isValid()) { + ALOGI("%s --> RONIN (%s) : lost track of previously valid timeline " + "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)", + stateToString(mState), cause, + mClient_MasterDevicePriority, mClient_MasterDeviceID, + mTimelineID, oldEPStr, + mClient_SyncsSentToCurMaster, + mClient_SyncRespsRXedFromCurMaster, + mClient_ExpiredSyncRespsRXedFromCurMaster); + + mRonin_WhoIsMasterRequestTimeouts = 0; + setState(ICommonClock::STATE_RONIN); + return sendWhoIsMasterRequest(); + } else { + ALOGI("%s --> INITIAL (%s) : never synced timeline " + "%02x-%014llx::%016llx::%s (%d TXed %d RXed %d RXExpired)", + stateToString(mState), cause, + mClient_MasterDevicePriority, mClient_MasterDeviceID, + mTimelineID, oldEPStr, + mClient_SyncsSentToCurMaster, + mClient_SyncRespsRXedFromCurMaster, + mClient_ExpiredSyncRespsRXedFromCurMaster); + + return becomeInitial("ronin, no timeline"); + } +} + +bool CommonTimeServer::becomeWaitForElection(const char* cause) { + ALOGI("%s --> WAIT_FOR_ELECTION (%s) : dropping out of election," + " waiting %d mSec for completion.", + stateToString(mState), cause, kWaitForElection_TimeoutMs); + + setState(ICommonClock::STATE_WAIT_FOR_ELECTION); + mCurTimeout.setTimeout(kWaitForElection_TimeoutMs); + return true; +} + +bool CommonTimeServer::becomeInitial(const char* cause) { + ALOGI("Entering INITIAL (%s), total reset.", cause); + + setState(ICommonClock::STATE_INITIAL); + + // reset clock recovery + mClockRecovery.reset(true, true); + + // reset internal state bookkeeping. + mCurTimeout.setTimeout(kInfiniteTimeout); + memset(&mMasterEP, 0, sizeof(mMasterEP)); + mMasterEPValid = false; + mLastPacketRxLocalTime = 0; + mTimelineID = ICommonClock::kInvalidTimelineID; + mClockSynced = false; + mInitial_WhoIsMasterRequestTimeouts = 0; + mClient_MasterDeviceID = 0; + mClient_MasterDevicePriority = 0; + mRonin_WhoIsMasterRequestTimeouts = 0; + resetSyncStats(); + + // send the first request to discover the master + return sendWhoIsMasterRequest(); +} + +void CommonTimeServer::notifyClockSync() { + if (!mClockSynced) { + mClockSynced = true; + mICommonClock->notifyOnTimelineChanged(mTimelineID); + } +} + +void CommonTimeServer::notifyClockSyncLoss() { + if (mClockSynced) { + mClockSynced = false; + mICommonClock->notifyOnTimelineChanged( + ICommonClock::kInvalidTimelineID); + } +} + +void CommonTimeServer::setState(ICommonClock::State s) { + mState = s; +} + +const char* CommonTimeServer::stateToString(ICommonClock::State s) { + switch(s) { + case ICommonClock::STATE_INITIAL: + return "INITIAL"; + case ICommonClock::STATE_CLIENT: + return "CLIENT"; + case ICommonClock::STATE_MASTER: + return "MASTER"; + case ICommonClock::STATE_RONIN: + return "RONIN"; + case ICommonClock::STATE_WAIT_FOR_ELECTION: + return "WAIT_FOR_ELECTION"; + default: + return "unknown"; + } +} + +void CommonTimeServer::sockaddrToString(const sockaddr_storage& addr, + bool addrValid, + char* buf, size_t bufLen) { + if (!bufLen || !buf) + return; + + if (addrValid) { + switch (addr.ss_family) { + case AF_INET: { + const struct sockaddr_in* sa = + reinterpret_cast<const struct sockaddr_in*>(&addr); + unsigned long a = ntohl(sa->sin_addr.s_addr); + uint16_t p = ntohs(sa->sin_port); + snprintf(buf, bufLen, "%lu.%lu.%lu.%lu:%hu", + ((a >> 24) & 0xFF), ((a >> 16) & 0xFF), + ((a >> 8) & 0xFF), (a & 0xFF), p); + } break; + + case AF_INET6: { + const struct sockaddr_in6* sa = + reinterpret_cast<const struct sockaddr_in6*>(&addr); + const uint8_t* a = sa->sin6_addr.s6_addr; + uint16_t p = ntohs(sa->sin6_port); + snprintf(buf, bufLen, + "%02X%02X:%02X%02X:%02X%02X:%02X%02X:" + "%02X%02X:%02X%02X:%02X%02X:%02X%02X port %hd", + a[0], a[1], a[ 2], a[ 3], a[ 4], a[ 5], a[ 6], a[ 7], + a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15], + p); + } break; + + default: + snprintf(buf, bufLen, + "<unknown sockaddr family %d>", addr.ss_family); + break; + } + } else { + snprintf(buf, bufLen, "<none>"); + } + + buf[bufLen - 1] = 0; +} + +bool CommonTimeServer::sockaddrMatch(const sockaddr_storage& a1, + const sockaddr_storage& a2, + bool matchAddressOnly) { + if (a1.ss_family != a2.ss_family) + return false; + + switch (a1.ss_family) { + case AF_INET: { + const struct sockaddr_in* sa1 = + reinterpret_cast<const struct sockaddr_in*>(&a1); + const struct sockaddr_in* sa2 = + reinterpret_cast<const struct sockaddr_in*>(&a2); + + if (sa1->sin_addr.s_addr != sa2->sin_addr.s_addr) + return false; + + return (matchAddressOnly || (sa1->sin_port == sa2->sin_port)); + } break; + + case AF_INET6: { + const struct sockaddr_in6* sa1 = + reinterpret_cast<const struct sockaddr_in6*>(&a1); + const struct sockaddr_in6* sa2 = + reinterpret_cast<const struct sockaddr_in6*>(&a2); + + if (memcmp(&sa1->sin6_addr, &sa2->sin6_addr, sizeof(sa2->sin6_addr))) + return false; + + return (matchAddressOnly || (sa1->sin6_port == sa2->sin6_port)); + } break; + + // Huh? We don't deal in non-IPv[46] addresses. Not sure how we got + // here, but we don't know how to comapre these addresses and simply + // default to a no-match decision. + default: return false; + } +} + +void CommonTimeServer::TimeoutHelper::setTimeout(int msec) { + mTimeoutValid = (msec >= 0); + if (mTimeoutValid) + mEndTime = systemTime() + + (static_cast<nsecs_t>(msec) * 1000000); +} + +int CommonTimeServer::TimeoutHelper::msecTillTimeout() { + if (!mTimeoutValid) + return kInfiniteTimeout; + + nsecs_t now = systemTime(); + if (now >= mEndTime) + return 0; + + uint64_t deltaMsec = (((mEndTime - now) + 999999) / 1000000); + + if (deltaMsec > static_cast<uint64_t>(MAX_INT)) + return MAX_INT; + + return static_cast<int>(deltaMsec); +} + +bool CommonTimeServer::shouldPanicNotGettingGoodData() { + if (mClient_FirstSyncTX) { + int64_t now = mLocalClock.getLocalTime(); + int64_t delta = now - (mClient_LastGoodSyncRX + ? mClient_LastGoodSyncRX + : mClient_FirstSyncTX); + int64_t deltaUsec = mCommonClock.localDurationToCommonDuration(delta); + + if (deltaUsec >= kNoGoodDataPanicThresholdUsec) + return true; + } + + return false; +} + +void CommonTimeServer::PacketRTTLog::logTX(int64_t txTime) { + txTimes[wrPtr] = txTime; + rxTimes[wrPtr] = 0; + wrPtr = (wrPtr + 1) % RTT_LOG_SIZE; + if (!wrPtr) + logFull = true; +} + +void CommonTimeServer::PacketRTTLog::logRX(int64_t txTime, int64_t rxTime) { + if (!logFull && !wrPtr) + return; + + uint32_t i = logFull ? wrPtr : 0; + do { + if (txTimes[i] == txTime) { + rxTimes[i] = rxTime; + break; + } + i = (i + 1) % RTT_LOG_SIZE; + } while (i != wrPtr); +} + +} // namespace android diff --git a/services/common_time/common_time_server.h b/services/common_time/common_time_server.h new file mode 100644 index 000000000000..a0f549f6d8f9 --- /dev/null +++ b/services/common_time/common_time_server.h @@ -0,0 +1,330 @@ +/* + * Copyright (C) 2012 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_COMMON_TIME_SERVER_H +#define ANDROID_COMMON_TIME_SERVER_H + +#include <arpa/inet.h> +#include <stdint.h> +#include <linux/socket.h> + +#include <common_time/ICommonClock.h> +#include <common_time/local_clock.h> +#include <utils/String8.h> + +#include "clock_recovery.h" +#include "common_clock.h" +#include "common_time_server_packets.h" + +#define RTT_LOG_SIZE 30 + +namespace android { + +class CommonClockService; +class CommonTimeConfigService; + +/***** time service implementation *****/ + +class CommonTimeServer : public Thread { + public: + CommonTimeServer(); + ~CommonTimeServer(); + + bool startServices(); + + // Common Clock API methods + CommonClock& getCommonClock() { return mCommonClock; } + LocalClock& getLocalClock() { return mLocalClock; } + uint64_t getTimelineID(); + int32_t getEstimatedError(); + ICommonClock::State getState(); + status_t getMasterAddr(struct sockaddr_storage* addr); + status_t isCommonTimeValid(bool* valid, uint32_t* timelineID); + + // Config API methods + status_t getMasterElectionPriority(uint8_t *priority); + status_t setMasterElectionPriority(uint8_t priority); + status_t getMasterElectionEndpoint(struct sockaddr_storage *addr); + status_t setMasterElectionEndpoint(const struct sockaddr_storage *addr); + status_t getMasterElectionGroupId(uint64_t *id); + status_t setMasterElectionGroupId(uint64_t id); + status_t getInterfaceBinding(String8& ifaceName); + status_t setInterfaceBinding(const String8& ifaceName); + status_t getMasterAnnounceInterval(int *interval); + status_t setMasterAnnounceInterval(int interval); + status_t getClientSyncInterval(int *interval); + status_t setClientSyncInterval(int interval); + status_t getPanicThreshold(int *threshold); + status_t setPanicThreshold(int threshold); + status_t getAutoDisable(bool *autoDisable); + status_t setAutoDisable(bool autoDisable); + status_t forceNetworklessMasterMode(); + + // Method used by the CommonClockService to notify the core service about + // changes in the number of active common clock clients. + void reevaluateAutoDisableState(bool commonClockHasClients); + + status_t dumpClockInterface(int fd, const Vector<String16>& args, + size_t activeClients); + status_t dumpConfigInterface(int fd, const Vector<String16>& args); + + private: + class PacketRTTLog { + public: + PacketRTTLog() { + resetLog(); + } + + void resetLog() { + wrPtr = 0; + logFull = 0; + } + + void logTX(int64_t txTime); + void logRX(int64_t txTime, int64_t rxTime); + void dumpLog(int fd, const CommonClock& cclk); + + private: + uint32_t wrPtr; + bool logFull; + int64_t txTimes[RTT_LOG_SIZE]; + int64_t rxTimes[RTT_LOG_SIZE]; + }; + + class TimeoutHelper { + public: + TimeoutHelper() : mTimeoutValid(false) { } + + void setTimeout(int msec); + int msecTillTimeout(); + + private: + bool mTimeoutValid; + nsecs_t mEndTime; + }; + + bool threadLoop(); + + bool runStateMachine_l(); + bool setupSocket_l(); + + void assignTimelineID(); + bool assignDeviceID(); + + static bool arbitrateMaster(uint64_t deviceID1, uint8_t devicePrio1, + uint64_t deviceID2, uint8_t devicePrio2); + + bool handlePacket(); + bool handleWhoIsMasterRequest (const WhoIsMasterRequestPacket* request, + const sockaddr_storage& srcAddr); + bool handleWhoIsMasterResponse(const WhoIsMasterResponsePacket* response, + const sockaddr_storage& srcAddr); + bool handleSyncRequest (const SyncRequestPacket* request, + const sockaddr_storage& srcAddr); + bool handleSyncResponse (const SyncResponsePacket* response, + const sockaddr_storage& srcAddr); + bool handleMasterAnnouncement (const MasterAnnouncementPacket* packet, + const sockaddr_storage& srcAddr); + + bool handleTimeout(); + bool handleTimeoutInitial(); + bool handleTimeoutClient(); + bool handleTimeoutMaster(); + bool handleTimeoutRonin(); + bool handleTimeoutWaitForElection(); + + bool sendWhoIsMasterRequest(); + bool sendSyncRequest(); + bool sendMasterAnnouncement(); + + bool becomeClient(const sockaddr_storage& masterAddr, + uint64_t masterDeviceID, + uint8_t masterDevicePriority, + uint64_t timelineID, + const char* cause); + bool becomeMaster(const char* cause); + bool becomeRonin(const char* cause); + bool becomeWaitForElection(const char* cause); + bool becomeInitial(const char* cause); + + void notifyClockSync(); + void notifyClockSyncLoss(); + + ICommonClock::State mState; + void setState(ICommonClock::State s); + + void clearPendingWakeupEvents_l(); + void wakeupThread_l(); + void cleanupSocket_l(); + void shutdownThread(); + + inline uint8_t effectivePriority() const { + return (mMasterPriority & 0x7F) | + (mForceLowPriority ? 0x00 : 0x80); + } + + inline bool shouldAutoDisable() const { + return (mAutoDisable && !mCommonClockHasClients); + } + + inline void resetSyncStats() { + mClient_SyncRequestPending = false; + mClient_SyncRequestTimeouts = 0; + mClient_SyncsSentToCurMaster = 0; + mClient_SyncRespsRXedFromCurMaster = 0; + mClient_ExpiredSyncRespsRXedFromCurMaster = 0; + mClient_FirstSyncTX = 0; + mClient_LastGoodSyncRX = 0; + mClient_PacketRTTLog.resetLog(); + } + + bool shouldPanicNotGettingGoodData(); + + // Helper to keep track of the state machine's current timeout + TimeoutHelper mCurTimeout; + + // common clock, local clock abstraction, and clock recovery loop + CommonClock mCommonClock; + LocalClock mLocalClock; + ClockRecoveryLoop mClockRecovery; + + // implementation of ICommonClock + sp<CommonClockService> mICommonClock; + + // implementation of ICommonTimeConfig + sp<CommonTimeConfigService> mICommonTimeConfig; + + // UDP socket for the time sync protocol + int mSocket; + + // eventfd used to wakeup the work thread in response to configuration + // changes. + int mWakeupThreadFD; + + // timestamp captured when a packet is received + int64_t mLastPacketRxLocalTime; + + // ID of the timeline that this device is following + uint64_t mTimelineID; + + // flag for whether the clock has been synced to a timeline + bool mClockSynced; + + // flag used to indicate that clients should be considered to be lower + // priority than all of their peers during elections. This flag is set and + // cleared by the state machine. It is set when the client joins a new + // network. If the client had been a master in the old network (or an + // isolated master with no network connectivity) it should defer to any + // masters which may already be on the network. It will be cleared whenever + // the state machine transitions to the master state. + bool mForceLowPriority; + inline void setForceLowPriority(bool val) { + mForceLowPriority = val; + if (mState == ICommonClock::STATE_MASTER) + mClient_MasterDevicePriority = effectivePriority(); + } + + // Lock to synchronize access to internal state and configuration. + Mutex mLock; + + // Flag updated by the common clock service to indicate that it does or does + // not currently have registered clients. When the the auto disable flag is + // cleared on the common time service, the service will participate in + // network synchronization whenever it has a valid network interface to bind + // to. When the auto disable flag is set on the common time service, it + // will only participate in network synchronization when it has both a valid + // interface AND currently active common clock clients. + bool mCommonClockHasClients; + + // Configuration info + struct sockaddr_storage mMasterElectionEP; // Endpoint over which we conduct master election + String8 mBindIface; // Endpoint for the service to bind to. + bool mBindIfaceValid; // whether or not the bind Iface is valid. + bool mBindIfaceDirty; // whether or not the bind Iface is valid. + struct sockaddr_storage mMasterEP; // Endpoint of our current master (if any) + bool mMasterEPValid; + uint64_t mDeviceID; // unique ID of this device + uint64_t mSyncGroupID; // synchronization group ID of this device. + uint8_t mMasterPriority; // Priority of this device in master election. + uint32_t mMasterAnnounceIntervalMs; + uint32_t mSyncRequestIntervalMs; + uint32_t mPanicThresholdUsec; + bool mAutoDisable; + + // Config defaults. + static const char* kDefaultMasterElectionAddr; + static const uint16_t kDefaultMasterElectionPort; + static const uint64_t kDefaultSyncGroupID; + static const uint8_t kDefaultMasterPriority; + static const uint32_t kDefaultMasterAnnounceIntervalMs; + static const uint32_t kDefaultSyncRequestIntervalMs; + static const uint32_t kDefaultPanicThresholdUsec; + static const bool kDefaultAutoDisable; + + // Priority mask and shift fields. + static const uint64_t kDeviceIDMask; + static const uint8_t kDevicePriorityMask; + static const uint8_t kDevicePriorityHiLowBit; + static const uint32_t kDevicePriorityShift; + + // Unconfgurable constants + static const int kSetupRetryTimeoutMs; + static const int64_t kNoGoodDataPanicThresholdUsec; + static const uint32_t kRTTDiscardPanicThreshMultiplier; + + /*** status while in the Initial state ***/ + int mInitial_WhoIsMasterRequestTimeouts; + static const int kInitial_NumWhoIsMasterRetries; + static const int kInitial_WhoIsMasterTimeoutMs; + + /*** status while in the Client state ***/ + uint64_t mClient_MasterDeviceID; + uint8_t mClient_MasterDevicePriority; + bool mClient_SyncRequestPending; + int mClient_SyncRequestTimeouts; + uint32_t mClient_SyncsSentToCurMaster; + uint32_t mClient_SyncRespsRXedFromCurMaster; + uint32_t mClient_ExpiredSyncRespsRXedFromCurMaster; + int64_t mClient_FirstSyncTX; + int64_t mClient_LastGoodSyncRX; + PacketRTTLog mClient_PacketRTTLog; + static const int kClient_NumSyncRequestRetries; + + + /*** status while in the Master state ***/ + static const uint32_t kDefaultMaster_AnnouncementIntervalMs; + + /*** status while in the Ronin state ***/ + int mRonin_WhoIsMasterRequestTimeouts; + static const int kRonin_NumWhoIsMasterRetries; + static const int kRonin_WhoIsMasterTimeoutMs; + + /*** status while in the WaitForElection state ***/ + static const int kWaitForElection_TimeoutMs; + + static const int kInfiniteTimeout; + + static const char* stateToString(ICommonClock::State s); + static void sockaddrToString(const sockaddr_storage& addr, bool addrValid, + char* buf, size_t bufLen); + static bool sockaddrMatch(const sockaddr_storage& a1, + const sockaddr_storage& a2, + bool matchAddressOnly); +}; + +} // namespace android + +#endif // ANDROID_COMMON_TIME_SERVER_H diff --git a/services/common_time/common_time_server_api.cpp b/services/common_time/common_time_server_api.cpp new file mode 100644 index 000000000000..fb8c2615e1c0 --- /dev/null +++ b/services/common_time/common_time_server_api.cpp @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2012 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. + */ + +/* + * A service that exchanges time synchronization information between + * a master that defines a timeline and clients that follow the timeline. + */ + +#define LOG_TAG "common_time" +#include <utils/Log.h> + +#include <binder/IServiceManager.h> +#include <binder/IPCThreadState.h> + +#include "common_time_server.h" + +namespace android { + +// +// Clock API +// +uint64_t CommonTimeServer::getTimelineID() { + AutoMutex _lock(&mLock); + return mTimelineID; +} + +ICommonClock::State CommonTimeServer::getState() { + AutoMutex _lock(&mLock); + return mState; +} + +status_t CommonTimeServer::getMasterAddr(struct sockaddr_storage* addr) { + AutoMutex _lock(&mLock); + if (mMasterEPValid) { + memcpy(addr, &mMasterEP, sizeof(*addr)); + return OK; + } + + return UNKNOWN_ERROR; +} + +int32_t CommonTimeServer::getEstimatedError() { + AutoMutex _lock(&mLock); + + if (ICommonClock::STATE_MASTER == mState) + return 0; + + if (!mClockSynced) + return ICommonClock::kErrorEstimateUnknown; + + return mClockRecovery.getLastErrorEstimate(); +} + +status_t CommonTimeServer::isCommonTimeValid(bool* valid, + uint32_t* timelineID) { + AutoMutex _lock(&mLock); + *valid = mCommonClock.isValid(); + *timelineID = mTimelineID; + return OK; +} + +// +// Config API +// +status_t CommonTimeServer::getMasterElectionPriority(uint8_t *priority) { + AutoMutex _lock(&mLock); + *priority = mMasterPriority; + return OK; +} + +status_t CommonTimeServer::setMasterElectionPriority(uint8_t priority) { + AutoMutex _lock(&mLock); + + if (priority > 0x7F) + return BAD_VALUE; + + mMasterPriority = priority; + return OK; +} + +status_t CommonTimeServer::getMasterElectionEndpoint( + struct sockaddr_storage *addr) { + AutoMutex _lock(&mLock); + memcpy(addr, &mMasterElectionEP, sizeof(*addr)); + return OK; +} + +status_t CommonTimeServer::setMasterElectionEndpoint( + const struct sockaddr_storage *addr) { + AutoMutex _lock(&mLock); + + if (!addr) + return BAD_VALUE; + + // TODO: add proper support for IPv6 + if (addr->ss_family != AF_INET) + return BAD_VALUE; + + // Only multicast and broadcast endpoints with explicit ports are allowed. + uint16_t ipv4Port = ntohs( + reinterpret_cast<const struct sockaddr_in*>(addr)->sin_port); + if (!ipv4Port) + return BAD_VALUE; + + uint32_t ipv4Addr = ntohl( + reinterpret_cast<const struct sockaddr_in*>(addr)->sin_addr.s_addr); + if ((ipv4Addr != 0xFFFFFFFF) && (0xE0000000 != (ipv4Addr & 0xF0000000))) + return BAD_VALUE; + + memcpy(&mMasterElectionEP, addr, sizeof(mMasterElectionEP)); + + // Force a rebind in order to change election enpoints. + mBindIfaceDirty = true; + wakeupThread_l(); + return OK; +} + +status_t CommonTimeServer::getMasterElectionGroupId(uint64_t *id) { + AutoMutex _lock(&mLock); + *id = mSyncGroupID; + return OK; +} + +status_t CommonTimeServer::setMasterElectionGroupId(uint64_t id) { + AutoMutex _lock(&mLock); + mSyncGroupID = id; + return OK; +} + +status_t CommonTimeServer::getInterfaceBinding(String8& ifaceName) { + AutoMutex _lock(&mLock); + if (!mBindIfaceValid) + return INVALID_OPERATION; + ifaceName = mBindIface; + return OK; +} + +status_t CommonTimeServer::setInterfaceBinding(const String8& ifaceName) { + AutoMutex _lock(&mLock); + + mBindIfaceDirty = true; + if (ifaceName.size()) { + mBindIfaceValid = true; + mBindIface = ifaceName; + } else { + mBindIfaceValid = false; + mBindIface.clear(); + } + + wakeupThread_l(); + return OK; +} + +status_t CommonTimeServer::getMasterAnnounceInterval(int *interval) { + AutoMutex _lock(&mLock); + *interval = mMasterAnnounceIntervalMs; + return OK; +} + +status_t CommonTimeServer::setMasterAnnounceInterval(int interval) { + AutoMutex _lock(&mLock); + + if (interval > (6 *3600000)) // Max interval is once every 6 hrs + return BAD_VALUE; + + if (interval < 500) // Min interval is once per 0.5 seconds + return BAD_VALUE; + + mMasterAnnounceIntervalMs = interval; + if (ICommonClock::STATE_MASTER == mState) { + int pendingTimeout = mCurTimeout.msecTillTimeout(); + if ((kInfiniteTimeout == pendingTimeout) || + (pendingTimeout > interval)) { + mCurTimeout.setTimeout(mMasterAnnounceIntervalMs); + wakeupThread_l(); + } + } + + return OK; +} + +status_t CommonTimeServer::getClientSyncInterval(int *interval) { + AutoMutex _lock(&mLock); + *interval = mSyncRequestIntervalMs; + return OK; +} + +status_t CommonTimeServer::setClientSyncInterval(int interval) { + AutoMutex _lock(&mLock); + + if (interval > (3600000)) // Max interval is once every 60 min + return BAD_VALUE; + + if (interval < 250) // Min interval is once per 0.25 seconds + return BAD_VALUE; + + mSyncRequestIntervalMs = interval; + if (ICommonClock::STATE_CLIENT == mState) { + int pendingTimeout = mCurTimeout.msecTillTimeout(); + if ((kInfiniteTimeout == pendingTimeout) || + (pendingTimeout > interval)) { + mCurTimeout.setTimeout(mSyncRequestIntervalMs); + wakeupThread_l(); + } + } + + return OK; +} + +status_t CommonTimeServer::getPanicThreshold(int *threshold) { + AutoMutex _lock(&mLock); + *threshold = mPanicThresholdUsec; + return OK; +} + +status_t CommonTimeServer::setPanicThreshold(int threshold) { + AutoMutex _lock(&mLock); + + if (threshold < 1000) // Min threshold is 1mSec + return BAD_VALUE; + + mPanicThresholdUsec = threshold; + return OK; +} + +status_t CommonTimeServer::getAutoDisable(bool *autoDisable) { + AutoMutex _lock(&mLock); + *autoDisable = mAutoDisable; + return OK; +} + +status_t CommonTimeServer::setAutoDisable(bool autoDisable) { + AutoMutex _lock(&mLock); + mAutoDisable = autoDisable; + wakeupThread_l(); + return OK; +} + +status_t CommonTimeServer::forceNetworklessMasterMode() { + AutoMutex _lock(&mLock); + + // Can't force networkless master mode if we are currently bound to a + // network. + if (mSocket >= 0) + return INVALID_OPERATION; + + becomeMaster("force networkless"); + + return OK; +} + +void CommonTimeServer::reevaluateAutoDisableState(bool commonClockHasClients) { + AutoMutex _lock(&mLock); + bool needWakeup = (mAutoDisable && mMasterEPValid && + (commonClockHasClients != mCommonClockHasClients)); + + mCommonClockHasClients = commonClockHasClients; + + if (needWakeup) { + ALOGI("Waking up service, auto-disable is engaged and service now has%s" + " clients", mCommonClockHasClients ? "" : " no"); + wakeupThread_l(); + } +} + +#define dump_printf(a, b...) do { \ + int res; \ + res = snprintf(buffer, sizeof(buffer), a, b); \ + buffer[sizeof(buffer) - 1] = 0; \ + if (res > 0) \ + write(fd, buffer, res); \ +} while (0) +#define checked_percentage(a, b) ((0 == b) ? 0.0f : ((100.0f * a) / b)) + +status_t CommonTimeServer::dumpClockInterface(int fd, + const Vector<String16>& args, + size_t activeClients) { + AutoMutex _lock(&mLock); + const size_t SIZE = 256; + char buffer[SIZE]; + + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump CommonClockService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + write(fd, buffer, strlen(buffer)); + } else { + int64_t commonTime; + int64_t localTime; + bool synced; + char maStr[64]; + + localTime = mLocalClock.getLocalTime(); + synced = (OK == mCommonClock.localToCommon(localTime, &commonTime)); + sockaddrToString(mMasterEP, mMasterEPValid, maStr, sizeof(maStr)); + + dump_printf("Common Clock Service Status\nLocal time : %lld\n", + localTime); + + if (synced) + dump_printf("Common time : %lld\n", commonTime); + else + dump_printf("Common time : %s\n", "not synced"); + + dump_printf("Timeline ID : %016llx\n", mTimelineID); + dump_printf("State : %s\n", stateToString(mState)); + dump_printf("Master Addr : %s\n", maStr); + + + if (synced) { + int32_t est = (ICommonClock::STATE_MASTER != mState) + ? mClockRecovery.getLastErrorEstimate() + : 0; + dump_printf("Error Est. : %.3f msec\n", + static_cast<float>(est) / 1000.0); + } else { + dump_printf("Error Est. : %s\n", "unknown"); + } + + dump_printf("Syncs TXes : %u\n", mClient_SyncsSentToCurMaster); + dump_printf("Syncs RXes : %u (%.2f%%)\n", + mClient_SyncRespsRXedFromCurMaster, + checked_percentage( + mClient_SyncRespsRXedFromCurMaster, + mClient_SyncsSentToCurMaster)); + dump_printf("RXs Expired : %u (%.2f%%)\n", + mClient_ExpiredSyncRespsRXedFromCurMaster, + checked_percentage( + mClient_ExpiredSyncRespsRXedFromCurMaster, + mClient_SyncsSentToCurMaster)); + + if (!mClient_LastGoodSyncRX) { + dump_printf("Last Good RX : %s\n", "unknown"); + } else { + int64_t localDelta, usecDelta; + localDelta = localTime - mClient_LastGoodSyncRX; + usecDelta = mCommonClock.localDurationToCommonDuration(localDelta); + dump_printf("Last Good RX : %lld uSec ago\n", usecDelta); + } + + dump_printf("Active Clients : %u\n", activeClients); + mClient_PacketRTTLog.dumpLog(fd, mCommonClock); + } + + return NO_ERROR; +} + +status_t CommonTimeServer::dumpConfigInterface(int fd, + const Vector<String16>& args) { + AutoMutex _lock(&mLock); + const size_t SIZE = 256; + char buffer[SIZE]; + + if (checkCallingPermission(String16("android.permission.DUMP")) == false) { + snprintf(buffer, SIZE, "Permission Denial: " + "can't dump CommonTimeConfigService from pid=%d, uid=%d\n", + IPCThreadState::self()->getCallingPid(), + IPCThreadState::self()->getCallingUid()); + write(fd, buffer, strlen(buffer)); + } else { + char meStr[64]; + + sockaddrToString(mMasterElectionEP, true, meStr, sizeof(meStr)); + + dump_printf("Common Time Config Service Status\n" + "Bound Interface : %s\n", + mBindIfaceValid ? mBindIface.string() : "<unbound>"); + dump_printf("Master Election Endpoint : %s\n", meStr); + dump_printf("Master Election Group ID : %016llx\n", mSyncGroupID); + dump_printf("Master Announce Interval : %d mSec\n", + mMasterAnnounceIntervalMs); + dump_printf("Client Sync Interval : %d mSec\n", + mSyncRequestIntervalMs); + dump_printf("Panic Threshold : %d uSec\n", + mPanicThresholdUsec); + dump_printf("Base ME Prio : 0x%02x\n", + static_cast<uint32_t>(mMasterPriority)); + dump_printf("Effective ME Prio : 0x%02x\n", + static_cast<uint32_t>(effectivePriority())); + dump_printf("Auto Disable Allowed : %s\n", + mAutoDisable ? "yes" : "no"); + dump_printf("Auto Disable Engaged : %s\n", + shouldAutoDisable() ? "yes" : "no"); + } + + return NO_ERROR; +} + +void CommonTimeServer::PacketRTTLog::dumpLog(int fd, const CommonClock& cclk) { + const size_t SIZE = 256; + char buffer[SIZE]; + uint32_t avail = !logFull ? wrPtr : RTT_LOG_SIZE; + + if (!avail) + return; + + dump_printf("\nPacket Log (%d entries)\n", avail); + + uint32_t ndx = 0; + uint32_t i = logFull ? wrPtr : 0; + do { + if (rxTimes[i]) { + int64_t delta = rxTimes[i] - txTimes[i]; + int64_t deltaUsec = cclk.localDurationToCommonDuration(delta); + dump_printf("pkt[%2d] : localTX %12lld localRX %12lld " + "(%.3f msec RTT)\n", + ndx, txTimes[i], rxTimes[i], + static_cast<float>(deltaUsec) / 1000.0); + } else { + dump_printf("pkt[%2d] : localTX %12lld localRX never\n", + ndx, txTimes[i]); + } + i = (i + 1) % RTT_LOG_SIZE; + ndx++; + } while (i != wrPtr); +} + +#undef dump_printf +#undef checked_percentage + +} // namespace android diff --git a/services/common_time/common_time_server_packets.cpp b/services/common_time/common_time_server_packets.cpp new file mode 100644 index 000000000000..9833c37f2519 --- /dev/null +++ b/services/common_time/common_time_server_packets.cpp @@ -0,0 +1,293 @@ +/* + * Copyright (C) 2012 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. + */ + +/* + * A service that exchanges time synchronization information between + * a master that defines a timeline and clients that follow the timeline. + */ + +#define LOG_TAG "common_time" +#include <utils/Log.h> + +#include <arpa/inet.h> +#include <stdint.h> + +#include "common_time_server_packets.h" + +namespace android { + +const uint32_t TimeServicePacketHeader::kMagic = + (static_cast<uint32_t>('c') << 24) | + (static_cast<uint32_t>('c') << 16) | + (static_cast<uint32_t>('l') << 8) | + static_cast<uint32_t>('k'); + +const uint16_t TimeServicePacketHeader::kCurVersion = 1; + +#define SERIALIZE_FIELD(field_name, type, converter) \ + do { \ + if ((offset + sizeof(field_name)) > length) \ + return -1; \ + *((type*)(data + offset)) = converter(field_name); \ + offset += sizeof(field_name); \ + } while (0) +#define SERIALIZE_INT16(field_name) SERIALIZE_FIELD(field_name, int16_t, htons) +#define SERIALIZE_INT32(field_name) SERIALIZE_FIELD(field_name, int32_t, htonl) +#define SERIALIZE_INT64(field_name) SERIALIZE_FIELD(field_name, int64_t, htonq) + +#define DESERIALIZE_FIELD(field_name, type, converter) \ + do { \ + if ((offset + sizeof(field_name)) > length) \ + return -1; \ + field_name = converter(*((type*)(data + offset))); \ + offset += sizeof(field_name); \ + } while (0) +#define DESERIALIZE_INT16(field_name) DESERIALIZE_FIELD(field_name, int16_t, ntohs) +#define DESERIALIZE_INT32(field_name) DESERIALIZE_FIELD(field_name, int32_t, ntohl) +#define DESERIALIZE_INT64(field_name) DESERIALIZE_FIELD(field_name, int64_t, ntohq) + +#define kDevicePriorityShift 56 +#define kDeviceIDMask ((static_cast<uint64_t>(1) << kDevicePriorityShift) - 1) + +inline uint64_t packDeviceID(uint64_t devID, uint8_t prio) { + return (devID & kDeviceIDMask) | + (static_cast<uint64_t>(prio) << kDevicePriorityShift); +} + +inline uint64_t unpackDeviceID(uint64_t packed) { + return (packed & kDeviceIDMask); +} + +inline uint8_t unpackDevicePriority(uint64_t packed) { + return static_cast<uint8_t>(packed >> kDevicePriorityShift); +} + +ssize_t TimeServicePacketHeader::serializeHeader(uint8_t* data, + uint32_t length) { + ssize_t offset = 0; + int16_t pktType = static_cast<int16_t>(packetType); + SERIALIZE_INT32(magic); + SERIALIZE_INT16(version); + SERIALIZE_INT16(pktType); + SERIALIZE_INT64(timelineID); + SERIALIZE_INT64(syncGroupID); + return offset; +} + +ssize_t TimeServicePacketHeader::deserializeHeader(const uint8_t* data, + uint32_t length) { + ssize_t offset = 0; + int16_t tmp; + DESERIALIZE_INT32(magic); + DESERIALIZE_INT16(version); + DESERIALIZE_INT16(tmp); + DESERIALIZE_INT64(timelineID); + DESERIALIZE_INT64(syncGroupID); + packetType = static_cast<TimeServicePacketType>(tmp); + return offset; +} + +ssize_t TimeServicePacketHeader::serializePacket(uint8_t* data, + uint32_t length) { + ssize_t ret, tmp; + + ret = serializeHeader(data, length); + if (ret < 0) + return ret; + + data += ret; + length -= ret; + + switch (packetType) { + case TIME_PACKET_WHO_IS_MASTER_REQUEST: + tmp =((WhoIsMasterRequestPacket*)(this))->serializePacket(data, + length); + break; + case TIME_PACKET_WHO_IS_MASTER_RESPONSE: + tmp =((WhoIsMasterResponsePacket*)(this))->serializePacket(data, + length); + break; + case TIME_PACKET_SYNC_REQUEST: + tmp =((SyncRequestPacket*)(this))->serializePacket(data, length); + break; + case TIME_PACKET_SYNC_RESPONSE: + tmp =((SyncResponsePacket*)(this))->serializePacket(data, length); + break; + case TIME_PACKET_MASTER_ANNOUNCEMENT: + tmp =((MasterAnnouncementPacket*)(this))->serializePacket(data, + length); + break; + default: + return -1; + } + + if (tmp < 0) + return tmp; + + return ret + tmp; +} + +ssize_t UniversalTimeServicePacket::deserializePacket( + const uint8_t* data, + uint32_t length, + uint64_t expectedSyncGroupID) { + ssize_t ret; + TimeServicePacketHeader* header; + if (length < 8) + return -1; + + packetType = ntohs(*((uint16_t*)(data + 6))); + switch (packetType) { + case TIME_PACKET_WHO_IS_MASTER_REQUEST: + ret = p.who_is_master_request.deserializePacket(data, length); + header = &p.who_is_master_request; + break; + case TIME_PACKET_WHO_IS_MASTER_RESPONSE: + ret = p.who_is_master_response.deserializePacket(data, length); + header = &p.who_is_master_response; + break; + case TIME_PACKET_SYNC_REQUEST: + ret = p.sync_request.deserializePacket(data, length); + header = &p.sync_request; + break; + case TIME_PACKET_SYNC_RESPONSE: + ret = p.sync_response.deserializePacket(data, length); + header = &p.sync_response; + break; + case TIME_PACKET_MASTER_ANNOUNCEMENT: + ret = p.master_announcement.deserializePacket(data, length); + header = &p.master_announcement; + break; + default: + return -1; + } + + if ((ret >= 0) && !header->checkPacket(expectedSyncGroupID)) + ret = -1; + + return ret; +} + +ssize_t WhoIsMasterRequestPacket::serializePacket(uint8_t* data, + uint32_t length) { + ssize_t offset = serializeHeader(data, length); + if (offset > 0) { + uint64_t packed = packDeviceID(senderDeviceID, senderDevicePriority); + SERIALIZE_INT64(packed); + } + return offset; +} + +ssize_t WhoIsMasterRequestPacket::deserializePacket(const uint8_t* data, + uint32_t length) { + ssize_t offset = deserializeHeader(data, length); + if (offset > 0) { + uint64_t packed; + DESERIALIZE_INT64(packed); + senderDeviceID = unpackDeviceID(packed); + senderDevicePriority = unpackDevicePriority(packed); + } + return offset; +} + +ssize_t WhoIsMasterResponsePacket::serializePacket(uint8_t* data, + uint32_t length) { + ssize_t offset = serializeHeader(data, length); + if (offset > 0) { + uint64_t packed = packDeviceID(deviceID, devicePriority); + SERIALIZE_INT64(packed); + } + return offset; +} + +ssize_t WhoIsMasterResponsePacket::deserializePacket(const uint8_t* data, + uint32_t length) { + ssize_t offset = deserializeHeader(data, length); + if (offset > 0) { + uint64_t packed; + DESERIALIZE_INT64(packed); + deviceID = unpackDeviceID(packed); + devicePriority = unpackDevicePriority(packed); + } + return offset; +} + +ssize_t SyncRequestPacket::serializePacket(uint8_t* data, + uint32_t length) { + ssize_t offset = serializeHeader(data, length); + if (offset > 0) { + SERIALIZE_INT64(clientTxLocalTime); + } + return offset; +} + +ssize_t SyncRequestPacket::deserializePacket(const uint8_t* data, + uint32_t length) { + ssize_t offset = deserializeHeader(data, length); + if (offset > 0) { + DESERIALIZE_INT64(clientTxLocalTime); + } + return offset; +} + +ssize_t SyncResponsePacket::serializePacket(uint8_t* data, + uint32_t length) { + ssize_t offset = serializeHeader(data, length); + if (offset > 0) { + SERIALIZE_INT64(clientTxLocalTime); + SERIALIZE_INT64(masterRxCommonTime); + SERIALIZE_INT64(masterTxCommonTime); + SERIALIZE_INT32(nak); + } + return offset; +} + +ssize_t SyncResponsePacket::deserializePacket(const uint8_t* data, + uint32_t length) { + ssize_t offset = deserializeHeader(data, length); + if (offset > 0) { + DESERIALIZE_INT64(clientTxLocalTime); + DESERIALIZE_INT64(masterRxCommonTime); + DESERIALIZE_INT64(masterTxCommonTime); + DESERIALIZE_INT32(nak); + } + return offset; +} + +ssize_t MasterAnnouncementPacket::serializePacket(uint8_t* data, + uint32_t length) { + ssize_t offset = serializeHeader(data, length); + if (offset > 0) { + uint64_t packed = packDeviceID(deviceID, devicePriority); + SERIALIZE_INT64(packed); + } + return offset; +} + +ssize_t MasterAnnouncementPacket::deserializePacket(const uint8_t* data, + uint32_t length) { + ssize_t offset = deserializeHeader(data, length); + if (offset > 0) { + uint64_t packed; + DESERIALIZE_INT64(packed); + deviceID = unpackDeviceID(packed); + devicePriority = unpackDevicePriority(packed); + } + return offset; +} + +} // namespace android + diff --git a/services/common_time/common_time_server_packets.h b/services/common_time/common_time_server_packets.h new file mode 100644 index 000000000000..57ba8a256308 --- /dev/null +++ b/services/common_time/common_time_server_packets.h @@ -0,0 +1,189 @@ +/* + * Copyright (C) 2012 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_COMMON_TIME_SERVER_PACKETS_H +#define ANDROID_COMMON_TIME_SERVER_PACKETS_H + +#include <stdint.h> +#include <common_time/ICommonClock.h> + +namespace android { + +/***** time sync protocol packets *****/ + +enum TimeServicePacketType { + TIME_PACKET_WHO_IS_MASTER_REQUEST = 1, + TIME_PACKET_WHO_IS_MASTER_RESPONSE, + TIME_PACKET_SYNC_REQUEST, + TIME_PACKET_SYNC_RESPONSE, + TIME_PACKET_MASTER_ANNOUNCEMENT, +}; + +class TimeServicePacketHeader { + public: + friend class UniversalTimeServicePacket; + // magic number identifying the protocol + uint32_t magic; + + // protocol version of the packet + uint16_t version; + + // type of the packet + TimeServicePacketType packetType; + + // the timeline ID + uint64_t timelineID; + + // synchronization group this packet belongs to (used to operate multiple + // synchronization domains which all use the same master election endpoint) + uint64_t syncGroupID; + + ssize_t serializePacket(uint8_t* data, uint32_t length); + + protected: + void initHeader(TimeServicePacketType type, + const uint64_t tlID, + const uint64_t groupID) { + magic = kMagic; + version = kCurVersion; + packetType = type; + timelineID = tlID; + syncGroupID = groupID; + } + + bool checkPacket(uint64_t expectedSyncGroupID) const { + return ((magic == kMagic) && + (version == kCurVersion) && + (!expectedSyncGroupID || (syncGroupID == expectedSyncGroupID))); + } + + ssize_t serializeHeader(uint8_t* data, uint32_t length); + ssize_t deserializeHeader(const uint8_t* data, uint32_t length); + + private: + static const uint32_t kMagic; + static const uint16_t kCurVersion; +}; + +// packet querying for a suitable master +class WhoIsMasterRequestPacket : public TimeServicePacketHeader { + public: + uint64_t senderDeviceID; + uint8_t senderDevicePriority; + + void initHeader(const uint64_t groupID) { + TimeServicePacketHeader::initHeader(TIME_PACKET_WHO_IS_MASTER_REQUEST, + ICommonClock::kInvalidTimelineID, + groupID); + } + + ssize_t serializePacket(uint8_t* data, uint32_t length); + ssize_t deserializePacket(const uint8_t* data, uint32_t length); +}; + +// response to a WhoIsMaster request +class WhoIsMasterResponsePacket : public TimeServicePacketHeader { + public: + uint64_t deviceID; + uint8_t devicePriority; + + void initHeader(const uint64_t tlID, const uint64_t groupID) { + TimeServicePacketHeader::initHeader(TIME_PACKET_WHO_IS_MASTER_RESPONSE, + tlID, groupID); + } + + ssize_t serializePacket(uint8_t* data, uint32_t length); + ssize_t deserializePacket(const uint8_t* data, uint32_t length); +}; + +// packet sent by a client requesting correspondence between local +// and common time +class SyncRequestPacket : public TimeServicePacketHeader { + public: + // local time when this request was transmitted + int64_t clientTxLocalTime; + + void initHeader(const uint64_t tlID, const uint64_t groupID) { + TimeServicePacketHeader::initHeader(TIME_PACKET_SYNC_REQUEST, + tlID, groupID); + } + + ssize_t serializePacket(uint8_t* data, uint32_t length); + ssize_t deserializePacket(const uint8_t* data, uint32_t length); +}; + +// response to a sync request sent by the master +class SyncResponsePacket : public TimeServicePacketHeader { + public: + // local time when this request was transmitted by the client + int64_t clientTxLocalTime; + + // common time when the master received the request + int64_t masterRxCommonTime; + + // common time when the master transmitted the response + int64_t masterTxCommonTime; + + // flag that is set if the recipient of the sync request is not acting + // as a master for the requested timeline + uint32_t nak; + + void initHeader(const uint64_t tlID, const uint64_t groupID) { + TimeServicePacketHeader::initHeader(TIME_PACKET_SYNC_RESPONSE, + tlID, groupID); + } + + ssize_t serializePacket(uint8_t* data, uint32_t length); + ssize_t deserializePacket(const uint8_t* data, uint32_t length); +}; + +// announcement of the master's presence +class MasterAnnouncementPacket : public TimeServicePacketHeader { + public: + // the master's device ID + uint64_t deviceID; + uint8_t devicePriority; + + void initHeader(const uint64_t tlID, const uint64_t groupID) { + TimeServicePacketHeader::initHeader(TIME_PACKET_MASTER_ANNOUNCEMENT, + tlID, groupID); + } + + ssize_t serializePacket(uint8_t* data, uint32_t length); + ssize_t deserializePacket(const uint8_t* data, uint32_t length); +}; + +class UniversalTimeServicePacket { + public: + uint16_t packetType; + union { + WhoIsMasterRequestPacket who_is_master_request; + WhoIsMasterResponsePacket who_is_master_response; + SyncRequestPacket sync_request; + SyncResponsePacket sync_response; + MasterAnnouncementPacket master_announcement; + } p; + + ssize_t deserializePacket(const uint8_t* data, + uint32_t length, + uint64_t expectedSyncGroupID); +}; + +}; // namespace android + +#endif // ANDROID_COMMON_TIME_SERVER_PACKETS_H + + diff --git a/services/common_time/diag_thread.cpp b/services/common_time/diag_thread.cpp new file mode 100644 index 000000000000..4cb955131099 --- /dev/null +++ b/services/common_time/diag_thread.cpp @@ -0,0 +1,323 @@ +/* + * 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 "common_time" +#include <utils/Log.h> + +#include <fcntl.h> +#include <linux/in.h> +#include <linux/tcp.h> +#include <poll.h> +#include <sys/socket.h> +#include <sys/types.h> +#include <unistd.h> +#include <utils/Errors.h> +#include <utils/misc.h> + +#include <common_time/local_clock.h> + +#include "common_clock.h" +#include "diag_thread.h" + +#define kMaxEvents 16 +#define kListenPort 9876 + +static bool setNonblocking(int fd) { + int flags = fcntl(fd, F_GETFL); + if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0) { + ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)", + fd, errno); + return false; + } + + return true; +} + +static bool setNodelay(int fd) { + int tmp = 1; + if (setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &tmp, sizeof(tmp)) < 0) { + ALOGE("Failed to set socket (%d) to no-delay mode (errno %d)", + fd, errno); + return false; + } + + return true; +} + +namespace android { + +DiagThread::DiagThread(CommonClock* common_clock, LocalClock* local_clock) { + common_clock_ = common_clock; + local_clock_ = local_clock; + listen_fd_ = -1; + data_fd_ = -1; + kernel_logID_basis_known_ = false; + discipline_log_ID_ = 0; +} + +DiagThread::~DiagThread() { +} + +status_t DiagThread::startWorkThread() { + status_t res; + stopWorkThread(); + res = run("Diag"); + + if (res != OK) + ALOGE("Failed to start work thread (res = %d)", res); + + return res; +} + +void DiagThread::stopWorkThread() { + status_t res; + res = requestExitAndWait(); // block until thread exit. + if (res != OK) + ALOGE("Failed to stop work thread (res = %d)", res); +} + +bool DiagThread::openListenSocket() { + bool ret = false; + int flags; + cleanupListenSocket(); + + if ((listen_fd_ = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) { + ALOGE("Socket failed."); + goto bailout; + } + + // Set non-blocking operation + if (!setNonblocking(listen_fd_)) + goto bailout; + + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + addr.sin_port = htons(kListenPort); + + if (bind(listen_fd_, (struct sockaddr*)&addr, sizeof(addr)) < 0) { + ALOGE("Bind failed."); + goto bailout; + } + + if (listen(listen_fd_, 1) < 0) { + ALOGE("Listen failed."); + goto bailout; + } + + ret = true; +bailout: + if (!ret) + cleanupListenSocket(); + + return ret; +} + +void DiagThread::cleanupListenSocket() { + if (listen_fd_ >= 0) { + int res; + + struct linger l; + l.l_onoff = 1; + l.l_linger = 0; + + setsockopt(listen_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); + shutdown(listen_fd_, SHUT_RDWR); + close(listen_fd_); + listen_fd_ = -1; + } +} + +void DiagThread::cleanupDataSocket() { + if (data_fd_ >= 0) { + int res; + + struct linger l; + l.l_onoff = 1; + l.l_linger = 0; + + setsockopt(data_fd_, SOL_SOCKET, SO_LINGER, &l, sizeof(l)); + shutdown(data_fd_, SHUT_RDWR); + close(data_fd_); + data_fd_ = -1; + } +} + +void DiagThread::resetLogIDs() { + // Drain and discard all of the events from the kernel + struct local_time_debug_event events[kMaxEvents]; + while(local_clock_->getDebugLog(events, kMaxEvents) > 0) + ; + + { + Mutex::Autolock lock(&discipline_log_lock_); + discipline_log_.clear(); + discipline_log_ID_ = 0; + } + + kernel_logID_basis_known_ = false; +} + +void DiagThread::pushDisciplineEvent(int64_t observed_local_time, + int64_t observed_common_time, + int64_t nominal_common_time, + int32_t total_correction, + int32_t rtt) { + Mutex::Autolock lock(&discipline_log_lock_); + + DisciplineEventRecord evt; + + evt.event_id = discipline_log_ID_++; + + evt.action_local_time = local_clock_->getLocalTime(); + common_clock_->localToCommon(evt.action_local_time, + &evt.action_common_time); + + evt.observed_local_time = observed_local_time; + evt.observed_common_time = observed_common_time; + evt.nominal_common_time = nominal_common_time; + evt.total_correction = total_correction; + evt.rtt = rtt; + + discipline_log_.push_back(evt); + while (discipline_log_.size() > kMaxDisciplineLogSize) + discipline_log_.erase(discipline_log_.begin()); +} + +bool DiagThread::threadLoop() { + struct pollfd poll_fds[1]; + + if (!openListenSocket()) { + ALOGE("Failed to open listen socket"); + goto bailout; + } + + while (!exitPending()) { + memset(&poll_fds, 0, sizeof(poll_fds)); + + if (data_fd_ < 0) { + poll_fds[0].fd = listen_fd_; + poll_fds[0].events = POLLIN; + } else { + poll_fds[0].fd = data_fd_; + poll_fds[0].events = POLLRDHUP | POLLIN; + } + + int poll_res = poll(poll_fds, NELEM(poll_fds), 50); + if (poll_res < 0) { + ALOGE("Fatal error (%d,%d) while waiting on events", + poll_res, errno); + goto bailout; + } + + if (exitPending()) + break; + + if (poll_fds[0].revents) { + if (poll_fds[0].fd == listen_fd_) { + data_fd_ = accept(listen_fd_, NULL, NULL); + + if (data_fd_ < 0) { + ALOGW("Failed accept on socket %d with err %d", + listen_fd_, errno); + } else { + if (!setNonblocking(data_fd_)) + cleanupDataSocket(); + if (!setNodelay(data_fd_)) + cleanupDataSocket(); + } + } else + if (poll_fds[0].fd == data_fd_) { + if (poll_fds[0].revents & POLLRDHUP) { + // Connection hung up; time to clean up. + cleanupDataSocket(); + } else + if (poll_fds[0].revents & POLLIN) { + uint8_t cmd; + if (read(data_fd_, &cmd, sizeof(cmd)) > 0) { + switch(cmd) { + case 'r': + case 'R': + resetLogIDs(); + break; + } + } + } + } + } + + struct local_time_debug_event events[kMaxEvents]; + int amt = local_clock_->getDebugLog(events, kMaxEvents); + + if (amt > 0) { + for (int i = 0; i < amt; i++) { + struct local_time_debug_event& e = events[i]; + + if (!kernel_logID_basis_known_) { + kernel_logID_basis_ = e.local_timesync_event_id; + kernel_logID_basis_known_ = true; + } + + char buf[1024]; + int64_t common_time; + status_t res = common_clock_->localToCommon(e.local_time, + &common_time); + snprintf(buf, sizeof(buf), "E,%lld,%lld,%lld,%d\n", + e.local_timesync_event_id - kernel_logID_basis_, + e.local_time, + common_time, + (OK == res) ? 1 : 0); + buf[sizeof(buf) - 1] = 0; + + if (data_fd_ >= 0) + write(data_fd_, buf, strlen(buf)); + } + } + + { // scope for autolock pattern + Mutex::Autolock lock(&discipline_log_lock_); + + while (discipline_log_.size() > 0) { + char buf[1024]; + DisciplineEventRecord& e = *discipline_log_.begin(); + snprintf(buf, sizeof(buf), + "D,%lld,%lld,%lld,%lld,%lld,%lld,%d,%d\n", + e.event_id, + e.action_local_time, + e.action_common_time, + e.observed_local_time, + e.observed_common_time, + e.nominal_common_time, + e.total_correction, + e.rtt); + buf[sizeof(buf) - 1] = 0; + + if (data_fd_ >= 0) + write(data_fd_, buf, strlen(buf)); + + discipline_log_.erase(discipline_log_.begin()); + } + } + } + +bailout: + cleanupDataSocket(); + cleanupListenSocket(); + return false; +} + +} // namespace android diff --git a/services/common_time/diag_thread.h b/services/common_time/diag_thread.h new file mode 100644 index 000000000000..c630e0d9b525 --- /dev/null +++ b/services/common_time/diag_thread.h @@ -0,0 +1,76 @@ +/* + * 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 __DIAG_THREAD_H__ +#define __DIAG_THREAD_H__ + +#include <utils/List.h> +#include <utils/threads.h> + +namespace android { + +class CommonClock; +class LocalClock; + +class DiagThread : public Thread { + public: + DiagThread(CommonClock* common_clock, LocalClock* local_clock); + ~DiagThread(); + + status_t startWorkThread(); + void stopWorkThread(); + virtual bool threadLoop(); + + void pushDisciplineEvent(int64_t observed_local_time, + int64_t observed_common_time, + int64_t nominal_common_time, + int32_t total_correction, + int32_t rtt); + + private: + typedef struct { + int64_t event_id; + int64_t action_local_time; + int64_t action_common_time; + int64_t observed_local_time; + int64_t observed_common_time; + int64_t nominal_common_time; + int32_t total_correction; + int32_t rtt; + } DisciplineEventRecord; + + bool openListenSocket(); + void cleanupListenSocket(); + void cleanupDataSocket(); + void resetLogIDs(); + + CommonClock* common_clock_; + LocalClock* local_clock_; + int listen_fd_; + int data_fd_; + + int64_t kernel_logID_basis_; + bool kernel_logID_basis_known_; + + static const size_t kMaxDisciplineLogSize = 16; + Mutex discipline_log_lock_; + List<DisciplineEventRecord> discipline_log_; + int64_t discipline_log_ID_; +}; + +} // namespace android + +#endif //__ DIAG_THREAD_H__ diff --git a/services/common_time/main.cpp b/services/common_time/main.cpp new file mode 100644 index 000000000000..49eb30abab66 --- /dev/null +++ b/services/common_time/main.cpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2012 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. + */ + +/* + * A service that exchanges time synchronization information between + * a master that defines a timeline and clients that follow the timeline. + */ + +#define LOG_TAG "common_time" +#include <utils/Log.h> + +#include <binder/IPCThreadState.h> +#include <binder/ProcessState.h> + +#include "common_time_server.h" + +int main(int argc, char *argv[]) { + using namespace android; + + sp<CommonTimeServer> service = new CommonTimeServer(); + if (service == NULL) + return 1; + + ProcessState::self()->startThreadPool(); + service->run("CommonTimeServer", ANDROID_PRIORITY_NORMAL); + + IPCThreadState::self()->joinThreadPool(); + return 0; +} + diff --git a/services/input/Android.mk b/services/input/Android.mk index 86c6c8ae55f1..159800f4623d 100644 --- a/services/input/Android.mk +++ b/services/input/Android.mk @@ -29,6 +29,7 @@ LOCAL_SRC_FILES:= \ LOCAL_SHARED_LIBRARIES := \ libcutils \ + libandroidfw \ libutils \ libhardware \ libhardware_legacy \ diff --git a/services/input/EventHub.cpp b/services/input/EventHub.cpp index 296c95ec937e..1b74aa6340f4 100644 --- a/services/input/EventHub.cpp +++ b/services/input/EventHub.cpp @@ -36,9 +36,9 @@ #include <errno.h> #include <assert.h> -#include <ui/KeyLayoutMap.h> -#include <ui/KeyCharacterMap.h> -#include <ui/VirtualKeyMap.h> +#include <androidfw/KeyLayoutMap.h> +#include <androidfw/KeyCharacterMap.h> +#include <androidfw/VirtualKeyMap.h> #include <string.h> #include <stdint.h> diff --git a/services/input/EventHub.h b/services/input/EventHub.h index 8a2afd32fcf0..4eb47c65dbe1 100644 --- a/services/input/EventHub.h +++ b/services/input/EventHub.h @@ -18,11 +18,11 @@ #ifndef _RUNTIME_EVENT_HUB_H #define _RUNTIME_EVENT_HUB_H -#include <ui/Input.h> -#include <ui/Keyboard.h> -#include <ui/KeyLayoutMap.h> -#include <ui/KeyCharacterMap.h> -#include <ui/VirtualKeyMap.h> +#include <androidfw/Input.h> +#include <androidfw/Keyboard.h> +#include <androidfw/KeyLayoutMap.h> +#include <androidfw/KeyCharacterMap.h> +#include <androidfw/VirtualKeyMap.h> #include <utils/String8.h> #include <utils/threads.h> #include <utils/Log.h> diff --git a/services/input/InputApplication.h b/services/input/InputApplication.h index 67ae94b70573..c04a93590326 100644 --- a/services/input/InputApplication.h +++ b/services/input/InputApplication.h @@ -17,7 +17,7 @@ #ifndef _UI_INPUT_APPLICATION_H #define _UI_INPUT_APPLICATION_H -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/RefBase.h> #include <utils/Timers.h> diff --git a/services/input/InputDispatcher.cpp b/services/input/InputDispatcher.cpp index e6e28df0dfb3..149c0d37d76b 100644 --- a/services/input/InputDispatcher.cpp +++ b/services/input/InputDispatcher.cpp @@ -45,7 +45,7 @@ #include "InputDispatcher.h" #include <cutils/log.h> -#include <ui/PowerManager.h> +#include <androidfw/PowerManager.h> #include <stddef.h> #include <unistd.h> diff --git a/services/input/InputDispatcher.h b/services/input/InputDispatcher.h index 5c517f5163c2..4b36480a27dd 100644 --- a/services/input/InputDispatcher.h +++ b/services/input/InputDispatcher.h @@ -17,8 +17,8 @@ #ifndef _UI_INPUT_DISPATCHER_H #define _UI_INPUT_DISPATCHER_H -#include <ui/Input.h> -#include <ui/InputTransport.h> +#include <androidfw/Input.h> +#include <androidfw/InputTransport.h> #include <utils/KeyedVector.h> #include <utils/Vector.h> #include <utils/threads.h> diff --git a/services/input/InputListener.h b/services/input/InputListener.h index f920cd1f6b78..b1dc0b888445 100644 --- a/services/input/InputListener.h +++ b/services/input/InputListener.h @@ -17,7 +17,7 @@ #ifndef _UI_INPUT_LISTENER_H #define _UI_INPUT_LISTENER_H -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/RefBase.h> #include <utils/Vector.h> diff --git a/services/input/InputManager.h b/services/input/InputManager.h index df4d299ed3b3..29584c91da66 100644 --- a/services/input/InputManager.h +++ b/services/input/InputManager.h @@ -25,8 +25,8 @@ #include "InputReader.h" #include "InputDispatcher.h" -#include <ui/Input.h> -#include <ui/InputTransport.h> +#include <androidfw/Input.h> +#include <androidfw/InputTransport.h> #include <utils/Errors.h> #include <utils/Vector.h> #include <utils/Timers.h> diff --git a/services/input/InputReader.cpp b/services/input/InputReader.cpp index 4be06e43d112..eccce292056d 100644 --- a/services/input/InputReader.cpp +++ b/services/input/InputReader.cpp @@ -39,8 +39,8 @@ #include "InputReader.h" #include <cutils/log.h> -#include <ui/Keyboard.h> -#include <ui/VirtualKeyMap.h> +#include <androidfw/Keyboard.h> +#include <androidfw/VirtualKeyMap.h> #include <stddef.h> #include <stdlib.h> diff --git a/services/input/InputReader.h b/services/input/InputReader.h index ad89a222293e..9bbe49ceca81 100644 --- a/services/input/InputReader.h +++ b/services/input/InputReader.h @@ -21,7 +21,7 @@ #include "PointerController.h" #include "InputListener.h" -#include <ui/Input.h> +#include <androidfw/Input.h> #include <ui/DisplayInfo.h> #include <utils/KeyedVector.h> #include <utils/threads.h> diff --git a/services/input/InputWindow.h b/services/input/InputWindow.h index 38968f91cf58..824a64b05b85 100644 --- a/services/input/InputWindow.h +++ b/services/input/InputWindow.h @@ -17,8 +17,8 @@ #ifndef _UI_INPUT_WINDOW_H #define _UI_INPUT_WINDOW_H -#include <ui/Input.h> -#include <ui/InputTransport.h> +#include <androidfw/Input.h> +#include <androidfw/InputTransport.h> #include <utils/RefBase.h> #include <utils/Timers.h> #include <utils/String8.h> diff --git a/services/input/PointerController.h b/services/input/PointerController.h index 700ef7295fe7..39dbf6b9e9bf 100644 --- a/services/input/PointerController.h +++ b/services/input/PointerController.h @@ -20,7 +20,7 @@ #include "SpriteController.h" #include <ui/DisplayInfo.h> -#include <ui/Input.h> +#include <androidfw/Input.h> #include <utils/RefBase.h> #include <utils/Looper.h> #include <utils/String8.h> diff --git a/services/input/tests/Android.mk b/services/input/tests/Android.mk index 171db3c68b14..8f8c34b4f38c 100644 --- a/services/input/tests/Android.mk +++ b/services/input/tests/Android.mk @@ -9,6 +9,7 @@ test_src_files := \ shared_libraries := \ libcutils \ + libandroidfw \ libutils \ libhardware \ libhardware_legacy \ diff --git a/services/java/com/android/server/AppWidgetServiceImpl.java b/services/java/com/android/server/AppWidgetServiceImpl.java index 41ede2e4b608..9c408c40826b 100644 --- a/services/java/com/android/server/AppWidgetServiceImpl.java +++ b/services/java/com/android/server/AppWidgetServiceImpl.java @@ -463,7 +463,7 @@ class AppWidgetServiceImpl { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DELETED); intent.setComponent(p.info.provider); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, id.appWidgetId); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent, mUserId); if (p.instances.size() == 0) { // cancel the future updates cancelBroadcasts(p); @@ -471,7 +471,7 @@ class AppWidgetServiceImpl { // send the broacast saying that the provider is not in use any more intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_DISABLED); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent, mUserId); } } } @@ -515,8 +515,6 @@ class AppWidgetServiceImpl { + " safe mode: " + provider); } - Binder.restoreCallingIdentity(ident); - id.provider = p; p.instances.add(id); int instancesSize = p.instances.size(); @@ -1066,7 +1064,7 @@ class AppWidgetServiceImpl { void sendEnableIntentLocked(Provider p) { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent, mUserId); } void sendUpdateIntentLocked(Provider p, int[] appWidgetIds) { @@ -1074,7 +1072,7 @@ class AppWidgetServiceImpl { Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); intent.setComponent(p.info.provider); - mContext.sendBroadcast(intent); + mContext.sendBroadcast(intent, mUserId); } } @@ -1477,12 +1475,11 @@ class AppWidgetServiceImpl { } AtomicFile savedStateFile() { - int userId = UserId.getCallingUserId(); - File dir = new File("/data/system/users/" + userId); + File dir = new File("/data/system/users/" + mUserId); File settingsFile = new File(dir, SETTINGS_FILENAME); if (!dir.exists()) { dir.mkdirs(); - if (userId == 0) { + if (mUserId == 0) { // Migrate old data File oldFile = new File("/data/system/" + SETTINGS_FILENAME); // Method doesn't throw an exception on failure. Ignore any errors diff --git a/services/java/com/android/server/CommonTimeManagementService.java b/services/java/com/android/server/CommonTimeManagementService.java new file mode 100644 index 000000000000..9a25d2ea5f39 --- /dev/null +++ b/services/java/com/android/server/CommonTimeManagementService.java @@ -0,0 +1,377 @@ +/* + * Copyright (C) 2012 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.server; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.net.InetAddress; + +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.pm.PackageManager; +import android.net.ConnectivityManager; +import android.net.IConnectivityManager; +import android.net.INetworkManagementEventObserver; +import android.net.InterfaceConfiguration; +import android.net.NetworkInfo; +import android.os.Binder; +import android.os.CommonTimeConfig; +import android.os.Handler; +import android.os.IBinder; +import android.os.INetworkManagementService; +import android.os.RemoteException; +import android.os.ServiceManager; +import android.os.SystemProperties; +import android.util.Log; + +/** + * @hide + * <p>CommonTimeManagementService manages the configuration of the native Common Time service, + * reconfiguring the native service as appropriate in response to changes in network configuration. + */ +class CommonTimeManagementService extends Binder { + /* + * Constants and globals. + */ + private static final String TAG = CommonTimeManagementService.class.getSimpleName(); + private static final int NATIVE_SERVICE_RECONNECT_TIMEOUT = 5000; + private static final String AUTO_DISABLE_PROP = "ro.common_time.auto_disable"; + private static final String ALLOW_WIFI_PROP = "ro.common_time.allow_wifi"; + private static final String SERVER_PRIO_PROP = "ro.common_time.server_prio"; + private static final String NO_INTERFACE_TIMEOUT_PROP = "ro.common_time.no_iface_timeout"; + private static final boolean AUTO_DISABLE; + private static final boolean ALLOW_WIFI; + private static final byte BASE_SERVER_PRIO; + private static final int NO_INTERFACE_TIMEOUT; + private static final InterfaceScoreRule[] IFACE_SCORE_RULES; + + static { + int tmp; + AUTO_DISABLE = (0 != SystemProperties.getInt(AUTO_DISABLE_PROP, 1)); + ALLOW_WIFI = (0 != SystemProperties.getInt(ALLOW_WIFI_PROP, 0)); + tmp = SystemProperties.getInt(SERVER_PRIO_PROP, 1); + NO_INTERFACE_TIMEOUT = SystemProperties.getInt(NO_INTERFACE_TIMEOUT_PROP, 60000); + + if (tmp < 1) + BASE_SERVER_PRIO = 1; + else + if (tmp > 30) + BASE_SERVER_PRIO = 30; + else + BASE_SERVER_PRIO = (byte)tmp; + + if (ALLOW_WIFI) { + IFACE_SCORE_RULES = new InterfaceScoreRule[] { + new InterfaceScoreRule("wlan", (byte)1), + new InterfaceScoreRule("eth", (byte)2), + }; + } else { + IFACE_SCORE_RULES = new InterfaceScoreRule[] { + new InterfaceScoreRule("eth", (byte)2), + }; + } + }; + + /* + * Internal state + */ + private final Context mContext; + private INetworkManagementService mNetMgr; + private CommonTimeConfig mCTConfig; + private String mCurIface; + private Handler mReconnectHandler = new Handler(); + private Handler mNoInterfaceHandler = new Handler(); + private Object mLock = new Object(); + private boolean mDetectedAtStartup = false; + private byte mEffectivePrio = BASE_SERVER_PRIO; + + /* + * Callback handler implementations. + */ + private INetworkManagementEventObserver mIfaceObserver = + new INetworkManagementEventObserver.Stub() { + + public void interfaceStatusChanged(String iface, boolean up) { + reevaluateServiceState(); + } + public void interfaceLinkStateChanged(String iface, boolean up) { + reevaluateServiceState(); + } + public void interfaceAdded(String iface) { + reevaluateServiceState(); + } + public void interfaceRemoved(String iface) { + reevaluateServiceState(); + } + public void limitReached(String limitName, String iface) { } + }; + + private BroadcastReceiver mConnectivityMangerObserver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + reevaluateServiceState(); + } + }; + + private CommonTimeConfig.OnServerDiedListener mCTServerDiedListener = + new CommonTimeConfig.OnServerDiedListener() { + public void onServerDied() { + scheduleTimeConfigReconnect(); + } + }; + + private Runnable mReconnectRunnable = new Runnable() { + public void run() { connectToTimeConfig(); } + }; + + private Runnable mNoInterfaceRunnable = new Runnable() { + public void run() { handleNoInterfaceTimeout(); } + }; + + /* + * Public interface (constructor, systemReady and dump) + */ + public CommonTimeManagementService(Context context) { + mContext = context; + } + + void systemReady() { + if (ServiceManager.checkService(CommonTimeConfig.SERVICE_NAME) == null) { + Log.i(TAG, "No common time service detected on this platform. " + + "Common time services will be unavailable."); + return; + } + + mDetectedAtStartup = true; + + IBinder b = ServiceManager.getService(Context.NETWORKMANAGEMENT_SERVICE); + mNetMgr = INetworkManagementService.Stub.asInterface(b); + + // Network manager is running along-side us, so we should never receiver a remote exception + // while trying to register this observer. + try { + mNetMgr.registerObserver(mIfaceObserver); + } + catch (RemoteException e) { } + + // Register with the connectivity manager for connectivity changed intents. + IntentFilter filter = new IntentFilter(); + filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION); + mContext.registerReceiver(mConnectivityMangerObserver, filter); + + // Connect to the common time config service and apply the initial configuration. + connectToTimeConfig(); + } + + @Override + protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { + if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) + != PackageManager.PERMISSION_GRANTED) { + pw.println(String.format( + "Permission Denial: can't dump CommonTimeManagement service from from " + + "pid=%d, uid=%d", Binder.getCallingPid(), Binder.getCallingUid())); + return; + } + + if (!mDetectedAtStartup) { + pw.println("Native Common Time service was not detected at startup. " + + "Service is unavailable"); + return; + } + + synchronized (mLock) { + pw.println("Current Common Time Management Service Config:"); + pw.println(String.format(" Native service : %s", + (null == mCTConfig) ? "reconnecting" + : "alive")); + pw.println(String.format(" Bound interface : %s", + (null == mCurIface ? "unbound" : mCurIface))); + pw.println(String.format(" Allow WiFi : %s", ALLOW_WIFI ? "yes" : "no")); + pw.println(String.format(" Allow Auto Disable : %s", AUTO_DISABLE ? "yes" : "no")); + pw.println(String.format(" Server Priority : %d", mEffectivePrio)); + pw.println(String.format(" No iface timeout : %d", NO_INTERFACE_TIMEOUT)); + } + } + + /* + * Inner helper classes + */ + private static class InterfaceScoreRule { + public final String mPrefix; + public final byte mScore; + public InterfaceScoreRule(String prefix, byte score) { + mPrefix = prefix; + mScore = score; + } + }; + + /* + * Internal implementation + */ + private void cleanupTimeConfig() { + mReconnectHandler.removeCallbacks(mReconnectRunnable); + mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable); + if (null != mCTConfig) { + mCTConfig.release(); + mCTConfig = null; + } + } + + private void connectToTimeConfig() { + // Get access to the common time service configuration interface. If we catch a remote + // exception in the process (service crashed or no running for w/e reason), schedule an + // attempt to reconnect in the future. + cleanupTimeConfig(); + try { + synchronized (mLock) { + mCTConfig = new CommonTimeConfig(); + mCTConfig.setServerDiedListener(mCTServerDiedListener); + mCurIface = mCTConfig.getInterfaceBinding(); + mCTConfig.setAutoDisable(AUTO_DISABLE); + mCTConfig.setMasterElectionPriority(mEffectivePrio); + } + + if (NO_INTERFACE_TIMEOUT >= 0) + mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT); + + reevaluateServiceState(); + } + catch (RemoteException e) { + scheduleTimeConfigReconnect(); + } + } + + private void scheduleTimeConfigReconnect() { + cleanupTimeConfig(); + Log.w(TAG, String.format("Native service died, will reconnect in %d mSec", + NATIVE_SERVICE_RECONNECT_TIMEOUT)); + mReconnectHandler.postDelayed(mReconnectRunnable, + NATIVE_SERVICE_RECONNECT_TIMEOUT); + } + + private void handleNoInterfaceTimeout() { + if (null != mCTConfig) { + Log.i(TAG, "Timeout waiting for interface to come up. " + + "Forcing networkless master mode."); + if (CommonTimeConfig.ERROR_DEAD_OBJECT == mCTConfig.forceNetworklessMasterMode()) + scheduleTimeConfigReconnect(); + } + } + + private void reevaluateServiceState() { + String bindIface = null; + byte bestScore = -1; + try { + // Check to see if this interface is suitable to use for time synchronization. + // + // TODO : This selection algorithm needs to be enhanced for use with mobile devices. In + // particular, the choice of whether to a wireless interface or not should not be an all + // or nothing thing controlled by properties. It would probably be better if the + // platform had some concept of public wireless networks vs. home or friendly wireless + // networks (something a user would configure in settings or when a new interface is + // added). Then this algorithm could pick only wireless interfaces which were flagged + // as friendly, and be dormant when on public wireless networks. + // + // Another issue which needs to be dealt with is the use of driver supplied interface + // name to determine the network type. The fact that the wireless interface on a device + // is named "wlan0" is just a matter of convention; its not a 100% rule. For example, + // there are devices out there where the wireless is name "tiwlan0", not "wlan0". The + // internal network management interfaces in Android have all of the information needed + // to make a proper classification, there is just no way (currently) to fetch an + // interface's type (available from the ConnectionManager) as well as its address + // (available from either the java.net interfaces or from the NetworkManagment service). + // Both can enumerate interfaces, but that is no way to correlate their results (no + // common shared key; although using the interface name in the connection manager would + // be a good start). Until this gets resolved, we resort to substring searching for + // tags like wlan and eth. + // + String ifaceList[] = mNetMgr.listInterfaces(); + if (null != ifaceList) { + for (String iface : ifaceList) { + + byte thisScore = -1; + for (InterfaceScoreRule r : IFACE_SCORE_RULES) { + if (iface.contains(r.mPrefix)) { + thisScore = r.mScore; + break; + } + } + + if (thisScore <= bestScore) + continue; + + InterfaceConfiguration config = mNetMgr.getInterfaceConfig(iface); + if (null == config) + continue; + + if (config.isActive()) { + bindIface = iface; + bestScore = thisScore; + } + } + } + } + catch (RemoteException e) { + // Bad news; we should not be getting remote exceptions from the connectivity manager + // since it is running in SystemServer along side of us. It probably does not matter + // what we do here, but go ahead and unbind the common time service in this case, just + // so we have some defined behavior. + bindIface = null; + } + + boolean doRebind = true; + synchronized (mLock) { + if ((null != bindIface) && (null == mCurIface)) { + Log.e(TAG, String.format("Binding common time service to %s.", bindIface)); + mCurIface = bindIface; + } else + if ((null == bindIface) && (null != mCurIface)) { + Log.e(TAG, "Unbinding common time service."); + mCurIface = null; + } else + if ((null != bindIface) && (null != mCurIface) && !bindIface.equals(mCurIface)) { + Log.e(TAG, String.format("Switching common time service binding from %s to %s.", + mCurIface, bindIface)); + mCurIface = bindIface; + } else { + doRebind = false; + } + } + + if (doRebind && (null != mCTConfig)) { + byte newPrio = (bestScore > 0) + ? (byte)(bestScore * BASE_SERVER_PRIO) + : BASE_SERVER_PRIO; + if (newPrio != mEffectivePrio) { + mEffectivePrio = newPrio; + mCTConfig.setMasterElectionPriority(mEffectivePrio); + } + + int res = mCTConfig.setNetworkBinding(mCurIface); + if (res != CommonTimeConfig.SUCCESS) + scheduleTimeConfigReconnect(); + + else if (NO_INTERFACE_TIMEOUT >= 0) { + mNoInterfaceHandler.removeCallbacks(mNoInterfaceRunnable); + if (null == mCurIface) + mNoInterfaceHandler.postDelayed(mNoInterfaceRunnable, NO_INTERFACE_TIMEOUT); + } + } + } +} diff --git a/services/java/com/android/server/PowerManagerService.java b/services/java/com/android/server/PowerManagerService.java index e953355723b4..7b4372fec8da 100644 --- a/services/java/com/android/server/PowerManagerService.java +++ b/services/java/com/android/server/PowerManagerService.java @@ -583,6 +583,7 @@ public class PowerManagerService extends IPowerManager.Stub } nativeInit(); + Power.powerInitNative(); synchronized (mLocks) { updateNativePowerStateLocked(); // We make sure to start out with the screen on due to user activity. diff --git a/services/java/com/android/server/SystemServer.java b/services/java/com/android/server/SystemServer.java index 0dbc7c367b46..c9b59975c32f 100644 --- a/services/java/com/android/server/SystemServer.java +++ b/services/java/com/android/server/SystemServer.java @@ -132,6 +132,7 @@ class ServerThread extends Thread { RecognitionManagerService recognition = null; ThrottleService throttle = null; NetworkTimeUpdateService networkTimeUpdater = null; + CommonTimeManagementService commonTimeMgmtService = null; // Critical services... try { @@ -575,6 +576,14 @@ class ServerThread extends Thread { } catch (Throwable e) { reportWtf("starting NetworkTimeUpdate service", e); } + + try { + Slog.i(TAG, "CommonTimeManagementService"); + commonTimeMgmtService = new CommonTimeManagementService(context); + ServiceManager.addService("commontime_management", commonTimeMgmtService); + } catch (Throwable e) { + reportWtf("starting CommonTimeManagementService service", e); + } } // Before things start rolling, be sure we have decided whether @@ -653,6 +662,7 @@ class ServerThread extends Thread { final LocationManagerService locationF = location; final CountryDetectorService countryDetectorF = countryDetector; final NetworkTimeUpdateService networkTimeUpdaterF = networkTimeUpdater; + final CommonTimeManagementService commonTimeMgmtServiceF = commonTimeMgmtService; final TextServicesManagerService textServiceManagerServiceF = tsms; final StatusBarManagerService statusBarF = statusBar; @@ -752,6 +762,11 @@ class ServerThread extends Thread { reportWtf("making Network Time Service ready", e); } try { + if (commonTimeMgmtServiceF != null) commonTimeMgmtServiceF.systemReady(); + } catch (Throwable e) { + reportWtf("making Common time management service ready", e); + } + try { if (textServiceManagerServiceF != null) textServiceManagerServiceF.systemReady(); } catch (Throwable e) { reportWtf("making Text Services Manager Service ready", e); diff --git a/services/java/com/android/server/accessibility/AccessibilityManagerService.java b/services/java/com/android/server/accessibility/AccessibilityManagerService.java index 586a67e26d84..455325af5156 100644 --- a/services/java/com/android/server/accessibility/AccessibilityManagerService.java +++ b/services/java/com/android/server/accessibility/AccessibilityManagerService.java @@ -86,8 +86,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private static final String LOG_TAG = "AccessibilityManagerService"; - private static final String FUNCTION_REGISTER_EVENT_LISTENER = - "registerEventListener"; + private static final String FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE = + "registerUiTestAutomationService"; private static int sIdCounter = 0; @@ -95,10 +95,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub private static final int DO_SET_SERVICE_INFO = 10; - public static final int ACTIVE_WINDOW_ID = -1; - - public static final long ROOT_NODE_ID = -1; - private static int sNextWindowId; final HandlerCaller mCaller; @@ -241,19 +237,9 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub if (intent.getAction() == Intent.ACTION_BOOT_COMPLETED) { synchronized (mLock) { populateAccessibilityServiceListLocked(); - // get accessibility enabled setting on boot - mIsAccessibilityEnabled = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.ACCESSIBILITY_ENABLED, 0) == 1; - - manageServicesLocked(); - - // get touch exploration enabled setting on boot - mIsTouchExplorationEnabled = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; + handleAccessibilityEnabledSettingChangedLocked(); + handleTouchExplorationEnabledSettingChangedLocked(); updateInputFilterLocked(); - sendStateToClientsLocked(); } @@ -301,9 +287,10 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - synchronized (mLock) { handleAccessibilityEnabledSettingChangedLocked(); + updateInputFilterLocked(); + sendStateToClientsLocked(); } } }); @@ -315,11 +302,8 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - synchronized (mLock) { - mIsTouchExplorationEnabled = Settings.Secure.getInt( - mContext.getContentResolver(), - Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; + handleTouchExplorationEnabledSettingChangedLocked(); updateInputFilterLocked(); sendStateToClientsLocked(); } @@ -333,7 +317,6 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub @Override public void onChange(boolean selfChange) { super.onChange(selfChange); - synchronized (mLock) { manageServicesLocked(); } @@ -475,7 +458,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public void registerUiTestAutomationService(IEventListener listener, AccessibilityServiceInfo accessibilityServiceInfo) { mSecurityPolicy.enforceCallingPermission(Manifest.permission.RETRIEVE_WINDOW_CONTENT, - FUNCTION_REGISTER_EVENT_LISTENER); + FUNCTION_REGISTER_UI_TEST_AUTOMATION_SERVICE); ComponentName componentName = new ComponentName("foo.bar", "AutomationAccessibilityService"); synchronized (mLock) { @@ -905,8 +888,15 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } else { unbindAllServicesLocked(); } - updateInputFilterLocked(); - sendStateToClientsLocked(); + } + + /** + * Updates the state based on the touch exploration enabled setting. + */ + private void handleTouchExplorationEnabledSettingChangedLocked() { + mIsTouchExplorationEnabled = Settings.Secure.getInt( + mContext.getContentResolver(), + Settings.Secure.TOUCH_EXPLORATION_ENABLED, 0) == 1; } private class AccessibilityConnectionWrapper implements DeathRecipient { @@ -1229,13 +1219,16 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub public void binderDied() { synchronized (mLock) { - unlinkToOwnDeath(); + // The death recipient is unregistered in tryRemoveServiceLocked tryRemoveServiceLocked(this); // We no longer have an automation service, so restore // the state based on values in the settings database. if (mIsAutomation) { mUiAutomationService = null; handleAccessibilityEnabledSettingChangedLocked(); + handleTouchExplorationEnabledSettingChangedLocked(); + updateInputFilterLocked(); + sendStateToClientsLocked(); } } } @@ -1256,7 +1249,7 @@ public class AccessibilityManagerService extends IAccessibilityManager.Stub } private int resolveAccessibilityWindowId(int accessibilityWindowId) { - if (accessibilityWindowId == ACTIVE_WINDOW_ID) { + if (accessibilityWindowId == AccessibilityNodeInfo.ACTIVE_WINDOW_ID) { return mSecurityPolicy.mRetrievalAlowingWindowId; } return accessibilityWindowId; diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index 9d5caaef6d1f..8a5e7fc84a7b 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -138,7 +138,6 @@ import java.io.StringWriter; import java.lang.IllegalStateException; import java.lang.ref.WeakReference; import java.util.ArrayList; -import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.Comparator; @@ -2752,7 +2751,13 @@ public final class ActivityManagerService extends ActivityManagerNative } // Just in case... - mMainStack.appDiedLocked(app); + if (mMainStack.mPausingActivity != null && mMainStack.mPausingActivity.app == app) { + if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " +mMainStack.mPausingActivity); + mMainStack.mPausingActivity = null; + } + if (mMainStack.mLastPausedActivity != null && mMainStack.mLastPausedActivity.app == app) { + mMainStack.mLastPausedActivity = null; + } // Remove this application's activities from active lists. mMainStack.removeHistoryRecordsForAppLocked(app); @@ -5754,7 +5759,7 @@ public final class ActivityManagerService extends ActivityManagerNative ComponentName comp = new ComponentName(cpi.packageName, cpi.name); ContentProviderRecord cpr = mProviderMap.getProviderByClass(comp, userId); if (cpr == null) { - cpr = new ContentProviderRecord(cpi, app.info, comp); + cpr = new ContentProviderRecord(this, cpi, app.info, comp); mProviderMap.putProviderByClass(comp, cpr); } if (DEBUG_MU) @@ -5826,7 +5831,8 @@ public final class ActivityManagerService extends ActivityManagerNative return msg; } - boolean incProviderCount(ProcessRecord r, ContentProviderRecord cpr) { + boolean incProviderCount(ProcessRecord r, final ContentProviderRecord cpr, + IBinder externalProcessToken) { if (r != null) { Integer cnt = r.conProviders.get(cpr); if (DEBUG_PROVIDER) Slog.v(TAG, @@ -5842,12 +5848,13 @@ public final class ActivityManagerService extends ActivityManagerNative r.conProviders.put(cpr, new Integer(cnt.intValue()+1)); } } else { - cpr.externals++; + cpr.addExternalProcessHandleLocked(externalProcessToken); } return false; } - boolean decProviderCount(ProcessRecord r, ContentProviderRecord cpr) { + boolean decProviderCount(ProcessRecord r, final ContentProviderRecord cpr, + IBinder externalProcessToken) { if (r != null) { Integer cnt = r.conProviders.get(cpr); if (DEBUG_PROVIDER) Slog.v(TAG, @@ -5863,13 +5870,13 @@ public final class ActivityManagerService extends ActivityManagerNative r.conProviders.put(cpr, new Integer(cnt.intValue()-1)); } } else { - cpr.externals++; + cpr.removeExternalProcessHandleLocked(externalProcessToken); } return false; } private final ContentProviderHolder getContentProviderImpl(IApplicationThread caller, - String name) { + String name, IBinder token) { ContentProviderRecord cpr; ProviderInfo cpi = null; @@ -5913,7 +5920,7 @@ public final class ActivityManagerService extends ActivityManagerNative // In this case the provider instance already exists, so we can // return it right away. - final boolean countChanged = incProviderCount(r, cpr); + final boolean countChanged = incProviderCount(r, cpr, token); if (countChanged) { if (cpr.proc != null && r.setAdj <= ProcessList.PERCEPTIBLE_APP_ADJ) { // If this is a perceptible app accessing the provider, @@ -5947,7 +5954,7 @@ public final class ActivityManagerService extends ActivityManagerNative Slog.i(TAG, "Existing provider " + cpr.name.flattenToShortString() + " is crashing; detaching " + r); - boolean lastRef = decProviderCount(r, cpr); + boolean lastRef = decProviderCount(r, cpr, token); appDiedLocked(cpr.proc, cpr.proc.pid, cpr.proc.thread); if (!lastRef) { // This wasn't the last ref our process had on @@ -6005,7 +6012,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } ai = getAppInfoForUser(ai, Binder.getOrigCallingUser()); - cpr = new ContentProviderRecord(cpi, ai, comp); + cpr = new ContentProviderRecord(this, cpi, ai, comp); } catch (RemoteException ex) { // pm is in same process, this will never happen. } @@ -6075,8 +6082,9 @@ public final class ActivityManagerService extends ActivityManagerNative if (firstClass) { mProviderMap.putProviderByClass(comp, cpr); } + mProviderMap.putProviderByName(name, cpr); - incProviderCount(r, cpr); + incProviderCount(r, cpr, token); } } @@ -6116,12 +6124,17 @@ public final class ActivityManagerService extends ActivityManagerNative throw new SecurityException(msg); } - ContentProviderHolder contentProvider = getContentProviderImpl(caller, name); - return contentProvider; + return getContentProviderImpl(caller, name, null); } - private ContentProviderHolder getContentProviderExternal(String name) { - return getContentProviderImpl(null, name); + public ContentProviderHolder getContentProviderExternal(String name, IBinder token) { + enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY, + "Do not have permission in call getContentProviderExternal()"); + return getContentProviderExternalUnchecked(name, token); + } + + private ContentProviderHolder getContentProviderExternalUnchecked(String name,IBinder token) { + return getContentProviderImpl(null, name, token); } /** @@ -6157,14 +6170,20 @@ public final class ActivityManagerService extends ActivityManagerNative + cpr.info.name + " in process " + r.processName); return; } else { - if (decProviderCount(r, localCpr)) { + if (decProviderCount(r, localCpr, null)) { updateOomAdjLocked(); } } } } - private void removeContentProviderExternal(String name) { + public void removeContentProviderExternal(String name, IBinder token) { + enforceCallingPermission(android.Manifest.permission.ACCESS_CONTENT_PROVIDERS_EXTERNALLY, + "Do not have permission in call removeContentProviderExternal()"); + removeContentProviderExternalUnchecked(name, token); + } + + private void removeContentProviderExternalUnchecked(String name, IBinder token) { synchronized (this) { ContentProviderRecord cpr = mProviderMap.getProviderByName(name, Binder.getOrigCallingUser()); @@ -6178,11 +6197,18 @@ public final class ActivityManagerService extends ActivityManagerNative ComponentName comp = new ComponentName(cpr.info.packageName, cpr.info.name); ContentProviderRecord localCpr = mProviderMap.getProviderByClass(comp, Binder.getOrigCallingUser()); - localCpr.externals--; - if (localCpr.externals < 0) { - Slog.e(TAG, "Externals < 0 for content provider " + localCpr); + if (localCpr.hasExternalProcessHandles()) { + if (localCpr.removeExternalProcessHandleLocked(token)) { + updateOomAdjLocked(); + } else { + Slog.e(TAG, "Attmpt to remove content provider " + localCpr + + " with no external reference for token: " + + token + "."); + } + } else { + Slog.e(TAG, "Attmpt to remove content provider: " + localCpr + + " with no external references."); } - updateOomAdjLocked(); } } @@ -6286,7 +6312,7 @@ public final class ActivityManagerService extends ActivityManagerNative ContentProviderHolder holder = null; try { - holder = getContentProviderExternal(name); + holder = getContentProviderExternalUnchecked(name, null); if (holder != null) { return holder.provider.getType(uri); } @@ -6295,7 +6321,7 @@ public final class ActivityManagerService extends ActivityManagerNative return null; } finally { if (holder != null) { - removeContentProviderExternal(name); + removeContentProviderExternalUnchecked(name, null); } Binder.restoreCallingIdentity(ident); } @@ -6400,7 +6426,7 @@ public final class ActivityManagerService extends ActivityManagerNative public ParcelFileDescriptor openContentUri(Uri uri) throws RemoteException { enforceNotIsolatedCaller("openContentUri"); String name = uri.getAuthority(); - ContentProviderHolder cph = getContentProviderExternal(name); + ContentProviderHolder cph = getContentProviderExternalUnchecked(name, null); ParcelFileDescriptor pfd = null; if (cph != null) { // We record the binder invoker's uid in thread-local storage before @@ -6422,7 +6448,7 @@ public final class ActivityManagerService extends ActivityManagerNative } // We've got the fd now, so we're done with the provider. - removeContentProviderExternal(name); + removeContentProviderExternalUnchecked(name, null); } else { Slog.d(TAG, "Failed to get provider for authority '" + name + "'"); } @@ -6467,7 +6493,7 @@ public final class ActivityManagerService extends ActivityManagerNative mMainStack.stopIfSleepingLocked(); final long endTime = System.currentTimeMillis() + timeout; while (mMainStack.mResumedActivity != null - || mMainStack.mPausingActivities.size() > 0) { + || mMainStack.mPausingActivity != null) { long delay = endTime - System.currentTimeMillis(); if (delay <= 0) { Slog.w(TAG, "Activity manager shutdown timed out"); @@ -8274,13 +8300,8 @@ public final class ActivityManagerService extends ActivityManagerNative } pw.println(" "); - if (mMainStack.mPausingActivities.size() > 0) { - pw.println(" mPausingActivities: " + Arrays.toString( - mMainStack.mPausingActivities.toArray())); - } - if (mMainStack.mInputPausedActivities.size() > 0) { - pw.println(" mInputPausedActivities: " + Arrays.toString( - mMainStack.mInputPausedActivities.toArray())); + if (mMainStack.mPausingActivity != null) { + pw.println(" mPausingActivity: " + mMainStack.mPausingActivity); } pw.println(" mResumedActivity: " + mMainStack.mResumedActivity); pw.println(" mFocusedActivity: " + mFocusedActivity); @@ -10253,7 +10274,7 @@ public final class ActivityManagerService extends ActivityManagerNative for (int i=0; i<NL; i++) { ContentProviderRecord cpr = (ContentProviderRecord) mLaunchingProviders.get(i); - if (cpr.clients.size() <= 0 && cpr.externals <= 0) { + if (cpr.clients.size() <= 0 && !cpr.hasExternalProcessHandles()) { synchronized (cpr) { cpr.launchingApp = null; cpr.notifyAll(); @@ -13455,7 +13476,7 @@ public final class ActivityManagerService extends ActivityManagerNative // If the provider has external (non-framework) process // dependencies, ensure that its adjustment is at least // FOREGROUND_APP_ADJ. - if (cpr.externals != 0) { + if (cpr.hasExternalProcessHandles()) { if (adj > ProcessList.FOREGROUND_APP_ADJ) { adj = ProcessList.FOREGROUND_APP_ADJ; schedGroup = Process.THREAD_GROUP_DEFAULT; @@ -13855,13 +13876,7 @@ public final class ActivityManagerService extends ActivityManagerNative private final ActivityRecord resumedAppLocked() { ActivityRecord resumedActivity = mMainStack.mResumedActivity; if (resumedActivity == null || resumedActivity.app == null) { - for (int i=mMainStack.mPausingActivities.size()-1; i>=0; i--) { - ActivityRecord r = mMainStack.mPausingActivities.get(i); - if (r.app != null) { - resumedActivity = r; - break; - } - } + resumedActivity = mMainStack.mPausingActivity; if (resumedActivity == null || resumedActivity.app == null) { resumedActivity = mMainStack.topRunningActivityLocked(null); } diff --git a/services/java/com/android/server/am/ActivityRecord.java b/services/java/com/android/server/am/ActivityRecord.java index 977ee8479709..cdab6c6f67b3 100644 --- a/services/java/com/android/server/am/ActivityRecord.java +++ b/services/java/com/android/server/am/ActivityRecord.java @@ -602,7 +602,6 @@ final class ActivityRecord { public void windowsDrawn() { synchronized(service) { - stack.reportActivityDrawnLocked(this); if (launchTime != 0) { final long curTime = SystemClock.uptimeMillis(); final long thisTime = curTime - launchTime; @@ -691,9 +690,7 @@ final class ActivityRecord { // Hmmm, who might we be waiting for? r = stack.mResumedActivity; if (r == null) { - if (stack.mPausingActivities.size() > 0) { - r = stack.mPausingActivities.get(stack.mPausingActivities.size()-1); - } + r = stack.mPausingActivity; } // Both of those null? Fall back to 'this' again if (r == null) { diff --git a/services/java/com/android/server/am/ActivityStack.java b/services/java/com/android/server/am/ActivityStack.java index e8c8275f554a..7b8bc26e6e13 100644 --- a/services/java/com/android/server/am/ActivityStack.java +++ b/services/java/com/android/server/am/ActivityStack.java @@ -227,13 +227,7 @@ final class ActivityStack { * When we are in the process of pausing an activity, before starting the * next one, this variable holds the activity that is currently being paused. */ - final ArrayList<ActivityRecord> mPausingActivities = new ArrayList<ActivityRecord>(); - - /** - * These activities currently have their input paused, as they want for - * the next top activity to have its windows visible. - */ - final ArrayList<ActivityRecord> mInputPausedActivities = new ArrayList<ActivityRecord>(); + ActivityRecord mPausingActivity = null; /** * This is the last activity that we put into the paused state. This is @@ -813,9 +807,9 @@ final class ActivityStack { startPausingLocked(false, true); return; } - if (mPausingActivities.size() > 0) { + if (mPausingActivity != null) { // Still waiting for something to pause; can't sleep yet. - if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivities); + if (DEBUG_PAUSE) Slog.v(TAG, "Sleep still waiting to pause " + mPausingActivity); return; } @@ -878,6 +872,11 @@ final class ActivityStack { } private final void startPausingLocked(boolean userLeaving, boolean uiSleeping) { + if (mPausingActivity != null) { + RuntimeException e = new RuntimeException(); + Slog.e(TAG, "Trying to pause when pause is already pending for " + + mPausingActivity, e); + } ActivityRecord prev = mResumedActivity; if (prev == null) { RuntimeException e = new RuntimeException(); @@ -885,25 +884,19 @@ final class ActivityStack { resumeTopActivityLocked(null); return; } - if (mPausingActivities.contains(prev)) { - RuntimeException e = new RuntimeException(); - Slog.e(TAG, "Trying to pause when pause when already pausing " + prev, e); - } if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSING: " + prev); else if (DEBUG_PAUSE) Slog.v(TAG, "Start pausing: " + prev); mResumedActivity = null; - mPausingActivities.add(prev); + mPausingActivity = prev; mLastPausedActivity = prev; prev.state = ActivityState.PAUSING; prev.task.touchActiveTime(); prev.updateThumbnail(screenshotActivities(prev), null); mService.updateCpuStats(); - ActivityRecord pausing; if (prev.app != null && prev.app.thread != null) { if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending pause: " + prev); - pausing = prev; try { EventLog.writeEvent(EventLogTags.AM_PAUSE_ACTIVITY, System.identityHashCode(prev), @@ -916,14 +909,12 @@ final class ActivityStack { } catch (Exception e) { // Ignore exception, if process died other code will cleanup. Slog.w(TAG, "Exception thrown during pause", e); - mPausingActivities.remove(prev); + mPausingActivity = null; mLastPausedActivity = null; - pausing = null; } } else { - mPausingActivities.remove(prev); + mPausingActivity = null; mLastPausedActivity = null; - pausing = null; } // If we are not going to sleep, we want to ensure the device is @@ -937,28 +928,18 @@ final class ActivityStack { } } - if (pausing != null) { + + if (mPausingActivity != null) { // Have the window manager pause its key dispatching until the new // activity has started. If we're pausing the activity just because // the screen is being turned off and the UI is sleeping, don't interrupt // key dispatch; the same activity will pick it up again on wakeup. if (!uiSleeping) { - pausing.pauseKeyDispatchingLocked(); - mInputPausedActivities.add(prev); + prev.pauseKeyDispatchingLocked(); } else { if (DEBUG_PAUSE) Slog.v(TAG, "Key dispatch not paused for screen off"); } - if (pausing.configDestroy) { - // The previous is being paused because the configuration - // is changing, which means it is actually stopping... - // To juggle the fact that we are also starting a new - // instance right now, we need to first completely stop - // the current instance before starting the new one. - if (DEBUG_PAUSE) Slog.v(TAG, "Destroying at pause: " + prev); - destroyActivityLocked(pausing, true, false, "pause-config"); - } - // Schedule a pause timeout in case the app doesn't respond. // We don't give it much time because this directly impacts the // responsiveness seen by the user. @@ -970,10 +951,7 @@ final class ActivityStack { // This activity failed to schedule the // pause, so just treat it as being paused now. if (DEBUG_PAUSE) Slog.v(TAG, "Activity not running, resuming next."); - } - - if (!mService.isSleeping()) { - resumeTopActivityLocked(pausing); + resumeTopActivityLocked(null); } } @@ -988,17 +966,16 @@ final class ActivityStack { if (index >= 0) { r = mHistory.get(index); mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); - if (mPausingActivities.contains(r)) { + if (mPausingActivity == r) { if (DEBUG_STATES) Slog.v(TAG, "Moving to PAUSED: " + r + (timeout ? " (due to timeout)" : " (pause complete)")); r.state = ActivityState.PAUSED; - completePauseLocked(r); + completePauseLocked(); } else { - ActivityRecord old = mPausingActivities.size() > 0 - ? mPausingActivities.get(0) : null; EventLog.writeEvent(EventLogTags.AM_FAILED_TO_PAUSE, System.identityHashCode(r), r.shortComponentName, - old != null ? old.shortComponentName : "(none)"); + mPausingActivity != null + ? mPausingActivity.shortComponentName : "(none)"); } } } @@ -1024,14 +1001,8 @@ final class ActivityStack { ProcessRecord fgApp = null; if (mResumedActivity != null) { fgApp = mResumedActivity.app; - } else { - for (int i=mPausingActivities.size()-1; i>=0; i--) { - ActivityRecord pausing = mPausingActivities.get(i); - if (pausing.app == r.app) { - fgApp = pausing.app; - break; - } - } + } else if (mPausingActivity != null) { + fgApp = mPausingActivity.app; } if (r.app != null && fgApp != null && r.app != fgApp && r.lastVisibleTime > mService.mPreviousProcessVisibleTime @@ -1043,49 +1014,58 @@ final class ActivityStack { } } - private final void completePauseLocked(ActivityRecord prev) { + private final void completePauseLocked() { + ActivityRecord prev = mPausingActivity; if (DEBUG_PAUSE) Slog.v(TAG, "Complete pause: " + prev); - if (prev.finishing) { - if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev); - prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE); - } else if (prev.app != null) { - if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev); - if (prev.waitingVisible) { - prev.waitingVisible = false; - mWaitingVisibleActivities.remove(prev); - if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v( - TAG, "Complete pause, no longer waiting: " + prev); - } - if (prev.configDestroy) { - // The previous is being paused because the configuration - // is changing, which means it is actually stopping... - // To juggle the fact that we are also starting a new - // instance right now, we need to first completely stop - // the current instance before starting the new one. - if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); - destroyActivityLocked(prev, true, false, "pause-config"); - } else { - mStoppingActivities.add(prev); - if (mStoppingActivities.size() > 3) { - // If we already have a few activities waiting to stop, - // then give up on things going idle and start clearing - // them out. - if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle"); - scheduleIdleLocked(); + if (prev != null) { + if (prev.finishing) { + if (DEBUG_PAUSE) Slog.v(TAG, "Executing finish of activity: " + prev); + prev = finishCurrentActivityLocked(prev, FINISH_AFTER_VISIBLE); + } else if (prev.app != null) { + if (DEBUG_PAUSE) Slog.v(TAG, "Enqueueing pending stop: " + prev); + if (prev.waitingVisible) { + prev.waitingVisible = false; + mWaitingVisibleActivities.remove(prev); + if (DEBUG_SWITCH || DEBUG_PAUSE) Slog.v( + TAG, "Complete pause, no longer waiting: " + prev); + } + if (prev.configDestroy) { + // The previous is being paused because the configuration + // is changing, which means it is actually stopping... + // To juggle the fact that we are also starting a new + // instance right now, we need to first completely stop + // the current instance before starting the new one. + if (DEBUG_PAUSE) Slog.v(TAG, "Destroying after pause: " + prev); + destroyActivityLocked(prev, true, false, "pause-config"); } else { - checkReadyForSleepLocked(); + mStoppingActivities.add(prev); + if (mStoppingActivities.size() > 3) { + // If we already have a few activities waiting to stop, + // then give up on things going idle and start clearing + // them out. + if (DEBUG_PAUSE) Slog.v(TAG, "To many pending stops, forcing idle"); + scheduleIdleLocked(); + } else { + checkReadyForSleepLocked(); + } } + } else { + if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev); + prev = null; } - } else { - if (DEBUG_PAUSE) Slog.v(TAG, "App died during pause, not stopping: " + prev); - prev = null; + mPausingActivity = null; } - mPausingActivities.remove(prev); - if (mService.isSleeping()) { + if (!mService.isSleeping()) { + resumeTopActivityLocked(prev); + } else { checkReadyForSleepLocked(); } + + if (prev != null) { + prev.resumeKeyDispatchingLocked(); + } if (prev.app != null && prev.cpuTimeAtResume > 0 && mService.mBatteryStatsService.isOnBattery()) { @@ -1142,9 +1122,7 @@ final class ActivityStack { if (mMainStack) { mService.setFocusedActivityLocked(next); } - if (mInputPausedActivities.remove(next)) { - next.resumeKeyDispatchingLocked(); - } + next.resumeKeyDispatchingLocked(); ensureActivitiesVisibleLocked(null, 0); mService.mWindowManager.executeAppTransition(); mNoAnimActivities.clear(); @@ -1374,6 +1352,13 @@ final class ActivityStack { if (DEBUG_SWITCH) Slog.v(TAG, "Resuming " + next); + // If we are currently pausing an activity, then don't do anything + // until that is done. + if (mPausingActivity != null) { + if (DEBUG_SWITCH) Slog.v(TAG, "Skip resume: pausing=" + mPausingActivity); + return false; + } + // Okay we are now going to start a switch, to 'next'. We may first // have to pause the current activity, but this is an important point // where we have decided to go to 'next' so keep track of that. @@ -2455,7 +2440,7 @@ final class ActivityStack { err = startActivityUncheckedLocked(r, sourceRecord, grantedUriPermissions, grantedMode, onlyIfNeeded, true); - if (mDismissKeyguardOnNextActivity && mPausingActivities.size() == 0) { + if (mDismissKeyguardOnNextActivity && mPausingActivity == null) { // Someone asked to have the keyguard dismissed on the next // activity start, but we are not actually doing an activity // switch... just dismiss the keyguard now, because we @@ -3126,18 +3111,7 @@ final class ActivityStack { } mService.notifyAll(); } - - void reportActivityDrawnLocked(ActivityRecord r) { - if (mResumedActivity == r) { - // Once the resumed activity has been drawn, we can stop - // pausing input on all other activities. - for (int i=mInputPausedActivities.size()-1; i>=0; i--) { - mInputPausedActivities.get(i).resumeKeyDispatchingLocked(); - } - mInputPausedActivities.clear(); - } - } - + void reportActivityVisibleLocked(ActivityRecord r) { for (int i=mWaitingActivityVisible.size()-1; i>=0; i--) { WaitResult w = mWaitingActivityVisible.get(i); @@ -3196,9 +3170,7 @@ final class ActivityStack { mService.setFocusedActivityLocked(topRunningActivityLocked(null)); } } - if (mInputPausedActivities.remove(r)) { - r.resumeKeyDispatchingLocked(); - } + r.resumeKeyDispatchingLocked(); try { r.stopped = false; if (DEBUG_STATES) Slog.v(TAG, "Moving to STOPPING: " + r @@ -3524,7 +3496,7 @@ final class ActivityStack { // Tell window manager to prepare for this one to be removed. mService.mWindowManager.setAppVisibility(r.appToken, false); - if (!mPausingActivities.contains(r)) { + if (mPausingActivity == null) { if (DEBUG_PAUSE) Slog.v(TAG, "Finish needs to pause: " + r); if (DEBUG_USER_LEAVING) Slog.v(TAG, "finish() => pause with userLeaving=false"); startPausingLocked(false, false); @@ -3608,20 +3580,6 @@ final class ActivityStack { return r; } - final void appDiedLocked(ProcessRecord app) { - for (int i=mPausingActivities.size()-1; i>=0; i--) { - ActivityRecord r = mPausingActivities.get(i); - if (r.app == app) { - if (DEBUG_PAUSE) Slog.v(TAG, "App died while pausing: " + r); - mPausingActivities.remove(i); - mHandler.removeMessages(PAUSE_TIMEOUT_MSG, r); - } - } - if (mLastPausedActivity != null && mLastPausedActivity.app == app) { - mLastPausedActivity = null; - } - } - /** * Perform the common clean-up of an activity record. This is called both * as part of destroyActivityLocked() (when destroying the client-side diff --git a/services/java/com/android/server/am/ContentProviderRecord.java b/services/java/com/android/server/am/ContentProviderRecord.java index 38355537e1c6..f338cfcb7737 100644 --- a/services/java/com/android/server/am/ContentProviderRecord.java +++ b/services/java/com/android/server/am/ContentProviderRecord.java @@ -20,24 +20,35 @@ import android.app.IActivityManager.ContentProviderHolder; import android.content.ComponentName; import android.content.pm.ApplicationInfo; import android.content.pm.ProviderInfo; +import android.os.IBinder; +import android.os.IBinder.DeathRecipient; import android.os.Process; +import android.os.RemoteException; +import android.util.Slog; import java.io.PrintWriter; +import java.util.HashMap; import java.util.HashSet; class ContentProviderRecord extends ContentProviderHolder { // All attached clients final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>(); + // Handles for non-framework processes supported by this provider + HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle; + // Count for external process for which we have no handles. + int externalProcessNoHandleCount; + final ActivityManagerService service; final int uid; final ApplicationInfo appInfo; final ComponentName name; - int externals; // number of non-framework processes supported by this provider ProcessRecord proc; // if non-null, hosting process. ProcessRecord launchingApp; // if non-null, waiting for this app to be launched. String stringName; - - public ContentProviderRecord(ProviderInfo _info, ApplicationInfo ai, ComponentName _name) { + + public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info, + ApplicationInfo ai, ComponentName _name) { super(_info); + service = _service; uid = ai.uid; appInfo = ai; name = _name; @@ -50,6 +61,7 @@ class ContentProviderRecord extends ContentProviderHolder { appInfo = cpr.appInfo; name = cpr.name; noReleaseNeeded = cpr.noReleaseNeeded; + service = cpr.service; } public boolean canRunHere(ProcessRecord app) { @@ -57,6 +69,57 @@ class ContentProviderRecord extends ContentProviderHolder { && (uid == Process.SYSTEM_UID || uid == app.info.uid); } + public void addExternalProcessHandleLocked(IBinder token) { + if (token == null) { + externalProcessNoHandleCount++; + } else { + if (externalProcessTokenToHandle == null) { + externalProcessTokenToHandle = new HashMap<IBinder, ExternalProcessHandle>(); + } + ExternalProcessHandle handle = externalProcessTokenToHandle.get(token); + if (handle == null) { + handle = new ExternalProcessHandle(token); + externalProcessTokenToHandle.put(token, handle); + } + handle.mAcquisitionCount++; + } + } + + public boolean removeExternalProcessHandleLocked(IBinder token) { + if (hasExternalProcessHandles()) { + boolean hasHandle = false; + if (externalProcessTokenToHandle != null) { + ExternalProcessHandle handle = externalProcessTokenToHandle.get(token); + if (handle != null) { + hasHandle = true; + handle.mAcquisitionCount--; + if (handle.mAcquisitionCount == 0) { + removeExternalProcessHandleInternalLocked(token); + return true; + } + } + } + if (!hasHandle) { + externalProcessNoHandleCount--; + return true; + } + } + return false; + } + + private void removeExternalProcessHandleInternalLocked(IBinder token) { + ExternalProcessHandle handle = externalProcessTokenToHandle.get(token); + handle.unlinkFromOwnDeathLocked(); + externalProcessTokenToHandle.remove(token); + if (externalProcessTokenToHandle.size() == 0) { + externalProcessTokenToHandle = null; + } + } + + public boolean hasExternalProcessHandles() { + return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0); + } + void dump(PrintWriter pw, String prefix) { pw.print(prefix); pw.print("package="); pw.print(info.applicationInfo.packageName); @@ -73,8 +136,9 @@ 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 (hasExternalProcessHandles()) { + pw.print(prefix); pw.print("externals="); + pw.println(externalProcessTokenToHandle.size()); } if (clients.size() > 0) { pw.print(prefix); pw.println("Clients:"); @@ -84,6 +148,7 @@ class ContentProviderRecord extends ContentProviderHolder { } } + @Override public String toString() { if (stringName != null) { return stringName; @@ -96,4 +161,35 @@ class ContentProviderRecord extends ContentProviderHolder { sb.append('}'); return stringName = sb.toString(); } + + // This class represents a handle from an external process to a provider. + private class ExternalProcessHandle implements DeathRecipient { + private static final String LOG_TAG = "ExternalProcessHanldle"; + + private final IBinder mToken; + private int mAcquisitionCount; + + public ExternalProcessHandle(IBinder token) { + mToken = token; + try { + token.linkToDeath(this, 0); + } catch (RemoteException re) { + Slog.e(LOG_TAG, "Couldn't register for death for token: " + mToken, re); + } + } + + public void unlinkFromOwnDeathLocked() { + mToken.unlinkToDeath(this, 0); + } + + @Override + public void binderDied() { + synchronized (service) { + if (hasExternalProcessHandles() && + externalProcessTokenToHandle.get(mToken) != null) { + removeExternalProcessHandleInternalLocked(mToken); + } + } + } + } } diff --git a/services/java/com/android/server/wm/WindowManagerService.java b/services/java/com/android/server/wm/WindowManagerService.java index 1c81bb1debcf..dafc6135229d 100644 --- a/services/java/com/android/server/wm/WindowManagerService.java +++ b/services/java/com/android/server/wm/WindowManagerService.java @@ -268,6 +268,7 @@ public class WindowManagerService extends IWindowManager.Stub final TokenWatcher mKeyguardTokenWatcher = new TokenWatcher( new Handler(), "WindowManagerService.mKeyguardTokenWatcher") { + @Override public void acquired() { if (shouldAllowDisableKeyguard()) { mPolicy.enableKeyguard(false); @@ -276,6 +277,7 @@ public class WindowManagerService extends IWindowManager.Stub Log.v(TAG, "Not disabling keyguard since device policy is enforced"); } } + @Override public void released() { mPolicy.enableKeyguard(true); synchronized (mKeyguardTokenWatcher) { @@ -599,6 +601,7 @@ public class WindowManagerService extends IWindowManager.Stub private boolean mSyswin = false; private float mScreenBrightness = -1; private float mButtonBrightness = -1; + private boolean mUpdateRotation = false; } private LayoutAndSurfaceFields mInnerFields = new LayoutAndSurfaceFields(); @@ -6377,7 +6380,7 @@ public class WindowManagerService extends IWindowManager.Stub INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); - return reportInjectionResult(result); + return reportInjectionResult(result, pid); } /** @@ -6407,7 +6410,7 @@ public class WindowManagerService extends IWindowManager.Stub INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); - return reportInjectionResult(result); + return reportInjectionResult(result, pid); } /** @@ -6437,7 +6440,7 @@ public class WindowManagerService extends IWindowManager.Stub INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); - return reportInjectionResult(result); + return reportInjectionResult(result, pid); } /** @@ -6458,24 +6461,23 @@ public class WindowManagerService extends IWindowManager.Stub INJECTION_TIMEOUT_MILLIS); Binder.restoreCallingIdentity(ident); - return reportInjectionResult(result); + return reportInjectionResult(result, pid); } - private boolean reportInjectionResult(int result) { + private boolean reportInjectionResult(int result, int pid) { switch (result) { case InputManager.INPUT_EVENT_INJECTION_PERMISSION_DENIED: - Slog.w(TAG, "Input event injection permission denied."); + Slog.w(TAG, "Input event injection from pid " + pid + " permission denied."); throw new SecurityException( "Injecting to another application requires INJECT_EVENTS permission"); case InputManager.INPUT_EVENT_INJECTION_SUCCEEDED: - //Slog.v(TAG, "Input event injection succeeded."); return true; case InputManager.INPUT_EVENT_INJECTION_TIMED_OUT: - Slog.w(TAG, "Input event injection timed out."); + Slog.w(TAG, "Input event injection from pid " + pid + " timed out."); return false; case InputManager.INPUT_EVENT_INJECTION_FAILED: default: - Slog.w(TAG, "Input event injection failed."); + Slog.w(TAG, "Input event injection from pid " + pid + " failed."); return false; } } @@ -7621,53 +7623,53 @@ public class WindowManagerService extends IWindowManager.Stub * @param innerDh Height of app window. * @return true if rotation has stopped, false otherwise */ - private boolean updateAppsAndRotationAnimationsLocked(long currentTime, + private void updateWindowsAppsAndRotationAnimationsLocked(long currentTime, int innerDw, int innerDh) { int i; + for (i = mWindows.size() - 1; i >= 0; i--) { + mInnerFields.mAnimating |= mWindows.get(i).stepAnimationLocked(currentTime); + } + final int NAT = mAppTokens.size(); for (i=0; i<NAT; i++) { - if (mAppTokens.get(i).stepAnimationLocked(currentTime, - innerDw, innerDh)) { - mInnerFields.mAnimating = true; - } + mInnerFields.mAnimating |= + mAppTokens.get(i).stepAnimationLocked(currentTime, innerDw, innerDh); } final int NEAT = mExitingAppTokens.size(); for (i=0; i<NEAT; i++) { - if (mExitingAppTokens.get(i).stepAnimationLocked(currentTime, - innerDw, innerDh)) { - mInnerFields.mAnimating = true; - } + mInnerFields.mAnimating |= + mExitingAppTokens.get(i).stepAnimationLocked(currentTime, innerDw, innerDh); } - boolean updateRotation = false; if (mScreenRotationAnimation != null) { if (mScreenRotationAnimation.isAnimating()) { if (mScreenRotationAnimation.stepAnimation(currentTime)) { + mInnerFields.mUpdateRotation = false; mInnerFields.mAnimating = true; } else { - updateRotation = true; + mInnerFields.mUpdateRotation = true; mScreenRotationAnimation.kill(); mScreenRotationAnimation = null; } } } - - return updateRotation; } /** * Extracted from {@link #performLayoutAndPlaceSurfacesLockedInner} to reduce size of method. * * @param currentTime The time which animations use for calculating transitions. + * @param dw Width of app window. + * @param dh Height of app window. * @param innerDw Width of app window. * @param innerDh Height of app window. */ - private void updateWindowsAndWallpaperLocked(final long currentTime, - final int innerDw, final int innerDh) { - int i; - final int N = mWindows.size(); + private int updateWindowsAndWallpaperLocked(final long currentTime, final int dw, final int dh, + final int innerDw, final int innerDh) { + + mPolicy.beginAnimationLw(dw, dh); - for (i=N-1; i>=0; i--) { + for (int i = mWindows.size() - 1; i >= 0; i--) { WindowState w = mWindows.get(i); final WindowManager.LayoutParams attrs = w.mAttrs; @@ -7685,9 +7687,6 @@ public class WindowManagerService extends IWindowManager.Stub final boolean wasAnimating = w.mAnimating; - int animDw = innerDw; - int animDh = innerDh; - // If the window has moved due to its containing // content frame changing, then we'd like to animate // it. The checks here are ordered by what is least @@ -7699,13 +7698,15 @@ public class WindowManagerService extends IWindowManager.Stub Animation a = AnimationUtils.loadAnimation(mContext, com.android.internal.R.anim.window_move_from_decor); w.setAnimation(a); - animDw = w.mLastFrame.left - w.mFrame.left; - animDh = w.mLastFrame.top - w.mFrame.top; + w.mAnimDw = w.mLastFrame.left - w.mFrame.left; + w.mAnimDh = w.mLastFrame.top - w.mFrame.top; + } else { + w.mAnimDw = innerDw; + w.mAnimDh = innerDh; } // Execute animation. - final boolean nowAnimating = w.stepAnimationLocked(currentTime, - animDw, animDh); + final boolean nowAnimating = w.isAnimating(); // If this window is animating, make a note that we have // an animating window and take care of a request to run @@ -7847,6 +7848,8 @@ public class WindowManagerService extends IWindowManager.Stub w.performShowLocked(); } } // end forall windows + + return mPolicy.finishAnimationLw(); } /** @@ -8117,7 +8120,7 @@ public class WindowManagerService extends IWindowManager.Stub * * @return bitmap indicating if another pass through layout must be made. */ - private int handleAnimatingAndTransitionLocked() { + private int handleAnimatingStoppedAndTransitionLocked() { int changes = 0; mAppTransitionRunning = false; @@ -8653,7 +8656,6 @@ public class WindowManagerService extends IWindowManager.Stub boolean focusDisplayed = false; mInnerFields.mAnimating = false; boolean createWatermark = false; - boolean updateRotation = false; if (mFxSession == null) { mFxSession = new SurfaceSession(); @@ -8707,22 +8709,13 @@ public class WindowManagerService extends IWindowManager.Stub // FIRST LOOP: Perform a layout, if needed. if (repeats < 4) { - performLayoutLockedInner(repeats == 0, false /*updateInputWindows*/); + performLayoutLockedInner(repeats == 1, false /*updateInputWindows*/); } else { Slog.w(TAG, "Layout repeat skipped after too many iterations"); } - changes = 0; ++mTransactionSequence; - // Update animations of all applications, including those - // associated with exiting/removed apps - mInnerFields.mAnimating = false; - - // SECOND LOOP: Execute animations and update visibility of windows. - updateRotation |= - updateAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh); - if (DEBUG_APP_TRANSITIONS) Slog.v(TAG, "*** ANIM STEP: seq=" + mTransactionSequence + " mAnimating=" + mInnerFields.mAnimating); @@ -8734,11 +8727,7 @@ public class WindowManagerService extends IWindowManager.Stub mInnerFields.mWindowAnimationBackground = null; mInnerFields.mWindowAnimationBackgroundColor = 0; - mPolicy.beginAnimationLw(dw, dh); - - updateWindowsAndWallpaperLocked(currentTime, innerDw, innerDh); - - changes |= mPolicy.finishAnimationLw(); + changes = updateWindowsAndWallpaperLocked(currentTime, dw, dh, innerDw, innerDh); if (mInnerFields.mTokenMayBeDrawn) { changes |= testTokenMayBeDrawnLocked(); @@ -8760,7 +8749,7 @@ public class WindowManagerService extends IWindowManager.Stub // reflects the correct Z-order, but the window list may now // be out of sync with it. So here we will just rebuild the // entire app window list. Fun! - changes |= handleAnimatingAndTransitionLocked(); + changes |= handleAnimatingStoppedAndTransitionLocked(); } if (mInnerFields.mWallpaperForceHidingChanged && changes == 0 && !mAppTransitionReady) { @@ -8783,6 +8772,12 @@ public class WindowManagerService extends IWindowManager.Stub + Integer.toHexString(changes)); } while (changes != 0); + // Update animations of all applications, including those + // associated with exiting/removed apps + mInnerFields.mAnimating = false; + + updateWindowsAppsAndRotationAnimationsLocked(currentTime, innerDw, innerDh); + // THIRD LOOP: Update the surfaces of all windows. final boolean someoneLosingFocus = mLosingFocus.size() != 0; @@ -9024,16 +9019,17 @@ public class WindowManagerService extends IWindowManager.Stub mTurnOnScreen = false; } - if (updateRotation) { + if (mInnerFields.mUpdateRotation) { if (DEBUG_ORIENTATION) Slog.d(TAG, "Performing post-rotate rotation"); if (updateRotationUncheckedLocked(false)) { mH.sendEmptyMessage(H.SEND_NEW_CONFIGURATION); } else { - updateRotation = false; + mInnerFields.mUpdateRotation = false; } } - if (mInnerFields.mOrientationChangeComplete && !needRelayout && !updateRotation) { + if (mInnerFields.mOrientationChangeComplete && !needRelayout && + !mInnerFields.mUpdateRotation) { checkDrawnWindowsLocked(); } diff --git a/services/java/com/android/server/wm/WindowState.java b/services/java/com/android/server/wm/WindowState.java index b013d2799520..d7a7cb0d34a3 100644 --- a/services/java/com/android/server/wm/WindowState.java +++ b/services/java/com/android/server/wm/WindowState.java @@ -297,6 +297,11 @@ final class WindowState implements WindowManagerPolicy.WindowState { CharSequence mLastTitle; boolean mWasPaused; + // Used to save animation distances between the time they are calculated and when they are + // used. + int mAnimDw; + int mAnimDh; + WindowState(WindowManagerService service, Session s, IWindow c, WindowToken token, WindowState attachedWindow, int seq, WindowManager.LayoutParams a, int viewVisibility) { @@ -973,7 +978,7 @@ final class WindowState implements WindowManagerPolicy.WindowState { // This must be called while inside a transaction. Returns true if // there is more animation to run. - boolean stepAnimationLocked(long currentTime, int dw, int dh) { + boolean stepAnimationLocked(long currentTime) { if (!mService.mDisplayFrozen && mService.mPolicy.isScreenOnFully()) { // We will run animations as long as the display isn't frozen. @@ -985,8 +990,9 @@ final class WindowState implements WindowManagerPolicy.WindowState { WindowManagerService.TAG, "Starting animation in " + this + " @ " + currentTime + ": ww=" + mFrame.width() + " wh=" + mFrame.height() + - " dw=" + dw + " dh=" + dh + " scale=" + mService.mWindowAnimationScale); - mAnimation.initialize(mFrame.width(), mFrame.height(), dw, dh); + " dw=" + mAnimDw + " dh=" + mAnimDh + + " scale=" + mService.mWindowAnimationScale); + mAnimation.initialize(mFrame.width(), mFrame.height(), mAnimDw, mAnimDh); mAnimation.setStartTime(currentTime); mLocalAnimating = true; mAnimating = true; diff --git a/services/jni/Android.mk b/services/jni/Android.mk index c63b84df88b7..c02dd36d504c 100644 --- a/services/jni/Android.mk +++ b/services/jni/Android.mk @@ -26,6 +26,7 @@ LOCAL_C_INCLUDES += \ LOCAL_SHARED_LIBRARIES := \ libandroid_runtime \ + libandroidfw \ libcutils \ libhardware \ libhardware_legacy \ diff --git a/services/jni/com_android_server_PowerManagerService.h b/services/jni/com_android_server_PowerManagerService.h index af107113ecf4..cc3b5ef54016 100644 --- a/services/jni/com_android_server_PowerManagerService.h +++ b/services/jni/com_android_server_PowerManagerService.h @@ -20,7 +20,7 @@ #include "JNIHelp.h" #include "jni.h" -#include <ui/PowerManager.h> +#include <androidfw/PowerManager.h> namespace android { diff --git a/services/surfaceflinger/LayerScreenshot.cpp b/services/surfaceflinger/LayerScreenshot.cpp index c127fa621fe1..c7cf46e53a41 100644 --- a/services/surfaceflinger/LayerScreenshot.cpp +++ b/services/surfaceflinger/LayerScreenshot.cpp @@ -70,6 +70,8 @@ void LayerScreenshot::initTexture(GLfloat u, GLfloat v) { glBindTexture(GL_TEXTURE_2D, mTextureName); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); mTexCoords[0] = 0; mTexCoords[1] = v; mTexCoords[2] = 0; mTexCoords[3] = 0; mTexCoords[4] = u; mTexCoords[5] = 0; diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp index 40717f4e95db..9e3f54839574 100644 --- a/services/surfaceflinger/SurfaceFlinger.cpp +++ b/services/surfaceflinger/SurfaceFlinger.cpp @@ -1922,6 +1922,8 @@ status_t SurfaceFlinger::electronBeamOffAnimationImplLocked() glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vtx); @@ -2094,6 +2096,8 @@ status_t SurfaceFlinger::electronBeamOnAnimationImplLocked() glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexCoordPointer(2, GL_FLOAT, 0, texCoords); glEnableClientState(GL_TEXTURE_COORD_ARRAY); glVertexPointer(2, GL_FLOAT, 0, vtx); diff --git a/services/surfaceflinger/tests/resize/resize.cpp b/services/surfaceflinger/tests/resize/resize.cpp index 56b2a8f1cb03..7f3f0644c73a 100644 --- a/services/surfaceflinger/tests/resize/resize.cpp +++ b/services/surfaceflinger/tests/resize/resize.cpp @@ -39,7 +39,7 @@ int main(int argc, char** argv) // create a client to surfaceflinger sp<SurfaceComposerClient> client = new SurfaceComposerClient(); - sp<Surface> surface = client->createSurface(getpid(), 0, 160, 240, + sp<Surface> surface = client->createSurface(0, 160, 240, PIXEL_FORMAT_RGB_565); diff --git a/services/surfaceflinger/tests/surface/surface.cpp b/services/surfaceflinger/tests/surface/surface.cpp index 8e1c3fe322b4..9c15f9bb751a 100644 --- a/services/surfaceflinger/tests/surface/surface.cpp +++ b/services/surfaceflinger/tests/surface/surface.cpp @@ -38,7 +38,7 @@ int main(int argc, char** argv) sp<SurfaceComposerClient> client = new SurfaceComposerClient(); sp<SurfaceControl> surfaceControl = client->createSurface( - getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565); + 0, 160, 240, PIXEL_FORMAT_RGB_565); SurfaceComposerClient::openGlobalTransaction(); surfaceControl->setLayer(100000); SurfaceComposerClient::closeGlobalTransaction(); diff --git a/test-runner/src/android/test/mock/MockContext.java b/test-runner/src/android/test/mock/MockContext.java index 674c0b700b5a..5fab2bb2d65d 100644 --- a/test-runner/src/android/test/mock/MockContext.java +++ b/test-runner/src/android/test/mock/MockContext.java @@ -267,6 +267,12 @@ public class MockContext extends Context { throw new UnsupportedOperationException(); } + /** @hide */ + @Override + public void sendBroadcast(Intent intent, int userId) { + throw new UnsupportedOperationException(); + } + @Override public void sendBroadcast(Intent intent, String receiverPermission) { throw new UnsupportedOperationException(); diff --git a/tests/HwAccelerationTest/AndroidManifest.xml b/tests/HwAccelerationTest/AndroidManifest.xml index 3904c214b54d..ed78daa3651e 100644 --- a/tests/HwAccelerationTest/AndroidManifest.xml +++ b/tests/HwAccelerationTest/AndroidManifest.xml @@ -568,6 +568,15 @@ </activity> <activity + android:name="PathsCacheActivity" + android:label="_PathsCache"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <activity android:name="PointsActivity" android:label="_Points"> <intent-filter> diff --git a/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java new file mode 100644 index 000000000000..b8ad823bf73a --- /dev/null +++ b/tests/HwAccelerationTest/src/com/android/test/hwui/PathsCacheActivity.java @@ -0,0 +1,111 @@ +/* + * 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 com.android.test.hwui; + +import android.app.Activity; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Path; +import android.os.Bundle; +import android.util.Log; +import android.view.View; + +import java.util.ArrayList; +import java.util.Random; + +@SuppressWarnings({"UnusedDeclaration"}) +public class PathsCacheActivity extends Activity { + private Path mPath; + + private final Random mRandom = new Random(); + private final ArrayList<Path> mPathList = new ArrayList<Path>(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + mPath = makePath(); + + final PathsView view = new PathsView(this); + setContentView(view); + } + + private Path makePath() { + Path path = new Path(); + buildPath(path); + return path; + } + + private void buildPath(Path path) { + path.moveTo(0.0f, 0.0f); + path.cubicTo(0.0f, 0.0f, 100.0f, 150.0f, 100.0f, 200.0f); + path.cubicTo(100.0f, 200.0f, 50.0f, 300.0f, -80.0f, 200.0f); + path.cubicTo(-80.0f, 200.0f, 100.0f, 200.0f, 200.0f, 0.0f); + } + + public class PathsView extends View { + private final Paint mMediumPaint; + + public PathsView(Context c) { + super(c); + + mMediumPaint = new Paint(); + mMediumPaint.setAntiAlias(true); + mMediumPaint.setColor(0xe00000ff); + mMediumPaint.setStrokeWidth(10.0f); + mMediumPaint.setStyle(Paint.Style.STROKE); + } + + @Override + protected void onDraw(Canvas canvas) { + super.onDraw(canvas); + + Log.d("OpenGLRenderer", "Start frame"); + + canvas.drawARGB(255, 255, 255, 255); + + canvas.save(); + canvas.translate(550.0f, 60.0f); + canvas.drawPath(mPath, mMediumPaint); + + mPath.reset(); + buildPath(mPath); + + canvas.translate(30.0f, 30.0f); + canvas.drawPath(mPath, mMediumPaint); + canvas.drawPath(mPath, mMediumPaint); + + canvas.restore(); + +// Path path = makePath(); +// int r = mRandom.nextInt(10); +// if (r == 5 || r == 3) { +// mPathList.add(path); +// } else if (r == 9) { +// mPathList.clear(); +// } +// +// canvas.save(); +// canvas.translate(550.0f + mRandom.nextInt(50), 60.0f + mRandom.nextInt(50)); +// canvas.drawPath(path, mMediumPaint); +// canvas.restore(); +// + invalidate(); + } + } +} diff --git a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml index e8d1e8e2b080..67af0fa04f53 100644 --- a/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml +++ b/tests/RenderScriptTests/SceneGraph/AndroidManifest.xml @@ -11,6 +11,13 @@ <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> + <activity android:name="SimpleApp" + android:label="SimpleSceneGraph"> + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> <activity android:name="FileSelector" android:label="FileSelector" android:hardwareAccelerated="true"> diff --git a/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png Binary files differnew file mode 100644 index 000000000000..ff34a7ffcc4f --- /dev/null +++ b/tests/RenderScriptTests/SceneGraph/res/drawable-nodpi/icon.png diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl index fa468cc3620c..c34adc9e334d 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_h.glsl @@ -3,13 +3,13 @@ varying vec2 varTex0; void main() { vec2 blurCoord = varTex0; blurCoord.x = varTex0.x + UNI_blurOffset0; - vec3 col = texture2D(UNI_Tex0, blurCoord).rgb; + vec3 col = texture2D(UNI_color, blurCoord).rgb; blurCoord.x = varTex0.x + UNI_blurOffset1; - col += texture2D(UNI_Tex0, blurCoord).rgb; + col += texture2D(UNI_color, blurCoord).rgb; blurCoord.x = varTex0.x + UNI_blurOffset2; - col += texture2D(UNI_Tex0, blurCoord).rgb; + col += texture2D(UNI_color, blurCoord).rgb; blurCoord.x = varTex0.x + UNI_blurOffset3; - col += texture2D(UNI_Tex0, blurCoord).rgb; + col += texture2D(UNI_color, blurCoord).rgb; - gl_FragColor = vec4(col * 0.25, 0.0); //texture2D(UNI_Tex0, varTex0); + gl_FragColor = vec4(col * 0.25, 0.0); } diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl index a644a3e6b8fb..ade05a27920d 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/blur_v.glsl @@ -3,15 +3,15 @@ varying vec2 varTex0; void main() { vec2 blurCoord = varTex0; blurCoord.y = varTex0.y + UNI_blurOffset0; - vec3 col = texture2D(UNI_Tex0, blurCoord).rgb; + vec3 col = texture2D(UNI_color, blurCoord).rgb; blurCoord.y = varTex0.y + UNI_blurOffset1; - col += texture2D(UNI_Tex0, blurCoord).rgb; + col += texture2D(UNI_color, blurCoord).rgb; blurCoord.y = varTex0.y + UNI_blurOffset2; - col += texture2D(UNI_Tex0, blurCoord).rgb; + col += texture2D(UNI_color, blurCoord).rgb; blurCoord.y = varTex0.y + UNI_blurOffset3; - col += texture2D(UNI_Tex0, blurCoord).rgb; + col += texture2D(UNI_color, blurCoord).rgb; col = col * 0.25; - gl_FragColor = vec4(col, 0.0); //texture2D(UNI_Tex0, varTex0); + gl_FragColor = vec4(col, 0.0); } diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl index 5d8938bc5a4e..2eb102872afd 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/diffuse.glsl @@ -12,8 +12,8 @@ void main() { float light0_Diffuse = dot(worldNorm, light0Vec); vec2 t0 = varTex0.xy; - lowp vec4 col = texture2D(UNI_Tex0, t0).rgba; + lowp vec4 col = texture2D(UNI_diffuse, t0).rgba; col.xyz = col.xyz * light0_Diffuse * 1.2; - gl_FragColor = col; //vec4(0.0, 1.0, 0.0, 0.0); + gl_FragColor = col; } diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl index 51f06124d759..b90a7b27354c 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/metal.glsl @@ -14,8 +14,8 @@ void main() { float light0_Specular = pow(light0Spec, 15.0) * 0.5; vec2 t0 = varTex0.xy; - lowp vec4 col = texture2D(UNI_Tex0, t0).rgba; - col.xyz = col.xyz * (textureCube(UNI_Tex1, worldNorm).rgb * 0.5 + vec3(light0_Diffuse)); + lowp vec4 col = texture2D(UNI_diffuse, t0).rgba; + col.xyz = col.xyz * (textureCube(UNI_reflection, worldNorm).rgb * 0.5 + vec3(light0_Diffuse)); col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0); gl_FragColor = col; diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl index 893d55399003..f3b89ede5369 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/paintf.glsl @@ -14,12 +14,12 @@ void main() { float light0_Specular = pow(light0Spec, 150.0) * 0.5; vec2 t0 = varTex0.xy; - lowp vec4 col = texture2D(UNI_Tex0, t0).rgba; + lowp vec4 col = texture2D(UNI_diffuse, t0).rgba; col.xyz = col.xyz * light0_Diffuse * 1.1; col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0); float fresnel = mix(pow(1.0 - light0_Diffuse, 15.0), 1.0, 0.1); - col.xyz = mix(col.xyz, textureCube(UNI_Tex1, -light0R).rgb * 2.4, fresnel); + col.xyz = mix(col.xyz, textureCube(UNI_reflection, -light0R).rgb * 2.4, fresnel); col.w = 0.8; gl_FragColor = col; } diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl index ceb53bd232d2..56f7151f22af 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/plastic.glsl @@ -14,7 +14,7 @@ void main() { float light0_Specular = pow(light0Spec, 10.0) * 0.5; vec2 t0 = varTex0.xy; - lowp vec4 col = texture2D(UNI_Tex0, t0).rgba; + lowp vec4 col = texture2D(UNI_diffuse, t0).rgba; col.xyz = col.xyz * light0_Diffuse * 1.2; col.xyz += light0_Specular * vec3(0.8, 0.8, 1.0); gl_FragColor = col; diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl index 42b231aa8657..1a927cab6236 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/select_color.glsl @@ -1,7 +1,7 @@ varying vec2 varTex0; void main() { - vec3 col = texture2D(UNI_Tex0, varTex0).rgb; + vec3 col = texture2D(UNI_color, varTex0).rgb; vec3 desat = vec3(0.299, 0.587, 0.114); float lum = dot(desat, col); diff --git a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl index dd709cf01f9d..662ecd852af2 100644 --- a/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl +++ b/tests/RenderScriptTests/SceneGraph/res/raw/texture.glsl @@ -1,7 +1,7 @@ varying vec2 varTex0; void main() { - lowp vec4 col = texture2D(UNI_Tex0, varTex0).rgba; + lowp vec4 col = texture2D(UNI_color, varTex0).rgba; gl_FragColor = col; } diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java index d954313da12d..b4b6fb9b795b 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ColladaParser.java @@ -248,17 +248,17 @@ public class ColladaParser { String description = field.getAttribute("sid");
if (fieldName.equals("translate")) {
Float3 value = getFloat3(field);
- current.addComponent(new TranslateComponent(description, value));
+ current.addTranslate(description, value);
//Log.v(TAG, indent + " translate " + description + toString(value));
} else if (fieldName.equals("rotate")) {
Float4 value = getFloat4(field);
//Log.v(TAG, indent + " rotate " + description + toString(value));
Float3 axis = new Float3(value.x, value.y, value.z);
- current.addComponent(new RotateComponent(description, axis, value.w));
+ current.addRotate(description, axis, value.w);
} else if (fieldName.equals("scale")) {
Float3 value = getFloat3(field);
//Log.v(TAG, indent + " scale " + description + toString(value));
- current.addComponent(new ScaleComponent(description, value));
+ current.addScale(description, value);
} else if (fieldName.equals("instance_geometry")) {
getRenderable(field, current);
} else if (fieldName.equals("instance_light")) {
diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java index d995dd00af3a..9274b171b62c 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/CompoundTransform.java @@ -134,6 +134,24 @@ public class CompoundTransform extends Transform { mTransformComponents = new ArrayList<Component>(); } + public TranslateComponent addTranslate(String name, Float3 translate) { + TranslateComponent c = new TranslateComponent(name, translate); + addComponent(c); + return c; + } + + public RotateComponent addRotate(String name, Float3 axis, float angle) { + RotateComponent c = new RotateComponent(name, axis, angle); + addComponent(c); + return c; + } + + public ScaleComponent addScale(String name, Float3 scale) { + ScaleComponent c = new ScaleComponent(name, scale); + addComponent(c); + return c; + } + public void addComponent(Component c) { if (c.mParent != null) { throw new IllegalArgumentException("Transform components may not be shared"); diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java index c8cc3ac5d890..8a468db9a1cf 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/FragmentShader.java @@ -48,6 +48,11 @@ public class FragmentShader extends Shader { return this; } + public Builder setShader(String code) { + mBuilder.setShader(code); + return this; + } + public Builder setObjectConst(Type type) { mShader.mPerObjConstants = type; return this; @@ -78,10 +83,12 @@ public class FragmentShader extends Shader { mBuilder.addConstant(mShader.mPerObjConstants); } for (int i = 0; i < mShader.mTextureTypes.size(); i ++) { - mBuilder.addTexture(mShader.mTextureTypes.get(i)); + mBuilder.addTexture(mShader.mTextureTypes.get(i), + mShader.mTextureNames.get(i)); } for (int i = 0; i < mShader.mShaderTextureTypes.size(); i ++) { - mBuilder.addTexture(mShader.mShaderTextureTypes.get(i)); + mBuilder.addTexture(mShader.mShaderTextureTypes.get(i), + mShader.mShaderTextureNames.get(i)); } mShader.mProgram = mBuilder.create(); diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java index 9f7ab411c00d..9266f3058cf7 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Renderable.java @@ -22,6 +22,7 @@ import java.util.HashMap; import java.util.Iterator; import com.android.scenegraph.Float4Param; +import com.android.scenegraph.MatrixTransform; import com.android.scenegraph.SceneManager; import com.android.scenegraph.ShaderParam; import com.android.scenegraph.TransformParam; @@ -89,6 +90,10 @@ public class Renderable extends RenderableBase { mMaterialName = name; } + public Transform getTransform() { + return mTransform; + } + public void setTransform(Transform t) { mTransform = t; if (mField != null) { @@ -196,12 +201,17 @@ public class Renderable extends RenderableBase { } void updateFieldItem(RenderScriptGL rs) { + if (mRenderState == null) { + mRenderState = SceneManager.getDefaultState(); + } + if (mTransform == null) { + mTransform = SceneManager.getDefaultTransform(); + } updateVertexConstants(rs); updateFragmentConstants(rs); - if (mTransform != null) { - mData.transformMatrix = mTransform.getRSData().getAllocation(); - } + mData.transformMatrix = mTransform.getRSData().getAllocation(); + mData.name = getNameAlloc(rs); mData.render_state = mRenderState.getRSData().getAllocation(); mData.bVolInitialized = 0; diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java index 8c09860c8b9e..27336abd5849 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Scene.java @@ -22,6 +22,10 @@ import java.util.HashMap; import java.util.regex.Matcher; import java.util.regex.Pattern; +import com.android.scenegraph.Camera; +import com.android.scenegraph.CompoundTransform; +import com.android.scenegraph.RenderPass; +import com.android.scenegraph.Renderable; import com.android.scenegraph.SceneManager; import com.android.scenegraph.TextureBase; @@ -75,9 +79,24 @@ public class Scene extends SceneGraphBase { } public void appendTransform(Transform t) { + if (t == null) { + throw new RuntimeException("Adding null object"); + } mRootTransforms.appendChild(t); } + public CompoundTransform appendNewCompoundTransform() { + CompoundTransform t = new CompoundTransform(); + appendTransform(t); + return t; + } + + public MatrixTransform appendNewMatrixTransform() { + MatrixTransform t = new MatrixTransform(); + appendTransform(t); + return t; + } + // temporary public void addToTransformMap(Transform t) { mTransformMap.put(t.getName(), t); @@ -88,26 +107,53 @@ public class Scene extends SceneGraphBase { } public void appendRenderPass(RenderPass p) { + if (p == null) { + throw new RuntimeException("Adding null object"); + } mRenderPasses.add(p); } + public RenderPass appendNewRenderPass() { + RenderPass p = new RenderPass(); + appendRenderPass(p); + return p; + } + public void clearRenderPasses() { mRenderPasses.clear(); } public void appendLight(LightBase l) { + if (l == null) { + throw new RuntimeException("Adding null object"); + } mLights.add(l); } public void appendCamera(Camera c) { + if (c == null) { + throw new RuntimeException("Adding null object"); + } mCameras.add(c); } + public Camera appendNewCamera() { + Camera c = new Camera(); + appendCamera(c); + return c; + } + public void appendShader(FragmentShader f) { + if (f == null) { + throw new RuntimeException("Adding null object"); + } mFragmentShaders.add(f); } public void appendShader(VertexShader v) { + if (v == null) { + throw new RuntimeException("Adding null object"); + } mVertexShaders.add(v); } @@ -120,8 +166,19 @@ public class Scene extends SceneGraphBase { } public void appendRenderable(RenderableBase d) { + if (d == null) { + throw new RuntimeException("Adding null object"); + } mRenderables.add(d); - mRenderableMap.put(d.getName(), d); + if (d.getName() != null) { + mRenderableMap.put(d.getName(), d); + } + } + + public Renderable appendNewRenderable() { + Renderable r = new Renderable(); + appendRenderable(r); + return r; } public ArrayList<RenderableBase> getRenderables() { @@ -133,6 +190,9 @@ public class Scene extends SceneGraphBase { } public void appendTextures(Texture2D tex) { + if (tex == null) { + throw new RuntimeException("Adding null object"); + } mTextures.add(tex); } @@ -224,24 +284,29 @@ public class Scene extends SceneGraphBase { } private void addShaders(RenderScriptGL rs, Resources res, SceneManager sceneManager) { - Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs), - mVertexShaders.size()); - Allocation[] shaderAllocs = new Allocation[mVertexShaders.size()]; - for (int i = 0; i < mVertexShaders.size(); i ++) { - VertexShader sI = mVertexShaders.get(i); - shaderAllocs[i] = sI.getRSData().getAllocation(); + if (mVertexShaders.size() > 0) { + Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs), + mVertexShaders.size()); + Allocation[] shaderAllocs = new Allocation[mVertexShaders.size()]; + for (int i = 0; i < mVertexShaders.size(); i ++) { + VertexShader sI = mVertexShaders.get(i); + shaderAllocs[i] = sI.getRSData().getAllocation(); + } + shaderData.copyFrom(shaderAllocs); + sceneManager.mRenderLoop.set_gVertexShaders(shaderData); } - shaderData.copyFrom(shaderAllocs); - sceneManager.mRenderLoop.set_gVertexShaders(shaderData); - - shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs), mFragmentShaders.size()); - shaderAllocs = new Allocation[mFragmentShaders.size()]; - for (int i = 0; i < mFragmentShaders.size(); i ++) { - FragmentShader sI = mFragmentShaders.get(i); - shaderAllocs[i] = sI.getRSData().getAllocation(); + + if (mFragmentShaders.size() > 0) { + Allocation shaderData = Allocation.createSized(rs, Element.ALLOCATION(rs), + mFragmentShaders.size()); + Allocation[] shaderAllocs = new Allocation[mFragmentShaders.size()]; + for (int i = 0; i < mFragmentShaders.size(); i ++) { + FragmentShader sI = mFragmentShaders.get(i); + shaderAllocs[i] = sI.getRSData().getAllocation(); + } + shaderData.copyFrom(shaderAllocs); + sceneManager.mRenderLoop.set_gFragmentShaders(shaderData); } - shaderData.copyFrom(shaderAllocs); - sceneManager.mRenderLoop.set_gFragmentShaders(shaderData); } public void initRS() { diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java index f77f48323770..4ff2c8b09371 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/SceneManager.java @@ -29,8 +29,10 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import com.android.scenegraph.Camera; +import com.android.scenegraph.FragmentShader; import com.android.scenegraph.MatrixTransform; import com.android.scenegraph.Scene; +import com.android.scenegraph.VertexShader; import com.android.testapp.R; import android.content.res.Resources; @@ -41,7 +43,6 @@ import android.renderscript.*; import android.renderscript.Allocation.MipmapControl; import android.renderscript.Mesh; import android.renderscript.RenderScriptGL; -import android.renderscript.Type.Builder; import android.util.Log; import android.view.SurfaceHolder; @@ -71,8 +72,15 @@ public class SceneManager extends SceneGraphBase { Scene mActiveScene; private static SceneManager sSceneManager; - private Allocation sDefault2D; - private Allocation sDefaultCube; + private Allocation mDefault2D; + private Allocation mDefaultCube; + + private FragmentShader mColor; + private FragmentShader mTexture; + private VertexShader mDefaultVertex; + + private RenderState mDefaultState; + private Transform mDefaultTransform; private static Allocation getDefault(boolean isCube) { final int dimension = 4; @@ -101,20 +109,20 @@ public class SceneManager extends SceneGraphBase { if (sSceneManager == null) { return null; } - if (sSceneManager.sDefault2D == null) { - sSceneManager.sDefault2D = getDefault(false); + if (sSceneManager.mDefault2D == null) { + sSceneManager.mDefault2D = getDefault(false); } - return sSceneManager.sDefault2D; + return sSceneManager.mDefault2D; } static Allocation getDefaultTexCube() { if (sSceneManager == null) { return null; } - if (sSceneManager.sDefaultCube == null) { - sSceneManager.sDefaultCube = getDefault(true); + if (sSceneManager.mDefaultCube == null) { + sSceneManager.mDefaultCube = getDefault(true); } - return sSceneManager.sDefaultCube; + return sSceneManager.mDefaultCube; } public static boolean isSDCardPath(String path) { @@ -154,24 +162,32 @@ public class SceneManager extends SceneGraphBase { return b; } - public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) { - Bitmap b = loadBitmap(name, res); + static Allocation createFromBitmap(Bitmap b, RenderScriptGL rs, boolean isCube) { if (b == null) { return null; } - return Allocation.createCubemapFromBitmap(rs, b, - MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, - Allocation.USAGE_GRAPHICS_TEXTURE); + MipmapControl mip = MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE; + int usage = Allocation.USAGE_GRAPHICS_TEXTURE; + if (isCube) { + return Allocation.createCubemapFromBitmap(rs, b, mip, usage); + } + return Allocation.createFromBitmap(rs, b, mip, usage); + } + + public static Allocation loadCubemap(String name, RenderScriptGL rs, Resources res) { + return createFromBitmap(loadBitmap(name, res), rs, true); + } + + public static Allocation loadCubemap(int id, RenderScriptGL rs, Resources res) { + return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, true); } public static Allocation loadTexture2D(String name, RenderScriptGL rs, Resources res) { - Bitmap b = loadBitmap(name, res); - if (b == null) { - return null; - } - return Allocation.createFromBitmap(rs, b, - Allocation.MipmapControl.MIPMAP_ON_SYNC_TO_TEXTURE, - Allocation.USAGE_GRAPHICS_TEXTURE); + return createFromBitmap(loadBitmap(name, res), rs, false); + } + + public static Allocation loadTexture2D(int id, RenderScriptGL rs, Resources res) { + return createFromBitmap(BitmapFactory.decodeResource(res, id), rs, false); } public static ProgramStore BLEND_ADD_DEPTH_NONE(RenderScript rs) { @@ -235,6 +251,10 @@ public class SceneManager extends SceneGraphBase { public void setActiveScene(Scene s) { mActiveScene = s; + if (mActiveScene == null) { + return; + } + // Do some sanity checking if (mActiveScene.getCameras().size() == 0) { Matrix4f camPos = new Matrix4f(); @@ -248,6 +268,9 @@ public class SceneManager extends SceneGraphBase { cam.setTransform(cameraTransform); mActiveScene.appendCamera(cam); } + + mActiveScene.appendShader(getDefaultVS()); + mActiveScene.appendTransform(getDefaultTransform()); } static RenderScriptGL getRS() { @@ -264,6 +287,129 @@ public class SceneManager extends SceneGraphBase { return sSceneManager.mRes; } + // Provides the folowing inputs to fragment shader + // Assigned by default if nothing is present + // vec3 varWorldPos; + // vec3 varWorldNormal; + // vec2 varTex0; + public static VertexShader getDefaultVS() { + if (sSceneManager == null) { + return null; + } + + if (sSceneManager.mDefaultVertex == null) { + RenderScriptGL rs = getRS(); + Element.Builder b = new Element.Builder(rs); + b.add(Element.MATRIX_4X4(rs), "model"); + Type.Builder objConstBuilder = new Type.Builder(rs, b.create()); + + b = new Element.Builder(rs); + b.add(Element.MATRIX_4X4(rs), "viewProj"); + Type.Builder shaderConstBuilder = new Type.Builder(rs, b.create()); + + b = new Element.Builder(rs); + b.add(Element.F32_4(rs), "position"); + b.add(Element.F32_2(rs), "texture0"); + b.add(Element.F32_3(rs), "normal"); + Element defaultIn = b.create(); + + final String code = "\n" + + "varying vec3 varWorldPos;\n" + + "varying vec3 varWorldNormal;\n" + + "varying vec2 varTex0;\n" + + "void main() {" + + " vec4 objPos = ATTRIB_position;\n" + + " vec4 worldPos = UNI_model * objPos;\n" + + " gl_Position = UNI_viewProj * worldPos;\n" + + " mat3 model3 = mat3(UNI_model[0].xyz, UNI_model[1].xyz, UNI_model[2].xyz);\n" + + " vec3 worldNorm = model3 * ATTRIB_normal;\n" + + " varWorldPos = worldPos.xyz;\n" + + " varWorldNormal = worldNorm;\n" + + " varTex0 = ATTRIB_texture0;\n" + + "}\n"; + + VertexShader.Builder sb = new VertexShader.Builder(rs); + sb.addInput(defaultIn); + sb.setObjectConst(objConstBuilder.setX(1).create()); + sb.setShaderConst(shaderConstBuilder.setX(1).create()); + sb.setShader(code); + sSceneManager.mDefaultVertex = sb.create(); + } + + return sSceneManager.mDefaultVertex; + } + + public static FragmentShader getColorFS() { + if (sSceneManager == null) { + return null; + } + if (sSceneManager.mColor == null) { + RenderScriptGL rs = getRS(); + Element.Builder b = new Element.Builder(rs); + b.add(Element.F32_4(rs), "color"); + Type.Builder objConstBuilder = new Type.Builder(rs, b.create()); + + final String code = "\n" + + "varying vec2 varTex0;\n" + + "void main() {\n" + + " lowp vec4 col = UNI_color;\n" + + " gl_FragColor = col;\n" + + "}\n"; + FragmentShader.Builder fb = new FragmentShader.Builder(rs); + fb.setShader(code); + fb.setObjectConst(objConstBuilder.create()); + sSceneManager.mColor = fb.create(); + } + + return sSceneManager.mColor; + } + + public static FragmentShader getTextureFS() { + if (sSceneManager == null) { + return null; + } + if (sSceneManager.mTexture == null) { + RenderScriptGL rs = getRS(); + + final String code = "\n" + + "varying vec2 varTex0;\n" + + "void main() {\n" + + " lowp vec4 col = texture2D(UNI_color, varTex0).rgba;\n" + + " gl_FragColor = col;\n" + + "}\n"; + + FragmentShader.Builder fb = new FragmentShader.Builder(rs); + fb.setShader(code); + fb.addTexture(Program.TextureType.TEXTURE_2D, "color"); + sSceneManager.mTexture = fb.create(); + sSceneManager.mTexture.mProgram.bindSampler(Sampler.CLAMP_LINEAR_MIP_LINEAR(rs), 0); + } + + return sSceneManager.mTexture; + } + + static RenderState getDefaultState() { + if (sSceneManager == null) { + return null; + } + if (sSceneManager.mDefaultState == null) { + sSceneManager.mDefaultState = new RenderState(getDefaultVS(), getColorFS(), null, null); + sSceneManager.mDefaultState.setName("__DefaultState"); + } + return sSceneManager.mDefaultState; + } + + static Transform getDefaultTransform() { + if (sSceneManager == null) { + return null; + } + if (sSceneManager.mDefaultTransform == null) { + sSceneManager.mDefaultTransform = new MatrixTransform(); + sSceneManager.mDefaultTransform.setName("__DefaultTransform"); + } + return sSceneManager.mDefaultTransform; + } + public static SceneManager getInstance() { if (sSceneManager == null) { sSceneManager = new SceneManager(); @@ -287,17 +433,10 @@ public class SceneManager extends SceneGraphBase { Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, 3, Mesh.TriangleMeshBuilder.TEXTURE_0); - tmb.setTexture(0.0f, 1.0f); - tmb.addVertex(-1.0f, 1.0f, 1.0f); - - tmb.setTexture(0.0f, 0.0f); - tmb.addVertex(-1.0f, -1.0f, 1.0f); - - tmb.setTexture(1.0f, 0.0f); - tmb.addVertex(1.0f, -1.0f, 1.0f); - - tmb.setTexture(1.0f, 1.0f); - tmb.addVertex(1.0f, 1.0f, 1.0f); + tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 1.0f); + tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 1.0f); + tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 1.0f); + tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 1.0f); tmb.addTriangle(0, 1, 2); tmb.addTriangle(2, 3, 0); @@ -321,6 +460,15 @@ public class SceneManager extends SceneGraphBase { mRes = res; mAllocationMap = new HashMap<String, Allocation>(); + mQuad = null; + mDefault2D = null; + mDefaultCube = null; + mDefaultVertex = null; + mColor = null; + mTexture = null; + mDefaultState = null; + mDefaultTransform = null; + mExportScript = new ScriptC_export(rs, res, R.raw.export); mTransformScript = new ScriptC_transform(rs, res, R.raw.transform); diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java index 8dea53574a45..3dd41cabe4c5 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/ShaderParam.java @@ -75,7 +75,7 @@ public abstract class ShaderParam extends SceneGraphBase { // Make one if it's not there if (matchingParam == null) { if (subElem.getDataType() == Element.DataType.FLOAT_32) { - matchingParam = new Float4Param(inputName); + matchingParam = new Float4Param(inputName, 0.5f, 0.5f, 0.5f, 0.5f); } else if (subElem.getDataType() == Element.DataType.MATRIX_4X4) { TransformParam trParam = new TransformParam(inputName); trParam.setTransform(transform); diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java index 8fae9d931700..b53ab883708e 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/Texture2D.java @@ -30,6 +30,7 @@ import android.util.Log; public class Texture2D extends TextureBase { String mFileName; String mFileDir; + int mResourceID; public Texture2D() { super(ScriptC_export.const_TextureType_TEXTURE_2D); @@ -40,6 +41,17 @@ public class Texture2D extends TextureBase { setTexture(tex); } + public Texture2D(String dir, String file) { + super(ScriptC_export.const_TextureType_TEXTURE_CUBE); + setFileDir(dir); + setFileName(file); + } + + public Texture2D(int resourceID) { + super(ScriptC_export.const_TextureType_TEXTURE_2D); + mResourceID = resourceID; + } + public void setFileDir(String dir) { mFileDir = dir; } @@ -62,8 +74,12 @@ public class Texture2D extends TextureBase { void load() { RenderScriptGL rs = SceneManager.getRS(); Resources res = SceneManager.getRes(); - String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1); - setTexture(SceneManager.loadTexture2D(mFileDir + shortName, rs, res)); + if (mFileName != null && mFileName.length() > 0) { + String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1); + setTexture(SceneManager.loadTexture2D(mFileDir + shortName, rs, res)); + } else if (mResourceID != 0) { + setTexture(SceneManager.loadTexture2D(mResourceID, rs, res)); + } } ScriptField_Texture_s getRsData(boolean loadNow) { diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java index 12c81c20dd11..1269e3caa06a 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/TextureCube.java @@ -31,6 +31,7 @@ import android.util.Log; public class TextureCube extends TextureBase { String mFileName; String mFileDir; + int mResourceID; public TextureCube() { super(ScriptC_export.const_TextureType_TEXTURE_CUBE); @@ -47,6 +48,11 @@ public class TextureCube extends TextureBase { setFileName(file); } + public TextureCube(int resourceID) { + super(ScriptC_export.const_TextureType_TEXTURE_2D); + mResourceID = resourceID; + } + public void setFileDir(String dir) { mFileDir = dir; } @@ -69,8 +75,12 @@ public class TextureCube extends TextureBase { void load() { RenderScriptGL rs = SceneManager.getRS(); Resources res = SceneManager.getRes(); - String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1); - setTexture(SceneManager.loadCubemap(mFileDir + shortName, rs, res)); + if (mFileName != null && mFileName.length() > 0) { + String shortName = mFileName.substring(mFileName.lastIndexOf('/') + 1); + setTexture(SceneManager.loadCubemap(mFileDir + shortName, rs, res)); + } else if (mResourceID != 0) { + setTexture(SceneManager.loadCubemap(mResourceID , rs, res)); + } } ScriptField_Texture_s getRsData(boolean loadNow) { diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java index f7d0e6d8a48a..4efaff7bbb12 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/VertexShader.java @@ -44,6 +44,11 @@ public class VertexShader extends Shader { return this; } + public Builder setShader(String code) { + mBuilder.setShader(code); + return this; + } + public Builder setObjectConst(Type type) { mShader.mPerObjConstants = type; return this; diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs index d8d48b3da25c..8a73dbdf12b7 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/scenegraph/render.rs @@ -142,10 +142,14 @@ static void drawAllObjects(rs_allocation allObj) { return; } - rsForEach(gVertexParamsScript, nullAlloc, gVertexShaders, - gActiveCamera, sizeof(gActiveCamera)); - rsForEach(gFragmentParamsScript, nullAlloc, gFragmentShaders, - gActiveCamera, sizeof(gActiveCamera)); + if (rsIsObject(gVertexShaders)) { + rsForEach(gVertexParamsScript, nullAlloc, gVertexShaders, + gActiveCamera, sizeof(gActiveCamera)); + } + if (rsIsObject(gFragmentShaders)) { + rsForEach(gFragmentParamsScript, nullAlloc, gFragmentShaders, + gActiveCamera, sizeof(gActiveCamera)); + } // Run the params and cull script rsForEach(gCullScript, nullAlloc, allObj, gActiveCamera, sizeof(gActiveCamera)); diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java new file mode 100644 index 000000000000..314db80f2a31 --- /dev/null +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleApp.java @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2012 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.testapp; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; + +import android.app.Activity; +import android.os.Bundle; +import android.util.Log; +import android.view.View; +import android.view.Window; +import android.view.Window; +import android.net.Uri; + +import java.lang.Runtime; + +public class SimpleApp extends Activity { + + private SimpleAppView mView; + + @Override + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + // Create our Preview view and set it as the content of our + // Activity + mView = new SimpleAppView(this); + setContentView(mView); + } +} + diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java new file mode 100644 index 000000000000..621bfa364bac --- /dev/null +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppRS.java @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2012 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.testapp; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Vector; + +import com.android.scenegraph.*; +import com.android.scenegraph.SceneManager.SceneLoadedCallback; + +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.os.AsyncTask; +import android.renderscript.*; +import android.renderscript.Program.TextureType; +import android.util.Log; + +// This is where the scenegraph and the rendered objects are initialized and used +public class SimpleAppRS { + + private static String TAG = "SimpleAppRS"; + + SceneManager mSceneManager; + + RenderScriptGL mRS; + Resources mRes; + + Scene mScene; + Mesh mSimpleMesh; + + public void init(RenderScriptGL rs, Resources res, int width, int height) { + mRS = rs; + mRes = res; + mSceneManager = SceneManager.getInstance(); + mSceneManager.initRS(mRS, mRes, width, height); + + mScene = new Scene(); + + setupGeometry(); + setupRenderables(); + setupCamera(); + setupRenderPass(); + + mSceneManager.setActiveScene(mScene); + + mScene.initRS(); + mRS.bindRootScript(mSceneManager.getRenderLoop()); + } + + private void setupGeometry() { + Mesh.TriangleMeshBuilder tmb = new Mesh.TriangleMeshBuilder(mRS, 3, + Mesh.TriangleMeshBuilder.TEXTURE_0); + + tmb.setTexture(0.0f, 1.0f).addVertex(-1.0f, 1.0f, 0.0f); + tmb.setTexture(0.0f, 0.0f).addVertex(-1.0f, -1.0f, 0.0f); + tmb.setTexture(1.0f, 0.0f).addVertex(1.0f, -1.0f, 0.0f); + tmb.setTexture(1.0f, 1.0f).addVertex(1.0f, 1.0f, 0.0f); + + tmb.addTriangle(0, 1, 2); + tmb.addTriangle(2, 3, 0); + mSimpleMesh = tmb.create(true); + } + + private void setupRenderables() { + // Built-in shader that provides position, texcoord and normal + VertexShader genericV = SceneManager.getDefaultVS(); + // Built-in shader that displays a color + FragmentShader colorF = SceneManager.getColorFS(); + // Built-in shader that displays a texture + FragmentShader textureF = SceneManager.getTextureFS(); + RenderState colorRS = new RenderState(genericV, colorF, null, null); + ProgramStore alphaBlend = ProgramStore.BLEND_ALPHA_DEPTH_TEST(mRS); + RenderState texRS = new RenderState(genericV, textureF, alphaBlend, null); + + // Draw a simple colored quad + Renderable quad = mScene.appendNewRenderable(); + quad.setMesh(mSimpleMesh); + quad.appendSourceParams(new Float4Param("color", 0.2f, 0.3f, 0.4f)); + quad.setRenderState(colorRS); + + // Draw a textured quad + quad = mScene.appendNewRenderable(); + quad.setMesh(mSimpleMesh); + // Make a transform to position the quad + CompoundTransform t = mScene.appendNewCompoundTransform(); + t.addTranslate("position", new Float3(2, 2, 0)); + quad.setTransform(t); + quad.appendSourceParams(new TextureParam("color", new Texture2D(R.drawable.icon))); + quad.setRenderState(texRS); + } + + private void setupCamera() { + Camera camera = mScene.appendNewCamera(); + camera.setFar(200); + camera.setNear(0.1f); + camera.setFOV(60); + CompoundTransform cameraTransform = mScene.appendNewCompoundTransform(); + cameraTransform.addTranslate("camera", new Float3(0, 0, 10)); + camera.setTransform(cameraTransform); + } + + private void setupRenderPass() { + RenderPass mainPass = mScene.appendNewRenderPass(); + mainPass.setClearColor(new Float4(1.0f, 1.0f, 1.0f, 1.0f)); + mainPass.setShouldClearColor(true); + mainPass.setClearDepth(1.0f); + mainPass.setShouldClearDepth(true); + mainPass.setCamera(mScene.getCameras().get(0)); + ArrayList<RenderableBase> allRender = mScene.getRenderables(); + for (RenderableBase renderable : allRender) { + mainPass.appendRenderable((Renderable)renderable); + } + } +} diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java new file mode 100644 index 000000000000..053e545fa0cf --- /dev/null +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/SimpleAppView.java @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2012 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.testapp; + +import android.renderscript.RSSurfaceView; +import android.renderscript.RenderScript; +import android.renderscript.RenderScriptGL; + +import android.content.Context; +import android.content.res.Resources; +import android.util.Log; +import android.view.Surface; +import android.view.SurfaceHolder; +import android.view.SurfaceView; + +public class SimpleAppView extends RSSurfaceView { + + public SimpleAppView(Context context) { + super(context); + } + + private RenderScriptGL mRS; + SimpleAppRS mRender; + + public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) { + super.surfaceChanged(holder, format, w, h); + if (mRS == null) { + RenderScriptGL.SurfaceConfig sc = new RenderScriptGL.SurfaceConfig(); + sc.setDepth(16, 24); + mRS = createRenderScriptGL(sc); + mRS.setSurface(holder, w, h); + mRender = new SimpleAppRS(); + mRender.init(mRS, getResources(), w, h); + } + } + + @Override + protected void onDetachedFromWindow() { + if (mRS != null) { + mRender = null; + mRS = null; + destroyRenderScriptGL(); + } + } +} + + diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java index 7bf781287f73..f159e85ca202 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TestAppRS.java @@ -149,34 +149,27 @@ public class TestAppRS { } private void initPaintShaders() { - ScriptField_ModelParams objConst = new ScriptField_ModelParams(mRS, 1); - ScriptField_ViewProjParams shaderConst = new ScriptField_ViewProjParams(mRS, 1); + mGenericV = SceneManager.getDefaultVS(); - VertexShader.Builder vb = new VertexShader.Builder(mRS); - vb.addInput(ScriptField_VertexShaderInputs.createElement(mRS)); - vb.setShader(mRes, R.raw.shader2v); - vb.setObjectConst(objConst.getAllocation().getType()); - vb.setShaderConst(shaderConst.getAllocation().getType()); - mGenericV = vb.create(); + ScriptField_CameraParams camParams = new ScriptField_CameraParams(mRS, 1); + Type camParamType = camParams.getAllocation().getType(); + ScriptField_LightParams lightParams = new ScriptField_LightParams(mRS, 1); - ScriptField_CameraParams fsConst = new ScriptField_CameraParams(mRS, 1); - ScriptField_LightParams fsConst2 = new ScriptField_LightParams(mRS, 1); - - mPaintF = createFromResource(R.raw.paintf, true, fsConst.getAllocation().getType()); + mPaintF = createFromResource(R.raw.paintf, true, camParamType); // Assign a reflection map TextureCube envCube = new TextureCube("sdcard/scenegraph/", "cube_env.png"); mPaintF.appendSourceParams(new TextureParam("reflection", envCube)); - mAluminumF = createFromResource(R.raw.metal, true, fsConst.getAllocation().getType()); + mAluminumF = createFromResource(R.raw.metal, true, camParamType); TextureCube diffCube = new TextureCube("sdcard/scenegraph/", "cube_spec.png"); mAluminumF.appendSourceParams(new TextureParam("reflection", diffCube)); - mPlasticF = createFromResource(R.raw.plastic, false, fsConst.getAllocation().getType()); - mDiffuseF = createFromResource(R.raw.diffuse, false, fsConst.getAllocation().getType()); - mTextureF = createFromResource(R.raw.texture, false, fsConst.getAllocation().getType()); + mPlasticF = createFromResource(R.raw.plastic, false, camParamType); + mDiffuseF = createFromResource(R.raw.diffuse, false, camParamType); + mTextureF = SceneManager.getTextureFS(); FragmentShader.Builder fb = new FragmentShader.Builder(mRS); - fb.setObjectConst(fsConst2.getAllocation().getType()); + fb.setObjectConst(lightParams.getAllocation().getType()); fb.setShader(mRes, R.raw.plastic_lights); mLightsF = fb.create(); @@ -214,7 +207,6 @@ public class TestAppRS { mActiveScene.appendShader(mPlasticF); mActiveScene.appendShader(mDiffuseF); mActiveScene.appendShader(mTextureF); - mActiveScene.appendShader(mGenericV); } public void prepareToRender(Scene s) { diff --git a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java index d8e48e80161a..e272cc58cd6f 100644 --- a/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java +++ b/tests/RenderScriptTests/SceneGraph/src/com/android/testapp/TouchHandler.java @@ -47,22 +47,17 @@ public class TouchHandler { mDistValue = new Float3(0, 0, 45); mPosValue = new Float3(0, 4, 0); - mRotateX = new RotateComponent("RotateX", new Float3(1, 0, 0), mRotateXValue); - mRotateY = new RotateComponent("RotateY", new Float3(0, 1, 0), mRotateYValue); - mDist = new TranslateComponent("Distance", mDistValue); - mPosition = new TranslateComponent("Distance", mPosValue); - // Make a camera transform we can manipulate - mCameraRig = new CompoundTransform(); + mCameraRig = scene.appendNewCompoundTransform(); mCameraRig.setName("CameraRig"); - mCameraRig.addComponent(mPosition); - mCameraRig.addComponent(mRotateY); - mCameraRig.addComponent(mRotateX); - mCameraRig.addComponent(mDist); - scene.appendTransform(mCameraRig); - mCamera = new Camera(); + + mPosition = mCameraRig.addTranslate("Position", mPosValue); + mRotateY = mCameraRig.addRotate("RotateY", new Float3(0, 1, 0), mRotateYValue); + mRotateX = mCameraRig.addRotate("RotateX", new Float3(1, 0, 0), mRotateXValue); + mDist = mCameraRig.addTranslate("Distance", mDistValue); + + mCamera = scene.appendNewCamera(); mCamera.setTransform(mCameraRig); - scene.appendCamera(mCamera); } public Camera getCamera() { diff --git a/tests/backup/Android.mk b/tests/backup/Android.mk index 31e2ec5263b3..88794c26520e 100644 --- a/tests/backup/Android.mk +++ b/tests/backup/Android.mk @@ -24,7 +24,7 @@ LOCAL_SRC_FILES := \ LOCAL_MODULE_TAGS := optional LOCAL_MODULE := backup_helper_test LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS) -LOCAL_SHARED_LIBRARIES := libutils +LOCAL_SHARED_LIBRARIES := libandroidfw libutils include $(BUILD_EXECUTABLE) diff --git a/tests/backup/backup_helper_test.cpp b/tests/backup/backup_helper_test.cpp index 04358ad46984..b5f6ff57e6d9 100644 --- a/tests/backup/backup_helper_test.cpp +++ b/tests/backup/backup_helper_test.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <utils/BackupHelpers.h> +#include <androidfw/BackupHelpers.h> #include <stdio.h> #include <string.h> diff --git a/tools/aapt/AaptAssets.h b/tools/aapt/AaptAssets.h index 1c653e1fb77a..59249529fbba 100644 --- a/tools/aapt/AaptAssets.h +++ b/tools/aapt/AaptAssets.h @@ -7,14 +7,14 @@ #define __AAPT_ASSETS_H #include <stdlib.h> -#include <utils/AssetManager.h> +#include <androidfw/AssetManager.h> +#include <androidfw/ResourceTypes.h> #include <utils/KeyedVector.h> -#include <utils/String8.h> -#include <utils/ResourceTypes.h> +#include <utils/RefBase.h> #include <utils/SortedVector.h> #include <utils/String8.h> +#include <utils/String8.h> #include <utils/Vector.h> -#include <utils/RefBase.h> #include "ZipFile.h" #include "Bundle.h" diff --git a/tools/aapt/Android.mk b/tools/aapt/Android.mk index cb55a9cfd4d6..d0a81dc1b1a8 100644 --- a/tools/aapt/Android.mk +++ b/tools/aapt/Android.mk @@ -38,6 +38,7 @@ LOCAL_C_INCLUDES += build/libs/host/include #LOCAL_WHOLE_STATIC_LIBRARIES := LOCAL_STATIC_LIBRARIES := \ libhost \ + libandroidfw \ libutils \ libcutils \ libexpat \ diff --git a/tools/aapt/Images.cpp b/tools/aapt/Images.cpp index ffbe875b72f7..6402e3cb38d8 100644 --- a/tools/aapt/Images.cpp +++ b/tools/aapt/Images.cpp @@ -8,7 +8,7 @@ #include "Images.h" -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> #include <png.h> diff --git a/tools/aapt/ResourceTable.cpp b/tools/aapt/ResourceTable.cpp index 7a0499c476d7..0c0b2ea0765f 100644 --- a/tools/aapt/ResourceTable.cpp +++ b/tools/aapt/ResourceTable.cpp @@ -9,8 +9,8 @@ #include "XMLNode.h" #include "ResourceFilter.h" +#include <androidfw/ResourceTypes.h> #include <utils/ByteOrder.h> -#include <utils/ResourceTypes.h> #include <stdarg.h> #define NOISY(x) //x diff --git a/tools/aapt/StringPool.h b/tools/aapt/StringPool.h index 255bdbfc0a7f..86044ed1ad2b 100644 --- a/tools/aapt/StringPool.h +++ b/tools/aapt/StringPool.h @@ -10,7 +10,7 @@ #include "Main.h" #include "AaptAssets.h" -#include <utils/ResourceTypes.h> +#include <androidfw/ResourceTypes.h> #include <utils/String16.h> #include <utils/TextOutput.h> diff --git a/tools/aapt/ZipFile.cpp b/tools/aapt/ZipFile.cpp index 0705be3380db..8057068dd685 100644 --- a/tools/aapt/ZipFile.cpp +++ b/tools/aapt/ZipFile.cpp @@ -20,7 +20,7 @@ #define LOG_TAG "zip" -#include <utils/ZipUtils.h> +#include <androidfw/ZipUtils.h> #include <utils/Log.h> #include "ZipFile.h" diff --git a/tools/aidl/Type.cpp b/tools/aidl/Type.cpp index 42e12265247d..d572af6d2aab 100755 --- a/tools/aidl/Type.cpp +++ b/tools/aidl/Type.cpp @@ -123,7 +123,7 @@ register_base_types() RPC_DATA_TYPE = new RpcDataType(); NAMES.Add(RPC_DATA_TYPE); - RPC_ERROR_TYPE = new UserDataType("com.android.athome.rpc", "RpcError", + RPC_ERROR_TYPE = new UserDataType("android.support.place.rpc", "RpcError", true, __FILE__, __LINE__); NAMES.Add(RPC_ERROR_TYPE); @@ -1234,7 +1234,7 @@ GenericListType::CreateFromRpcData(StatementBlock* addTo, Expression* k, Variabl // ================================================================ RpcDataType::RpcDataType() - :UserDataType("com.android.athome.rpc", "RpcData", true, true, true) + :UserDataType("android.support.place.rpc", "RpcData", true, true, true) { } diff --git a/tools/aidl/generate_java_rpc.cpp b/tools/aidl/generate_java_rpc.cpp index ecff3a1ad3e7..e5fa07605f2a 100644 --- a/tools/aidl/generate_java_rpc.cpp +++ b/tools/aidl/generate_java_rpc.cpp @@ -7,26 +7,26 @@ Type* SERVICE_CONTEXT_TYPE = new Type("android.content", "Context", Type::BUILT_IN, false, false, false); -Type* PRESENTER_BASE_TYPE = new Type("com.android.athome.connector", +Type* PRESENTER_BASE_TYPE = new Type("android.support.place.connector", "EventListener", Type::BUILT_IN, false, false, false); -Type* PRESENTER_LISTENER_BASE_TYPE = new Type("com.android.athome.connector", +Type* PRESENTER_LISTENER_BASE_TYPE = new Type("android.support.place.connector", "EventListener.Listener", Type::BUILT_IN, false, false, false); -Type* RPC_BROKER_TYPE = new Type("com.android.athome.connector", "Broker", +Type* RPC_BROKER_TYPE = new Type("android.support.place.connector", "Broker", Type::BUILT_IN, false, false, false); Type* RPC_CONTAINER_TYPE = new Type("com.android.athome.connector", "ConnectorContainer", Type::BUILT_IN, false, false, false); Type* PLACE_INFO_TYPE = new Type("android.support.place.connector", "PlaceInfo", Type::BUILT_IN, false, false, false); // TODO: Just use Endpoint, so this works for all endpoints. -Type* RPC_CONNECTOR_TYPE = new Type("com.android.athome.connector", "Connector", +Type* RPC_CONNECTOR_TYPE = new Type("android.support.place.connector", "Connector", Type::BUILT_IN, false, false, false); -Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("com.android.athome.rpc", +Type* RPC_ENDPOINT_INFO_TYPE = new UserDataType("android.support.place.rpc", "EndpointInfo", true, __FILE__, __LINE__); -Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("com.android.athome.rpc", "RpcResultHandler", +Type* RPC_RESULT_HANDLER_TYPE = new UserDataType("android.support.place.rpc", "RpcResultHandler", true, __FILE__, __LINE__); -Type* RPC_ERROR_LISTENER_TYPE = new Type("com.android.athome.rpc", "RpcErrorHandler", +Type* RPC_ERROR_LISTENER_TYPE = new Type("android.support.place.rpc", "RpcErrorHandler", Type::BUILT_IN, false, false, false); -Type* RPC_CONTEXT_TYPE = new UserDataType("com.android.athome.rpc", "RpcContext", true, +Type* RPC_CONTEXT_TYPE = new UserDataType("android.support.place.rpc", "RpcContext", true, __FILE__, __LINE__); static void generate_create_from_data(Type* t, StatementBlock* addTo, const string& key, diff --git a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java index 96de51c08793..97d99695386f 100644 --- a/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java +++ b/tools/layoutlib/bridge/src/android/view/AttachInfo_Accessor.java @@ -29,7 +29,7 @@ public class AttachInfo_Accessor { public static void setAttachInfo(View view) { AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(), - new Handler(), null); + new ViewRootImpl(view.getContext()), new Handler(), null); info.mHasWindowFocus = true; info.mWindowVisibility = View.VISIBLE; info.mInTouchMode = false; // this is so that we can display selections. diff --git a/tools/makekeycodes/makekeycodes.cpp b/tools/makekeycodes/makekeycodes.cpp index 16df774c643e..6ffbfb859ab8 100644 --- a/tools/makekeycodes/makekeycodes.cpp +++ b/tools/makekeycodes/makekeycodes.cpp @@ -1,5 +1,21 @@ +/* + * Copyright (C) 2012 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 <stdio.h> -#include <ui/KeycodeLabels.h> +#include <androidfw/KeycodeLabels.h> int main(int argc, char** argv) diff --git a/tools/obbtool/Android.mk b/tools/obbtool/Android.mk index 72a9858ecaf8..dd57ae662ee9 100644 --- a/tools/obbtool/Android.mk +++ b/tools/obbtool/Android.mk @@ -19,6 +19,7 @@ LOCAL_CFLAGS := -Wall -Werror LOCAL_STATIC_LIBRARIES := \ libutils \ + libandroidfw \ libcutils ifeq ($(HOST_OS),linux) diff --git a/tools/obbtool/Main.cpp b/tools/obbtool/Main.cpp index 932dbec7b2e1..b2152e8f8468 100644 --- a/tools/obbtool/Main.cpp +++ b/tools/obbtool/Main.cpp @@ -14,7 +14,7 @@ * limitations under the License. */ -#include <utils/ObbFile.h> +#include <androidfw/ObbFile.h> #include <utils/String8.h> #include <getopt.h> diff --git a/tools/validatekeymaps/Android.mk b/tools/validatekeymaps/Android.mk index 1368a0784790..fce2e931125e 100644 --- a/tools/validatekeymaps/Android.mk +++ b/tools/validatekeymaps/Android.mk @@ -18,7 +18,7 @@ LOCAL_CFLAGS := -Wall -Werror #LOCAL_C_INCLUDES += LOCAL_STATIC_LIBRARIES := \ - libui \ + libandroidfw \ libutils \ libcutils diff --git a/tools/validatekeymaps/Main.cpp b/tools/validatekeymaps/Main.cpp index 8ab9b6ae3de6..3cc2467b7166 100644 --- a/tools/validatekeymaps/Main.cpp +++ b/tools/validatekeymaps/Main.cpp @@ -14,9 +14,9 @@ * limitations under the License. */ -#include <ui/KeyCharacterMap.h> -#include <ui/KeyLayoutMap.h> -#include <ui/VirtualKeyMap.h> +#include <androidfw/KeyCharacterMap.h> +#include <androidfw/KeyLayoutMap.h> +#include <androidfw/VirtualKeyMap.h> #include <utils/PropertyMap.h> #include <utils/String8.h> diff --git a/wifi/java/android/net/wifi/WifiStateMachine.java b/wifi/java/android/net/wifi/WifiStateMachine.java index 013445664843..1b64f3e3a2f5 100644 --- a/wifi/java/android/net/wifi/WifiStateMachine.java +++ b/wifi/java/android/net/wifi/WifiStateMachine.java @@ -1754,7 +1754,9 @@ public class WifiStateMachine extends StateMachine { * If we've exceeded the maximum number of retries for DHCP * to a given network, disable the network */ - if (++mReconnectCount > getMaxDhcpRetries()) { + int maxRetries = getMaxDhcpRetries(); + // maxRetries == 0 means keep trying forever + if (maxRetries > 0 && ++mReconnectCount > maxRetries) { loge("Failed " + mReconnectCount + " times, Disabling " + mLastNetworkId); mWifiConfigStore.disableNetwork(mLastNetworkId, |